<commit_before>#![allow(unused_results)]
#![allow(clippy::print_stdout)]

use std::sync::atomic::AtomicUsize;

#[cfg(not(target_arch = "x86_64"))]
use std::time::{Duration, Instant};

#[cfg(feature = "no_metrics")]
use std::marker::PhantomData;

#[cfg(not(feature = "no_metrics"))]
use std::sync::atomic::Ordering::{Acquire, Relaxed};

use crate::Lazy;

use super::*;

/// A metric collector for all pagecache users running in this
/// process.
pub static M: Lazy<Metrics, fn() -> Metrics> = Lazy::new(Metrics::default);

#[allow(clippy::cast_precision_loss)]
pub(crate) fn clock() -> u64 {
    if cfg!(feature = "no_metrics") {
        0
    } else {
        #[cfg(target_arch = "x86_64")]
        #[allow(unsafe_code)]
        unsafe {
            let mut aux = 0;
            core::arch::x86_64::__rdtscp(&mut aux)
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            let u = uptime();
            (u.as_secs() * 1_000_000_000) + u64::from(u.subsec_nanos())
        }
    }
}

// not correct, since it starts counting at the first observance...
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn uptime() -> Duration {
    static START: Lazy<Instant, fn() -> Instant> = Lazy::new(Instant::now);

    if cfg!(feature = "no_metrics") {
        Duration::new(0, 0)
    } else {
        START.elapsed()
    }
}

/// Measure the duration of an event, and call `Histogram::measure()`.
pub struct Measure<'h> {
    _start: u64,
    #[cfg(not(feature = "no_metrics"))]
    histo: &'h Histogram,
    #[cfg(feature = "no_metrics")]
    _pd: PhantomData<&'h ()>,
}

impl<'h> Measure<'h> {
    /// The time delta from ctor to dtor is recorded in `histo`.
    #[inline]
    pub fn new(_histo: &'h Histogram) -> Measure<'h> {
        Measure {
            #[cfg(feature = "no_metrics")]
            _pd: PhantomData,
            #[cfg(not(feature = "no_metrics"))]
            histo: _histo,
            _start: clock(),
        }
    }
}

impl<'h> Drop for Measure<'h> {
    #[inline]
    fn drop(&mut self) {
        #[cfg(not(feature = "no_metrics"))]
        self.histo.measure(clock() - self._start);
    }
}

#[derive(Default, Debug)]
pub struct Metrics {
    pub accountant_bump_tip: Histogram,
    pub accountant_hold: Histogram,
    pub accountant_lock: Histogram,
    pub accountant_mark_link: Histogram,
    pub accountant_mark_replace: Histogram,
    pub accountant_next: Histogram,
    pub advance_snapshot: Histogram,
    pub assign_offset: Histogram,
    pub compress: Histogram,
    pub decompress: Histogram,
    pub deserialize: Histogram,
    pub get_page: Histogram,
    pub get_pagetable: Histogram,
    pub link_page: Histogram,
    pub log_reservation_attempts: CachePadded<AtomicUsize>,
    pub log_reservations: CachePadded<AtomicUsize>,
    pub make_stable: Histogram,
    pub page_out: Histogram,
    pub pull: Histogram,
    pub read: Histogram,
    pub read_segment_message: Histogram,
    pub replace_page: Histogram,
    pub reserve_lat: Histogram,
    pub reserve_sz: Histogram,
    pub rewrite_page: Histogram,
    pub segment_read: Histogram,
    pub segment_utilization_startup: Histogram,
    pub segment_utilization_shutdown: Histogram,
    pub serialize: Histogram,
    pub snapshot_apply: Histogram,
    pub start_pagecache: Histogram,
    pub start_segment_accountant: Histogram,
    pub tree_cas: Histogram,
    pub tree_child_split_attempt: CachePadded<AtomicUsize>,
    pub tree_child_split_success: CachePadded<AtomicUsize>,
    pub tree_del: Histogram,
    pub tree_get: Histogram,
    pub tree_loops: CachePadded<AtomicUsize>,
    pub tree_merge: Histogram,
    pub tree_parent_split_attempt: CachePadded<AtomicUsize>,
    pub tree_parent_split_success: CachePadded<AtomicUsize>,
    pub tree_reverse_scan: Histogram,
    pub tree_root_split_attempt: CachePadded<AtomicUsize>,
    pub tree_root_split_success: CachePadded<AtomicUsize>,
    pub tree_scan: Histogram,
    pub tree_set: Histogram,
    pub tree_start: Histogram,
    pub tree_traverse: Histogram,
    pub write_to_log: Histogram,
    pub written_bytes: Histogram,
    #[cfg(feature = "measure_allocs")]
    pub allocations: CachePadded<AtomicUsize>,
    #[cfg(feature = "measure_allocs")]
    pub allocated_bytes: CachePadded<AtomicUsize>,
}

#[cfg(not(feature = "no_metrics"))]
impl Metrics {
    #[inline]
    pub fn tree_looped(&self) {
        self.tree_loops.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_attempted(&self) {
        self.log_reservation_attempts.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_success(&self) {
        self.log_reservations.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_attempt(&self) {
        self.tree_child_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_success(&self) {
        self.tree_child_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_attempt(&self) {
        self.tree_parent_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_success(&self) {
        self.tree_parent_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_attempt(&self) {
        self.tree_root_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_success(&self) {
        self.tree_root_split_success.fetch_add(1, Relaxed);
    }

    pub fn print_profile(&self) {
        println!(
            "sled profile:\n\
             {0: >17} | {1: >10} | {2: >10} | {3: >10} | {4: >10} | {5: >10} | {6: >10} | {7: >10} | {8: >10} | {9: >10}",
            "op",
            "min (us)",
            "med (us)",
            "90 (us)",
            "99 (us)",
            "99.9 (us)",
            "99.99 (us)",
            "max (us)",
            "count",
            "sum (s)"
        );
        println!("{}", std::iter::repeat("-").take(134).collect::<String>());

        let p = |mut tuples: Vec<(String, _, _, _, _, _, _, _, _, _)>| {
            tuples.sort_by_key(|t| (t.9 * -1. * 1e3) as i64);
            for v in tuples {
                println!(
                    "{0: >17} | {1: >10.1} | {2: >10.1} | {3: >10.1} \
                     | {4: >10.1} | {5: >10.1} | {6: >10.1} | {7: >10.1} \
                     | {8: >10.1} | {9: >10.3}",
                    v.0, v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9,
                );
            }
        };

        let lat = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.) / 1e3,
                histo.percentile(50.) / 1e3,
                histo.percentile(90.) / 1e3,
                histo.percentile(99.) / 1e3,
                histo.percentile(99.9) / 1e3,
                histo.percentile(99.99) / 1e3,
                histo.percentile(100.) / 1e3,
                histo.count(),
                histo.sum() as f64 / 1e9,
            )
        };

        let sz = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.),
                histo.percentile(50.),
                histo.percentile(90.),
                histo.percentile(99.),
                histo.percentile(99.9),
                histo.percentile(99.99),
                histo.percentile(100.),
                histo.count(),
                histo.sum() as f64,
            )
        };

        println!("tree:");
        p(vec![
            lat("traverse", &self.tree_traverse),
            lat("get", &self.tree_get),
            lat("set", &self.tree_set),
            lat("merge", &self.tree_merge),
            lat("del", &self.tree_del),
            lat("cas", &self.tree_cas),
            lat("scan", &self.tree_scan),
            lat("rev scan", &self.tree_reverse_scan),
        ]);
        let total_loops = self.tree_loops.load(Acquire);
        let total_ops = self.tree_get.count()
            + self.tree_set.count()
            + self.tree_merge.count()
            + self.tree_del.count()
            + self.tree_cas.count()
            + self.tree_scan.count()
            + self.tree_reverse_scan.count();
        let loop_pct = total_loops * 100 / total_ops;
        println!(
            "tree contention loops: {} ({}% retry rate)",
            total_loops, loop_pct
        );
        println!(
            "tree split success rates: child({}/{}) parent({}/{}) root({}/{})",
            self.tree_child_split_success.load(Acquire),
            self.tree_child_split_attempt.load(Acquire),
            self.tree_parent_split_success.load(Acquire),
            self.tree_parent_split_attempt.load(Acquire),
            self.tree_root_split_success.load(Acquire),
            self.tree_root_split_attempt.load(Acquire),
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("pagecache:");
        p(vec![
            lat("get", &self.get_page),
            lat("get pt", &self.get_pagetable),
            lat("rewrite", &self.rewrite_page),
            lat("replace", &self.replace_page),
            lat("link", &self.link_page),
            lat("pull", &self.pull),
            lat("page_out", &self.page_out),
        ]);
        let hit_ratio = (self.get_page.count() - self.pull.count()) * 100
            / std::cmp::max(1, self.get_page.count());
        println!("hit ratio: {}%", hit_ratio);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("serialization and compression:");
        p(vec![
            lat("serialize", &self.serialize),
            lat("deserialize", &self.deserialize),
            #[cfg(feature = "compression")]
            lat("compress", &self.compress),
            #[cfg(feature = "compression")]
            lat("decompress", &self.decompress),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("log:");
        p(vec![
            lat("make_stable", &self.make_stable),
            lat("read", &self.read),
            lat("write", &self.write_to_log),
            sz("written bytes", &self.written_bytes),
            lat("assign offset", &self.assign_offset),
            lat("reserve lat", &self.reserve_lat),
            sz("reserve sz", &self.reserve_sz),
        ]);
        let log_reservations =
            std::cmp::max(1, self.log_reservations.load(Acquire));
        let log_reservation_attempts =
            self.log_reservation_attempts.load(Acquire);
        let log_reservation_retry_rate =
            (log_reservation_attempts - log_reservations) * 100
                / log_reservations;
        println!("log reservations: {}", log_reservations);
        println!(
            "log res attempts: {}, ({}% retry rate)",
            log_reservation_attempts, log_reservation_retry_rate,
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("segment accountant:");
        p(vec![
            lat("acquire", &self.accountant_lock),
            lat("hold", &self.accountant_hold),
            lat("next", &self.accountant_next),
            lat("replace", &self.accountant_mark_replace),
            lat("link", &self.accountant_mark_link),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("recovery:");
        p(vec![
            lat("start", &self.tree_start),
            lat("advance snapshot", &self.advance_snapshot),
            lat("load SA", &self.start_segment_accountant),
            lat("load PC", &self.start_pagecache),
            lat("snap apply", &self.snapshot_apply),
            lat("segment read", &self.segment_read),
            lat("log message read", &self.read_segment_message),
            sz("seg util start", &self.segment_utilization_startup),
            sz("seg util end", &self.segment_utilization_shutdown),
        ]);

        #[cfg(feature = "measure_allocs")]
        {
            println!(
                "{}",
                std::iter::repeat("-").take(134).collect::<String>()
            );
            println!("allocation statistics:");
            println!(
                "total allocations: {}",
                measure_allocs::ALLOCATIONS.load(Acquire)
            );
            println!(
                "allocated bytes: {}",
                measure_allocs::ALLOCATED_BYTES.load(Acquire)
            );
        }
    }
}

#[cfg(feature = "no_metrics")]
impl Metrics {
    pub const fn log_reservation_attempted(&self) {}

    pub const fn log_reservation_success(&self) {}

    pub const fn tree_child_split_attempt(&self) {}

    pub const fn tree_child_split_success(&self) {}

    pub const fn tree_parent_split_attempt(&self) {}

    pub const fn tree_parent_split_success(&self) {}

    pub const fn tree_root_split_attempt(&self) {}

    pub const fn tree_root_split_success(&self) {}

    pub const fn tree_looped(&self) {}

    pub const fn print_profile(&self) {}
}
<commit_msg>Avoid divide by zero in metrics code<commit_after>#![allow(unused_results)]
#![allow(clippy::print_stdout)]

use std::sync::atomic::AtomicUsize;

#[cfg(not(target_arch = "x86_64"))]
use std::time::{Duration, Instant};

#[cfg(feature = "no_metrics")]
use std::marker::PhantomData;

#[cfg(not(feature = "no_metrics"))]
use std::sync::atomic::Ordering::{Acquire, Relaxed};

use crate::Lazy;

use super::*;

/// A metric collector for all pagecache users running in this
/// process.
pub static M: Lazy<Metrics, fn() -> Metrics> = Lazy::new(Metrics::default);

#[allow(clippy::cast_precision_loss)]
pub(crate) fn clock() -> u64 {
    if cfg!(feature = "no_metrics") {
        0
    } else {
        #[cfg(target_arch = "x86_64")]
        #[allow(unsafe_code)]
        unsafe {
            let mut aux = 0;
            core::arch::x86_64::__rdtscp(&mut aux)
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            let u = uptime();
            (u.as_secs() * 1_000_000_000) + u64::from(u.subsec_nanos())
        }
    }
}

// not correct, since it starts counting at the first observance...
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn uptime() -> Duration {
    static START: Lazy<Instant, fn() -> Instant> = Lazy::new(Instant::now);

    if cfg!(feature = "no_metrics") {
        Duration::new(0, 0)
    } else {
        START.elapsed()
    }
}

/// Measure the duration of an event, and call `Histogram::measure()`.
pub struct Measure<'h> {
    _start: u64,
    #[cfg(not(feature = "no_metrics"))]
    histo: &'h Histogram,
    #[cfg(feature = "no_metrics")]
    _pd: PhantomData<&'h ()>,
}

impl<'h> Measure<'h> {
    /// The time delta from ctor to dtor is recorded in `histo`.
    #[inline]
    pub fn new(_histo: &'h Histogram) -> Measure<'h> {
        Measure {
            #[cfg(feature = "no_metrics")]
            _pd: PhantomData,
            #[cfg(not(feature = "no_metrics"))]
            histo: _histo,
            _start: clock(),
        }
    }
}

impl<'h> Drop for Measure<'h> {
    #[inline]
    fn drop(&mut self) {
        #[cfg(not(feature = "no_metrics"))]
        self.histo.measure(clock() - self._start);
    }
}

#[derive(Default, Debug)]
pub struct Metrics {
    pub accountant_bump_tip: Histogram,
    pub accountant_hold: Histogram,
    pub accountant_lock: Histogram,
    pub accountant_mark_link: Histogram,
    pub accountant_mark_replace: Histogram,
    pub accountant_next: Histogram,
    pub advance_snapshot: Histogram,
    pub assign_offset: Histogram,
    pub compress: Histogram,
    pub decompress: Histogram,
    pub deserialize: Histogram,
    pub get_page: Histogram,
    pub get_pagetable: Histogram,
    pub link_page: Histogram,
    pub log_reservation_attempts: CachePadded<AtomicUsize>,
    pub log_reservations: CachePadded<AtomicUsize>,
    pub make_stable: Histogram,
    pub page_out: Histogram,
    pub pull: Histogram,
    pub read: Histogram,
    pub read_segment_message: Histogram,
    pub replace_page: Histogram,
    pub reserve_lat: Histogram,
    pub reserve_sz: Histogram,
    pub rewrite_page: Histogram,
    pub segment_read: Histogram,
    pub segment_utilization_startup: Histogram,
    pub segment_utilization_shutdown: Histogram,
    pub serialize: Histogram,
    pub snapshot_apply: Histogram,
    pub start_pagecache: Histogram,
    pub start_segment_accountant: Histogram,
    pub tree_cas: Histogram,
    pub tree_child_split_attempt: CachePadded<AtomicUsize>,
    pub tree_child_split_success: CachePadded<AtomicUsize>,
    pub tree_del: Histogram,
    pub tree_get: Histogram,
    pub tree_loops: CachePadded<AtomicUsize>,
    pub tree_merge: Histogram,
    pub tree_parent_split_attempt: CachePadded<AtomicUsize>,
    pub tree_parent_split_success: CachePadded<AtomicUsize>,
    pub tree_reverse_scan: Histogram,
    pub tree_root_split_attempt: CachePadded<AtomicUsize>,
    pub tree_root_split_success: CachePadded<AtomicUsize>,
    pub tree_scan: Histogram,
    pub tree_set: Histogram,
    pub tree_start: Histogram,
    pub tree_traverse: Histogram,
    pub write_to_log: Histogram,
    pub written_bytes: Histogram,
    #[cfg(feature = "measure_allocs")]
    pub allocations: CachePadded<AtomicUsize>,
    #[cfg(feature = "measure_allocs")]
    pub allocated_bytes: CachePadded<AtomicUsize>,
}

#[cfg(not(feature = "no_metrics"))]
impl Metrics {
    #[inline]
    pub fn tree_looped(&self) {
        self.tree_loops.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_attempted(&self) {
        self.log_reservation_attempts.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_success(&self) {
        self.log_reservations.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_attempt(&self) {
        self.tree_child_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_success(&self) {
        self.tree_child_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_attempt(&self) {
        self.tree_parent_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_success(&self) {
        self.tree_parent_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_attempt(&self) {
        self.tree_root_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_success(&self) {
        self.tree_root_split_success.fetch_add(1, Relaxed);
    }

    pub fn print_profile(&self) {
        println!(
            "sled profile:\n\
             {0: >17} | {1: >10} | {2: >10} | {3: >10} | {4: >10} | {5: >10} | {6: >10} | {7: >10} | {8: >10} | {9: >10}",
            "op",
            "min (us)",
            "med (us)",
            "90 (us)",
            "99 (us)",
            "99.9 (us)",
            "99.99 (us)",
            "max (us)",
            "count",
            "sum (s)"
        );
        println!("{}", std::iter::repeat("-").take(134).collect::<String>());

        let p = |mut tuples: Vec<(String, _, _, _, _, _, _, _, _, _)>| {
            tuples.sort_by_key(|t| (t.9 * -1. * 1e3) as i64);
            for v in tuples {
                println!(
                    "{0: >17} | {1: >10.1} | {2: >10.1} | {3: >10.1} \
                     | {4: >10.1} | {5: >10.1} | {6: >10.1} | {7: >10.1} \
                     | {8: >10.1} | {9: >10.3}",
                    v.0, v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9,
                );
            }
        };

        let lat = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.) / 1e3,
                histo.percentile(50.) / 1e3,
                histo.percentile(90.) / 1e3,
                histo.percentile(99.) / 1e3,
                histo.percentile(99.9) / 1e3,
                histo.percentile(99.99) / 1e3,
                histo.percentile(100.) / 1e3,
                histo.count(),
                histo.sum() as f64 / 1e9,
            )
        };

        let sz = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.),
                histo.percentile(50.),
                histo.percentile(90.),
                histo.percentile(99.),
                histo.percentile(99.9),
                histo.percentile(99.99),
                histo.percentile(100.),
                histo.count(),
                histo.sum() as f64,
            )
        };

        println!("tree:");
        p(vec![
            lat("traverse", &self.tree_traverse),
            lat("get", &self.tree_get),
            lat("set", &self.tree_set),
            lat("merge", &self.tree_merge),
            lat("del", &self.tree_del),
            lat("cas", &self.tree_cas),
            lat("scan", &self.tree_scan),
            lat("rev scan", &self.tree_reverse_scan),
        ]);
        let total_loops = self.tree_loops.load(Acquire);
        let total_ops = self.tree_get.count()
            + self.tree_set.count()
            + self.tree_merge.count()
            + self.tree_del.count()
            + self.tree_cas.count()
            + self.tree_scan.count()
            + self.tree_reverse_scan.count();
        let loop_pct = total_loops * 100 / (total_ops + 1);
        println!(
            "tree contention loops: {} ({}% retry rate)",
            total_loops, loop_pct
        );
        println!(
            "tree split success rates: child({}/{}) parent({}/{}) root({}/{})",
            self.tree_child_split_success.load(Acquire),
            self.tree_child_split_attempt.load(Acquire),
            self.tree_parent_split_success.load(Acquire),
            self.tree_parent_split_attempt.load(Acquire),
            self.tree_root_split_success.load(Acquire),
            self.tree_root_split_attempt.load(Acquire),
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("pagecache:");
        p(vec![
            lat("get", &self.get_page),
            lat("get pt", &self.get_pagetable),
            lat("rewrite", &self.rewrite_page),
            lat("replace", &self.replace_page),
            lat("link", &self.link_page),
            lat("pull", &self.pull),
            lat("page_out", &self.page_out),
        ]);
        let hit_ratio = (self.get_page.count() - self.pull.count()) * 100
            / (self.get_page.count() + 1);
        println!("hit ratio: {}%", hit_ratio);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("serialization and compression:");
        p(vec![
            lat("serialize", &self.serialize),
            lat("deserialize", &self.deserialize),
            #[cfg(feature = "compression")]
            lat("compress", &self.compress),
            #[cfg(feature = "compression")]
            lat("decompress", &self.decompress),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("log:");
        p(vec![
            lat("make_stable", &self.make_stable),
            lat("read", &self.read),
            lat("write", &self.write_to_log),
            sz("written bytes", &self.written_bytes),
            lat("assign offset", &self.assign_offset),
            lat("reserve lat", &self.reserve_lat),
            sz("reserve sz", &self.reserve_sz),
        ]);
        let log_reservations =
            std::cmp::max(1, self.log_reservations.load(Acquire));
        let log_reservation_attempts =
            self.log_reservation_attempts.load(Acquire);
        let log_reservation_retry_rate =
            (log_reservation_attempts - log_reservations) * 100
                / (log_reservations + 1);
        println!("log reservations: {}", log_reservations);
        println!(
            "log res attempts: {}, ({}% retry rate)",
            log_reservation_attempts, log_reservation_retry_rate,
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("segment accountant:");
        p(vec![
            lat("acquire", &self.accountant_lock),
            lat("hold", &self.accountant_hold),
            lat("next", &self.accountant_next),
            lat("replace", &self.accountant_mark_replace),
            lat("link", &self.accountant_mark_link),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("recovery:");
        p(vec![
            lat("start", &self.tree_start),
            lat("advance snapshot", &self.advance_snapshot),
            lat("load SA", &self.start_segment_accountant),
            lat("load PC", &self.start_pagecache),
            lat("snap apply", &self.snapshot_apply),
            lat("segment read", &self.segment_read),
            lat("log message read", &self.read_segment_message),
            sz("seg util start", &self.segment_utilization_startup),
            sz("seg util end", &self.segment_utilization_shutdown),
        ]);

        #[cfg(feature = "measure_allocs")]
        {
            println!(
                "{}",
                std::iter::repeat("-").take(134).collect::<String>()
            );
            println!("allocation statistics:");
            println!(
                "total allocations: {}",
                measure_allocs::ALLOCATIONS.load(Acquire)
            );
            println!(
                "allocated bytes: {}",
                measure_allocs::ALLOCATED_BYTES.load(Acquire)
            );
        }
    }
}

#[cfg(feature = "no_metrics")]
impl Metrics {
    pub const fn log_reservation_attempted(&self) {}

    pub const fn log_reservation_success(&self) {}

    pub const fn tree_child_split_attempt(&self) {}

    pub const fn tree_child_split_success(&self) {}

    pub const fn tree_parent_split_attempt(&self) {}

    pub const fn tree_parent_split_success(&self) {}

    pub const fn tree_root_split_attempt(&self) {}

    pub const fn tree_root_split_success(&self) {}

    pub const fn tree_looped(&self) {}

    pub const fn print_profile(&self) {}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use api::{FontKey, FontRenderMode, GlyphDimensions};
use api::{NativeFontHandle, GlyphOptions};
use api::{GlyphKey};

use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
use freetype::freetype::{FT_Library, FT_Set_Char_Size};
use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error};

use std::{cmp, mem, ptr, slice};
use std::collections::HashMap;

struct Face {
    face: FT_Face,
}

pub struct FontContext {
    lib: FT_Library,
    faces: HashMap<FontKey, Face>,
}

// FreeType resources are safe to move between threads as long as they
// are not concurrently accessed. In our case, everything is hidden inside
// a given FontContext so it is safe to move the latter between threads.
unsafe impl Send for FontContext {}

pub struct RasterizedGlyph {
    pub top: f32,
    pub left: f32,
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn float_to_fixed(before: usize, f: f64) -> i32 {
    ((1i32 << before) as f64 * f) as i32
}

fn float_to_fixed_ft(f: f64) -> i32 {
    float_to_fixed(6, f)
}

const SUCCESS: FT_Error = FT_Error(0);

impl FontContext {
    pub fn new() -> FontContext {
        let mut lib: FT_Library = ptr::null_mut();
        unsafe {
            let result = FT_Init_FreeType(&mut lib);
            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);

            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
            if !result.succeeded() {
                println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
            }
        }

        FontContext {
            lib,
            faces: HashMap::new(),
        }
    }

    pub fn has_font(&self, font_key: &FontKey) -> bool {
        self.faces.contains_key(font_key)
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
        if !self.faces.contains_key(&font_key) {
            let mut face: FT_Face = ptr::null_mut();
            let result = unsafe {
                FT_New_Memory_Face(self.lib,
                                   bytes.as_ptr(),
                                   bytes.len() as FT_Long,
                                   index as FT_Long,
                                   &mut face)
            };
            if result.succeeded() && !face.is_null() {
                self.faces.insert(*font_key, Face {
                    face,
                    //_bytes: bytes
                });
            } else {
                println!("WARN: webrender failed to load font {:?}", font_key);
            }
        }
    }

    pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
        panic!("TODO: Not supported on Linux");
    }

    pub fn delete_font(&mut self, font_key: &FontKey) {
        if let Some(face) = self.faces.remove(font_key) {
            let result = unsafe {
                FT_Done_Face(face.face)
            };
            assert!(result.succeeded());
        }
    }

    fn load_glyph(&self,
                  font_key: FontKey,
                  size: Au,
                  character: u32) -> Option<FT_GlyphSlot> {

        debug_assert!(self.faces.contains_key(&font_key));
        let face = self.faces.get(&font_key).unwrap();
        let char_size = float_to_fixed_ft(size.to_f64_px());

        assert_eq!(SUCCESS, unsafe {
            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
        });

        let result = unsafe {
            FT_Load_Glyph(face.face, character as FT_UInt, 0)
        };

        if result == SUCCESS {
            let slot = unsafe { (*face.face).glyph };
            assert!(slot != ptr::null_mut());
            Some(slot)
        } else {
            error!("Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                character, size, font_key, result);
            None
        }
    }

    fn get_glyph_dimensions_impl(slot: FT_GlyphSlot) -> Option<GlyphDimensions> {
        let metrics = unsafe { &(*slot).metrics };
        if metrics.width == 0 || metrics.height == 0 {
            None
        } else {
            let left = metrics.horiBearingX >> 6;
            let top = metrics.horiBearingY >> 6;
            let right = (metrics.horiBearingX + metrics.width + 0x3f) >> 6;
            let bottom = (metrics.horiBearingY + metrics.height + 0x3f) >> 6;
            Some(GlyphDimensions {
                left: left as i32,
                top: top as i32,
                width: (right - left) as u32,
                height: (bottom - top) as u32,
            })
        }
    }

    pub fn get_glyph_dimensions(&mut self,
                                key: &GlyphKey) -> Option<GlyphDimensions> {
        self.load_glyph(key.font_key, key.size, key.index)
            .and_then(Self::get_glyph_dimensions_impl)
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           _glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {

        let slot = match self.load_glyph(key.font_key, key.size, key.index) {
            Some(slot) => slot,
            None => return None,
        };
        let render_mode = match render_mode {
            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
        };

        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
        if result != SUCCESS {
            error!("Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result);
            return None;
        }

        let dimensions = match Self::get_glyph_dimensions_impl(slot) {
            Some(val) => val,
            None => return None,
        };

        let bitmap = unsafe { &(*slot).bitmap };
        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
        info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions);
        // we may be filling only a part of the buffer, so initialize the whole thing with 0
        let mut final_buffer = Vec::with_capacity(dimensions.width as usize *
                                                  dimensions.height as usize *
                                                  4);

        let offset_x = dimensions.left - unsafe { (*slot).bitmap_left };
        let src_pixel_width = match pixel_mode {
            FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                assert!(bitmap.width % 3 == 0);
                bitmap.width / 3
            },
            _ => bitmap.width,
        };
        // determine the destination range of texels that `bitmap` provides data for
        let dst_start = cmp::max(0, -offset_x);
        let dst_end = cmp::min(dimensions.width as i32, src_pixel_width as i32 - offset_x);

        for y in 0 .. dimensions.height {
            let src_y = y as i32 - dimensions.top + unsafe { (*slot).bitmap_top };
            if src_y < 0 || src_y >= bitmap.rows as i32 {
                for _x in 0 .. dimensions.width {
                    final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                }
                continue
            }
            let base = unsafe {
                bitmap.buffer.offset((src_y * bitmap.pitch) as isize)
            };
            for _x in 0 .. dst_start {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            match pixel_mode {
                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
                    for x in dst_start .. dst_end {
                        let src_x = x + offset_x;
                        let mask = 0x80 >> (src_x & 0x7);
                        let byte = unsafe {
                            *base.offset((src_x >> 3) as isize)
                        };
                        let alpha = if byte & mask != 0 { 0xff } else { 0 };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                    for x in dst_start .. dst_end {
                        let alpha = unsafe {
                            *base.offset((x + offset_x) as isize)
                        };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                    for x in dst_start .. dst_end {
                        let src_x = ((x + offset_x) * 3) as isize;
                        assert!(src_x+2 < bitmap.pitch as isize);
                        let t = unsafe {
                            slice::from_raw_parts(base.offset(src_x), 3)
                        };
                        final_buffer.extend_from_slice(&[t[2], t[1], t[0], 0xff]);
                    }
                }
                _ => panic!("Unsupported {:?}", pixel_mode)
            }
            for _x in dst_end .. dimensions.width as i32 {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            assert_eq!(final_buffer.len(), ((y+1) * dimensions.width * 4) as usize);
        }

        Some(RasterizedGlyph {
            left: dimensions.left as f32,
            top: dimensions.top as f32,
            width: dimensions.width as u32,
            height: dimensions.height as u32,
            bytes: final_buffer,
        })
    }
}

impl Drop for FontContext {
    fn drop(&mut self) {
        unsafe {
            FT_Done_FreeType(self.lib);
        }
    }
}
<commit_msg>Use light hinting for Linux glyphs. This is a better default and matches glyphs exactly as Gecko draws them by default.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use api::{FontKey, FontRenderMode, GlyphDimensions};
use api::{NativeFontHandle, GlyphOptions};
use api::{GlyphKey};

use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
use freetype::freetype::{FT_Library, FT_Set_Char_Size};
use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error, FT_Int32};

use std::{cmp, mem, ptr, slice};
use std::collections::HashMap;

// This constant is not present in the freetype
// bindings due to bindgen not handling the way
// the macro is defined.
const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16;

// Default to slight hinting, which is what most
// Linux distros use by default, and is a better
// default than no hinting.
// TODO(gw): Make this configurable.
const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT;

struct Face {
    face: FT_Face,
}

pub struct FontContext {
    lib: FT_Library,
    faces: HashMap<FontKey, Face>,
}

// FreeType resources are safe to move between threads as long as they
// are not concurrently accessed. In our case, everything is hidden inside
// a given FontContext so it is safe to move the latter between threads.
unsafe impl Send for FontContext {}

pub struct RasterizedGlyph {
    pub top: f32,
    pub left: f32,
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn float_to_fixed(before: usize, f: f64) -> i32 {
    ((1i32 << before) as f64 * f) as i32
}

fn float_to_fixed_ft(f: f64) -> i32 {
    float_to_fixed(6, f)
}

const SUCCESS: FT_Error = FT_Error(0);

impl FontContext {
    pub fn new() -> FontContext {
        let mut lib: FT_Library = ptr::null_mut();
        unsafe {
            let result = FT_Init_FreeType(&mut lib);
            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);

            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
            if !result.succeeded() {
                println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
            }
        }

        FontContext {
            lib,
            faces: HashMap::new(),
        }
    }

    pub fn has_font(&self, font_key: &FontKey) -> bool {
        self.faces.contains_key(font_key)
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
        if !self.faces.contains_key(&font_key) {
            let mut face: FT_Face = ptr::null_mut();
            let result = unsafe {
                FT_New_Memory_Face(self.lib,
                                   bytes.as_ptr(),
                                   bytes.len() as FT_Long,
                                   index as FT_Long,
                                   &mut face)
            };
            if result.succeeded() && !face.is_null() {
                self.faces.insert(*font_key, Face {
                    face,
                    //_bytes: bytes
                });
            } else {
                println!("WARN: webrender failed to load font {:?}", font_key);
            }
        }
    }

    pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
        panic!("TODO: Not supported on Linux");
    }

    pub fn delete_font(&mut self, font_key: &FontKey) {
        if let Some(face) = self.faces.remove(font_key) {
            let result = unsafe {
                FT_Done_Face(face.face)
            };
            assert!(result.succeeded());
        }
    }

    fn load_glyph(&self,
                  font_key: FontKey,
                  size: Au,
                  character: u32) -> Option<FT_GlyphSlot> {

        debug_assert!(self.faces.contains_key(&font_key));
        let face = self.faces.get(&font_key).unwrap();
        let char_size = float_to_fixed_ft(size.to_f64_px());

        assert_eq!(SUCCESS, unsafe {
            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
        });

        let result = unsafe {
            FT_Load_Glyph(face.face,
                          character as FT_UInt,
                          GLYPH_LOAD_FLAGS)
        };

        if result == SUCCESS {
            let slot = unsafe { (*face.face).glyph };
            assert!(slot != ptr::null_mut());
            Some(slot)
        } else {
            error!("Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                character, size, font_key, result);
            None
        }
    }

    fn get_glyph_dimensions_impl(slot: FT_GlyphSlot) -> Option<GlyphDimensions> {
        let metrics = unsafe { &(*slot).metrics };
        if metrics.width == 0 || metrics.height == 0 {
            None
        } else {
            let left = metrics.horiBearingX >> 6;
            let top = metrics.horiBearingY >> 6;
            let right = (metrics.horiBearingX + metrics.width + 0x3f) >> 6;
            let bottom = (metrics.horiBearingY + metrics.height + 0x3f) >> 6;
            Some(GlyphDimensions {
                left: left as i32,
                top: top as i32,
                width: (right - left) as u32,
                height: (bottom - top) as u32,
            })
        }
    }

    pub fn get_glyph_dimensions(&mut self,
                                key: &GlyphKey) -> Option<GlyphDimensions> {
        self.load_glyph(key.font_key, key.size, key.index)
            .and_then(Self::get_glyph_dimensions_impl)
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           _glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {

        let slot = match self.load_glyph(key.font_key, key.size, key.index) {
            Some(slot) => slot,
            None => return None,
        };
        let render_mode = match render_mode {
            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
        };

        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
        if result != SUCCESS {
            error!("Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result);
            return None;
        }

        let dimensions = match Self::get_glyph_dimensions_impl(slot) {
            Some(val) => val,
            None => return None,
        };

        let bitmap = unsafe { &(*slot).bitmap };
        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
        info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions);
        // we may be filling only a part of the buffer, so initialize the whole thing with 0
        let mut final_buffer = Vec::with_capacity(dimensions.width as usize *
                                                  dimensions.height as usize *
                                                  4);

        let offset_x = dimensions.left - unsafe { (*slot).bitmap_left };
        let src_pixel_width = match pixel_mode {
            FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                assert!(bitmap.width % 3 == 0);
                bitmap.width / 3
            },
            _ => bitmap.width,
        };
        // determine the destination range of texels that `bitmap` provides data for
        let dst_start = cmp::max(0, -offset_x);
        let dst_end = cmp::min(dimensions.width as i32, src_pixel_width as i32 - offset_x);

        for y in 0 .. dimensions.height {
            let src_y = y as i32 - dimensions.top + unsafe { (*slot).bitmap_top };
            if src_y < 0 || src_y >= bitmap.rows as i32 {
                for _x in 0 .. dimensions.width {
                    final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                }
                continue
            }
            let base = unsafe {
                bitmap.buffer.offset((src_y * bitmap.pitch) as isize)
            };
            for _x in 0 .. dst_start {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            match pixel_mode {
                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
                    for x in dst_start .. dst_end {
                        let src_x = x + offset_x;
                        let mask = 0x80 >> (src_x & 0x7);
                        let byte = unsafe {
                            *base.offset((src_x >> 3) as isize)
                        };
                        let alpha = if byte & mask != 0 { 0xff } else { 0 };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                    for x in dst_start .. dst_end {
                        let alpha = unsafe {
                            *base.offset((x + offset_x) as isize)
                        };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                    for x in dst_start .. dst_end {
                        let src_x = ((x + offset_x) * 3) as isize;
                        assert!(src_x+2 < bitmap.pitch as isize);
                        let t = unsafe {
                            slice::from_raw_parts(base.offset(src_x), 3)
                        };
                        final_buffer.extend_from_slice(&[t[2], t[1], t[0], 0xff]);
                    }
                }
                _ => panic!("Unsupported {:?}", pixel_mode)
            }
            for _x in dst_end .. dimensions.width as i32 {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            assert_eq!(final_buffer.len(), ((y+1) * dimensions.width * 4) as usize);
        }

        Some(RasterizedGlyph {
            left: dimensions.left as f32,
            top: dimensions.top as f32,
            width: dimensions.width as u32,
            height: dimensions.height as u32,
            bytes: final_buffer,
        })
    }
}

impl Drop for FontContext {
    fn drop(&mut self) {
        unsafe {
            FT_Done_FreeType(self.lib);
        }
    }
}
<|endoftext|>


<commit_before>mod idt;

lazy_static! {
    static ref IDT: idt::Idt = {
        let mut idt = idt::Idt::new();

        idt.set_handler(0, divide_by_zero_handler);
        //idt.set_handler(8, double_fault_handler);
        //idt.set_handler(13, general_protection_fault_handler);
        idt.set_handler(14, page_fault_handler);

        idt
    };
}

pub fn init() {
    IDT.load();
}

use vga_buffer::print_error;

#[naked]
extern "C" fn divide_by_zero_handler() -> ! {
    unsafe {
        asm!("mov rdi, rsp; call $0" ::
             "i"(main_handler as extern "C" fn(_) -> !) : "rdi" : "intel");
        ::core::intrinsics::unreachable();
    }
}

extern "C" fn main_handler(stack_frame: *const ExceptionStackFrame) -> ! {
    unsafe {
        print_error(format_args!("EXCEPTION: DIVIDE BY ZERO\n{:#?}", *stack_frame));
    }
    loop {}
}

extern "C" fn divide_by_zero_handler_() -> ! {
    let stack_frame: *const ExceptionStackFrame;
    unsafe {
        asm!("mov $0, rsp" : "=r"(stack_frame) ::: "intel");
        print_error(format_args!("EXCEPTION: DIVIDE BY ZERO\n{:#?}", *stack_frame));
    };
    loop {}
}

extern "C" fn double_fault_handler() -> ! {
    unsafe { print_error(format_args!("EXCEPTION: DOUBLE FAULT")) };
    loop {}
}

extern "C" fn general_protection_fault_handler() -> ! {
    loop {}
    unsafe { print_error(format_args!("EXCEPTION: GENERAL PROTECTION FAULT")) };
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrameErrorCode {
    error_code: u64,
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

#[naked]
extern "C" fn page_fault_handler() -> ! {
    unsafe {
        asm!("
            mov rdi, rsp
            call $0
        " :: "i"(handler as extern "C" fn(*const ExceptionStackFrameErrorCode) -> !) :: "intel");
    }
    loop{}

    extern "C" fn handler(stack_frame: *const ExceptionStackFrameErrorCode) -> ! {
        unsafe {
            print_error(format_args!("EXCEPTION: PAGE FAULT\n  stack frame: {:#?}", *stack_frame));
        }
        loop {}
    }
}
<commit_msg>Add handler! macros<commit_after>mod idt;

macro_rules! handler {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                asm!("mov rdi, rsp
                      sub rsp, 8 // align the stack pointer
                      call $0"
                      :: "i"($name as extern "C" fn(*const ExceptionStackFrame) -> !)
                      : "rdi" : "intel");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

macro_rules! handler_with_error_code {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                asm!("pop rsi // pop error code into rsi
                      mov rdi, rsp
                      sub rsp, 8 // align the stack pointer
                      call $0"
                      :: "i"($name as extern "C" fn(*const ExceptionStackFrame, u64) -> !)
                      : "rdi" : "intel");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

lazy_static! {
    static ref IDT: idt::Idt = {
        let mut idt = idt::Idt::new();

        idt.set_handler(0, handler!(divide_by_zero_handler));
        idt.set_handler(6, handler!(invalid_opcode_handler));
        idt.set_handler(8, double_fault_handler);
        idt.set_handler(14, handler_with_error_code!(page_fault_handler));

        idt
    };
}

pub fn init() {
    IDT.load();
}

use vga_buffer::print_error;

extern "C" fn divide_by_zero_handler(stack_frame: *const ExceptionStackFrame) -> ! {
    unsafe {
        print_error(format_args!("EXCEPTION: DIVIDE BY ZERO\n{:#?}", *stack_frame));
    }
    loop {}
}

extern "C" fn invalid_opcode_handler(stack_frame: *const ExceptionStackFrame)
    -> !
{
    unsafe {
        print_error(format_args!("EXCEPTION: INVALID OPCODE at {:#x}\n{:#?}",
            (*stack_frame).instruction_pointer, *stack_frame));
    }
    loop {}
}

extern "C" fn page_fault_handler(stack_frame: *const ExceptionStackFrame,
                                 error_code: u64) -> !
{
    unsafe {
        print_error(format_args!(
            "EXCEPTION: PAGE FAULT with error code {:?}\n{:#?}",
            PageFaultErrorCode::from_bits(error_code), *stack_frame));
    }
    loop {}
}

extern "C" fn double_fault_handler() -> ! {
    unsafe { print_error(format_args!("EXCEPTION: DOUBLE FAULT")) };
    loop {}
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

bitflags! {
    flags PageFaultErrorCode: u64 {
        const PROTECTION_VIOLATION = 1 << 0,
        const CAUSED_BY_WRITE = 1 << 1,
        const USER_MODE = 1 << 2,
        const MALFORMED_TABLE = 1 << 3,
        const INSTRUCTION_FETCH = 1 << 4,
    }
}
<|endoftext|>


<commit_before>//! An alias of the rust-http Request struct.

use http::server::request::{AbsolutePath};
pub use Request = http::server::request::Request;

/// Adds a url getter method.
pub trait GetUrl {
    /// A url getter method for requests or responses.
    fn url<'a>(&'a self) -> Option<&'a String>;
}

impl GetUrl for Request {
    /// Get the url from a Request
    ///
    /// Returns Some(&url) if this is an AbsolutePath
    /// request, otherwise it returns None.
    fn url<'a>(&'a self) -> Option<&'a String> {
        match self.request_uri {
            AbsolutePath(ref path) => Some(path),
            _ => None
        }
    }
}

<commit_msg>(feat) Added mutable getter for the url of Request objects.<commit_after>//! An alias of the rust-http Request struct.

use http::server::request::{AbsolutePath};
pub use Request = http::server::request::Request;

/// Adds a url getter method.
pub trait GetUrl {
    /// A url getter method for requests or responses.
    fn url<'a>(&'a self) -> Option<&'a String>;

    /// A mutable url getter method for requests or responses.
    fn url_mut<'a>(&'a mut self) -> Option<&'a mut String>;
}

impl GetUrl for Request {
    /// Get the url from a Request
    ///
    /// Returns Some(&url) if this is an AbsolutePath
    /// request, otherwise it returns None.
    fn url<'a>(&'a self) -> Option<&'a String> {
        match self.request_uri {
            AbsolutePath(ref path) => Some(path),
            _ => None
        }
    }

    /// Get a mutable url from a Request
    ///
    /// Returns Some(&mut url) if this is an AbsolutePath
    /// request, otherwise it returns None.
    fn url_mut<'a>(&'a mut self) -> Option<&'a mut String> {
        match self.request_uri {
            AbsolutePath(ref mut path) => Some(path),
            _ => None
        }
    }
}

<|endoftext|>


<commit_before>use std::io::{BufReader, BufWriter};

static K1: u32 = 0x5A827999u32;
static K2: u32 = 0x6ED9EBA1u32;
static K3: u32 = 0x8F1BBCDCu32;
static K4: u32 = 0xCA62C1D6u32;

fn circular_shift(bits: u32, word: u32) -> u32 {
    return word << (bits as uint) | word >> ((32u32 - bits) as uint);
}

#[allow(unused_must_use)]
pub fn sha1(message: &[u8]) -> Vec<u8> {
    let mut hash: [u32, ..5] = [0x67452301,
                                0xEFCDAB89,
                                0x98BADCFE,
                                0x10325476,
                                0xC3D2E1F0];
    let mut msg = message.to_vec();
    let msg_bit_len = msg.len() * 8;
    let offset = (msg.len() * 8) % 512;
    if offset < 448 {
        msg.push(128u8);
        for _ in range(0, (448 - (offset + 8)) / 8) {
            msg.push(0u8);
        }
    } else if offset >= 448 {
        msg.push(128u8);
        for _ in range(0, (512 - (offset + 8)) / 8 + 56) {
            msg.push(0u8);
        }
    }
    {
        msg.extend([0u8, ..8].to_vec().into_iter());
        let len = msg.len();
        let mut writer = BufWriter::new(msg.slice_from_mut(len - 8));
        writer.write_be_u64(msg_bit_len as u64);
    }

    for i in range(0, msg.len() * 8 / 512) {
        let mut w = [0u32, ..80];
        let part = msg.slice(i * 64, (i+1) * 64);
        {
            let mut reader = BufReader::new(part);
            for j in range(0u, 16u) {
                w[j] = reader.read_be_u32().unwrap();
            }
        }
        for j in range(16u, 80u) {
            let val = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16];
            w[j] = circular_shift(1, val);
        }
        let mut a = hash[0];
        let mut b = hash[1];
        let mut c = hash[2];
        let mut d = hash[3];
        let mut e = hash[4];
        let mut temp: u32;
        for t in range(0u, 20u) {
            temp = circular_shift(5, a) + (b & c | !b & d) + e + w[t] + K1;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        for t in range(20u, 40u) {
            temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K2;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        for t in range(40u, 60u) {
            temp = circular_shift(5, a) + (b & c | b & d | c & d) + e + w[t] + K3;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        for t in range(60u, 80u) {
            temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K4;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        hash[0] += a;
        hash[1] += b;
        hash[2] += c;
        hash[3] += d;
        hash[4] += e;
    }

    let mut output = [0u8, ..20];
    {
        let mut writer = BufWriter::new(&mut output);
        writer.write_be_u32(hash[0]);
        writer.write_be_u32(hash[1]);
        writer.write_be_u32(hash[2]);
        writer.write_be_u32(hash[3]);
        writer.write_be_u32(hash[4]);
    }
    output.to_vec()
}

#[cfg(test)]
mod test {
    use super::sha1;
    #[test]
    fn test_sha1() {
        assert_eq!(sha1(&[115u8, 104u8, 97u8]), vec![0xd8u8, 0xf4u8, 0x59u8, 0x03u8, 0x20u8, 0xe1u8,
                                                    0x34u8, 0x3au8, 0x91u8, 0x5bu8, 0x63u8, 0x94u8,
                                                    0x17u8, 0x06u8, 0x50u8, 0xa8u8,  0xf3u8, 0x5du8,
                                                    0x69u8, 0x26u8]);
        assert_eq!(sha1(&[65u8, ..57]), vec![0xe8u8, 0xd6u8, 0xeau8, 0x5cu8, 0x62u8, 0x7fu8, 0xc8u8,
                                            0x67u8, 0x6fu8, 0xa6u8, 0x62u8, 0x67u8, 0x7bu8, 0x02u8,
                                            0x86u8, 0x40u8, 0x84u8, 0x4du8, 0xc3u8, 0x5cu8]);
        assert_eq!(sha1(&[65u8, ..56]), vec![0x6bu8, 0x45u8, 0xe3u8, 0xcfu8, 0x1eu8, 0xb3u8, 0x32u8,
                                            0x4bu8, 0x9fu8, 0xd4u8, 0xdfu8, 0x3bu8, 0x83u8, 0xd8u8,
                                            0x9cu8, 0x4cu8, 0x2cu8, 0x4cu8, 0xa8u8, 0x96u8]);
        assert_eq!(sha1(&[65u8, ..64]), vec![0x30u8, 0xb8u8, 0x6eu8, 0x44u8, 0xe6u8, 0x00u8, 0x14u8,
                                            0x03u8, 0x82u8, 0x7au8, 0x62u8, 0xc5u8, 0x8bu8, 0x08u8,
                                            0x89u8, 0x3eu8, 0x77u8, 0xcfu8, 0x12u8, 0x1fu8]);
        assert_eq!(sha1(&[65u8, ..65]), vec![0x82u8, 0x6bu8, 0x7eu8, 0x7au8, 0x7au8, 0xf8u8, 0xa5u8,
                                            0x29u8, 0xaeu8, 0x1cu8, 0x74u8, 0x43u8, 0xc2u8, 0x3bu8,
                                            0xf1u8, 0x85u8, 0xc0u8, 0xadu8, 0x44u8, 0x0cu8]);
    }
}

<commit_msg>Minor sha1 cleanup<commit_after>use std::io::{BufReader, BufWriter};

static K1: u32 = 0x5A827999u32;
static K2: u32 = 0x6ED9EBA1u32;
static K3: u32 = 0x8F1BBCDCu32;
static K4: u32 = 0xCA62C1D6u32;

#[inline]
fn circular_shift(bits: u32, word: u32) -> u32 {
    return word << (bits as uint) | word >> ((32u32 - bits) as uint);
}

#[allow(unused_must_use)]
pub fn sha1(message: &[u8]) -> Vec<u8> {
    let mut hash: [u32, ..5] = [0x67452301,
                                0xEFCDAB89,
                                0x98BADCFE,
                                0x10325476,
                                0xC3D2E1F0];
    let mut msg = message.to_vec();
    let msg_bit_len = msg.len() * 8;
    let offset = (msg.len() * 8) % 512;
    if offset < 448 {
        msg.push(128u8);
        for _ in range(0, (448 - (offset + 8)) / 8) {
            msg.push(0u8);
        }
    } else if offset >= 448 {
        msg.push(128u8);
        for _ in range(0, (512 - (offset + 8)) / 8 + 56) {
            msg.push(0u8);
        }
    }
    msg.write_be_u64(msg_bit_len as u64);

    for i in range(0, msg.len() * 8 / 512) {
        let mut w = [0u32, ..80];
        let part = msg.slice(i * 64, (i+1) * 64);
        {
            let mut reader = BufReader::new(part);
            for j in range(0u, 16u) {
                w[j] = reader.read_be_u32().unwrap();
            }
        }
        for j in range(16u, 80u) {
            let val = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16];
            w[j] = circular_shift(1, val);
        }
        let mut a = hash[0];
        let mut b = hash[1];
        let mut c = hash[2];
        let mut d = hash[3];
        let mut e = hash[4];
        let mut temp: u32;
        for t in range(0u, 20u) {
            temp = circular_shift(5, a) + (b & c | !b & d) + e + w[t] + K1;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        for t in range(20u, 40u) {
            temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K2;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        for t in range(40u, 60u) {
            temp = circular_shift(5, a) + (b & c | b & d | c & d) + e + w[t] + K3;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        for t in range(60u, 80u) {
            temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K4;
            e = d;
            d = c;
            c = circular_shift(30, b);
            b = a;
            a = temp;
        }
        hash[0] += a;
        hash[1] += b;
        hash[2] += c;
        hash[3] += d;
        hash[4] += e;
    }

    let mut output = [0u8, ..20];
    {
        let mut writer = BufWriter::new(&mut output);
        writer.write_be_u32(hash[0]);
        writer.write_be_u32(hash[1]);
        writer.write_be_u32(hash[2]);
        writer.write_be_u32(hash[3]);
        writer.write_be_u32(hash[4]);
    }
    output.to_vec()
}

#[cfg(test)]
mod test {
    use super::sha1;
    #[test]
    fn test_sha1() {
        assert_eq!(sha1(&[115u8, 104u8, 97u8]), vec![0xd8u8, 0xf4u8, 0x59u8, 0x03u8, 0x20u8, 0xe1u8,
                                                    0x34u8, 0x3au8, 0x91u8, 0x5bu8, 0x63u8, 0x94u8,
                                                    0x17u8, 0x06u8, 0x50u8, 0xa8u8,  0xf3u8, 0x5du8,
                                                    0x69u8, 0x26u8]);
        assert_eq!(sha1(&[65u8, ..57]), vec![0xe8u8, 0xd6u8, 0xeau8, 0x5cu8, 0x62u8, 0x7fu8, 0xc8u8,
                                            0x67u8, 0x6fu8, 0xa6u8, 0x62u8, 0x67u8, 0x7bu8, 0x02u8,
                                            0x86u8, 0x40u8, 0x84u8, 0x4du8, 0xc3u8, 0x5cu8]);
        assert_eq!(sha1(&[65u8, ..56]), vec![0x6bu8, 0x45u8, 0xe3u8, 0xcfu8, 0x1eu8, 0xb3u8, 0x32u8,
                                            0x4bu8, 0x9fu8, 0xd4u8, 0xdfu8, 0x3bu8, 0x83u8, 0xd8u8,
                                            0x9cu8, 0x4cu8, 0x2cu8, 0x4cu8, 0xa8u8, 0x96u8]);
        assert_eq!(sha1(&[65u8, ..64]), vec![0x30u8, 0xb8u8, 0x6eu8, 0x44u8, 0xe6u8, 0x00u8, 0x14u8,
                                            0x03u8, 0x82u8, 0x7au8, 0x62u8, 0xc5u8, 0x8bu8, 0x08u8,
                                            0x89u8, 0x3eu8, 0x77u8, 0xcfu8, 0x12u8, 0x1fu8]);
        assert_eq!(sha1(&[65u8, ..65]), vec![0x82u8, 0x6bu8, 0x7eu8, 0x7au8, 0x7au8, 0xf8u8, 0xa5u8,
                                            0x29u8, 0xaeu8, 0x1cu8, 0x74u8, 0x43u8, 0xc2u8, 0x3bu8,
                                            0xf1u8, 0x85u8, 0xc0u8, 0xadu8, 0x44u8, 0x0cu8]);
    }
}

<|endoftext|>


<commit_before>use std::sync::Arc;
use std::collections::HashMap;
use std::collections::hash_map;

use service::Service;
use solicit::header::Headers;
use stream_part::HttpPartStream;
use resp::Response;

#[derive(Default)]
struct Node {
    service: Option<Arc<Service>>,
    children: HashMap<String, Node>,
}

impl Node {
    fn add_service(&mut self, path: &str, service: Arc<Service>) {
        match split_path(path) {
            None => {
                self.service = Some(service);
            }
            Some((first, rem)) => {
                let node = match self.children.entry(first.to_owned()) {
                    hash_map::Entry::Occupied(e) => e.into_mut(),
                    hash_map::Entry::Vacant(e) => e.insert(Node {
                        service: None,
                        children: HashMap::new(),
                    }),
                };
                node.add_service(rem, service);
            }
        }
    }

    fn remove_service(&mut self, path: &str) -> Option<Arc<Service>> {
        match split_path(path) {
            None => {
                self.service.take()
            }
            Some((first, rem)) => {
                match self.children.get_mut(first) {
                    Some(child) => {
                        child.remove_service(rem)
                    }
                    None => None,
                }
            }
        }
    }

    fn find_service(&self, path: &str) -> Option<&Service> {
        if let Some((first, rem)) = split_path(path) {
            if let Some(node) = self.children.get(first) {
                if let Some(service) = node.find_service(rem) {
                    return Some(service);
                }
            }
        }

        self.service.as_ref().map(|a| a.as_ref())
    }
}

fn split_path<'a>(mut path: &'a str) -> Option<(&'a str, &'a str)> {
    path = path.trim_left_matches('/');

    if path.is_empty() {
        None
    } else {
        let slash = path.find('/');
        match slash {
            Some(slash) => {
                Some((&path[..slash], &path[slash + 1..]))
            }
            None => {
                Some((path, ""))
            }
        }
    }
}

#[test]
fn test_split_path() {
    assert_eq!(None, split_path(""));
    assert_eq!(None, split_path("/"));
    assert_eq!(Some(("first", "")), split_path("first"));
    assert_eq!(Some(("first", "")), split_path("first/"));
    assert_eq!(Some(("first", "")), split_path("/first"));
    assert_eq!(Some(("first", "")), split_path("/first/"));
    assert_eq!(Some(("first", "second")), split_path("/first/second"));
    assert_eq!(Some(("first", "second/")), split_path("/first/second/"));
    assert_eq!(Some(("first", "second/third")), split_path("/first/second/third"));
    assert_eq!(Some(("first", "second/third/")), split_path("/first/second/third/"));
}

#[derive(Default)]
pub struct ServicePaths {
    root: Node,
}

impl ServicePaths {
    pub fn new() -> ServicePaths {
        Default::default()
    }

    pub fn set_service(&mut self, path: &str, service: Arc<Service>) {
        assert!(path.starts_with("/"));
        self.root.add_service(path, service);
    }

    pub fn remove_service(&mut self, path: &str) ->Option<Arc<Service>> {
        assert!(path.starts_with("/"));
        self.root.remove_service(path)
    }

    fn find_service(&self, path: &str) -> Option<&Service> {
        self.root.find_service(path)
    }
}

impl Service for ServicePaths {
    fn start_request(&self, headers: Headers, req: HttpPartStream) -> Response {
        if let Some(service) = self.find_service(headers.path()) {
            service.start_request(headers, req)
        } else {
            Response::not_found_404()
        }
    }
}
<commit_msg>Convenient shortcut<commit_after>use std::sync::Arc;
use std::collections::HashMap;
use std::collections::hash_map;

use service::Service;
use solicit::header::Headers;
use stream_part::HttpPartStream;
use resp::Response;

#[derive(Default)]
struct Node {
    service: Option<Arc<Service>>,
    children: HashMap<String, Node>,
}

impl Node {
    fn add_service(&mut self, path: &str, service: Arc<Service>) {
        match split_path(path) {
            None => {
                self.service = Some(service);
            }
            Some((first, rem)) => {
                let node = match self.children.entry(first.to_owned()) {
                    hash_map::Entry::Occupied(e) => e.into_mut(),
                    hash_map::Entry::Vacant(e) => e.insert(Node {
                        service: None,
                        children: HashMap::new(),
                    }),
                };
                node.add_service(rem, service);
            }
        }
    }

    fn remove_service(&mut self, path: &str) -> Option<Arc<Service>> {
        match split_path(path) {
            None => {
                self.service.take()
            }
            Some((first, rem)) => {
                match self.children.get_mut(first) {
                    Some(child) => {
                        child.remove_service(rem)
                    }
                    None => None,
                }
            }
        }
    }

    fn find_service(&self, path: &str) -> Option<&Service> {
        if let Some((first, rem)) = split_path(path) {
            if let Some(node) = self.children.get(first) {
                if let Some(service) = node.find_service(rem) {
                    return Some(service);
                }
            }
        }

        self.service.as_ref().map(|a| a.as_ref())
    }
}

fn split_path<'a>(mut path: &'a str) -> Option<(&'a str, &'a str)> {
    path = path.trim_left_matches('/');

    if path.is_empty() {
        None
    } else {
        let slash = path.find('/');
        match slash {
            Some(slash) => {
                Some((&path[..slash], &path[slash + 1..]))
            }
            None => {
                Some((path, ""))
            }
        }
    }
}

#[test]
fn test_split_path() {
    assert_eq!(None, split_path(""));
    assert_eq!(None, split_path("/"));
    assert_eq!(Some(("first", "")), split_path("first"));
    assert_eq!(Some(("first", "")), split_path("first/"));
    assert_eq!(Some(("first", "")), split_path("/first"));
    assert_eq!(Some(("first", "")), split_path("/first/"));
    assert_eq!(Some(("first", "second")), split_path("/first/second"));
    assert_eq!(Some(("first", "second/")), split_path("/first/second/"));
    assert_eq!(Some(("first", "second/third")), split_path("/first/second/third"));
    assert_eq!(Some(("first", "second/third/")), split_path("/first/second/third/"));
}

#[derive(Default)]
pub struct ServicePaths {
    root: Node,
}

impl ServicePaths {
    pub fn new() -> ServicePaths {
        Default::default()
    }

    pub fn set_service(&mut self, path: &str, service: Arc<Service>) {
        assert!(path.starts_with("/"));
        self.root.add_service(path, service);
    }

    pub fn set_service_fn<F>(&mut self, path: &str, service: F)
        where F : Fn(Headers, HttpPartStream) -> Response + Send + Sync + 'static
    {
        impl<F : Fn(Headers, HttpPartStream) -> Response + Send + Sync + 'static> Service for F {
            fn start_request(&self, headers: Headers, req: HttpPartStream) -> Response {
                self(headers, req)
            }
        }

        self.set_service(path, Arc::new(service))
    }

    pub fn remove_service(&mut self, path: &str) ->Option<Arc<Service>> {
        assert!(path.starts_with("/"));
        self.root.remove_service(path)
    }

    fn find_service(&self, path: &str) -> Option<&Service> {
        self.root.find_service(path)
    }
}

impl Service for ServicePaths {
    fn start_request(&self, headers: Headers, req: HttpPartStream) -> Response {
        if let Some(service) = self.find_service(headers.path()) {
            service.start_request(headers, req)
        } else {
            Response::not_found_404()
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::{RingBuf, HashSet, VecMap};
use std::mem;
use std::ptr;

use block_allocator::BlockAllocator;
use block_info::BlockInfo;
use constants::{BLOCK_SIZE, LINE_SIZE, NUM_LINES_PER_BLOCK};
use gc_object::{GCRTTI, GCObject, GCObjectRef};

type BlockTuple = (*mut BlockInfo, u16, u16);

pub struct LineAllocator {
    block_allocator: BlockAllocator,
    object_map: HashSet<GCObjectRef>,
    object_map_backup: HashSet<GCObjectRef>,
    mark_histogram: VecMap<u8>,
    unavailable_blocks: RingBuf<*mut BlockInfo>,
    recyclable_blocks: RingBuf<*mut BlockInfo>,
    current_block: Option<BlockTuple>,
    overflow_block: Option<BlockTuple>,
    current_live_mark: bool,
}

impl LineAllocator {
    pub fn new(block_allocator: BlockAllocator) -> LineAllocator {
        return LineAllocator {
            block_allocator: block_allocator,
            object_map: HashSet::new(),
            object_map_backup: HashSet::new(),
            mark_histogram: VecMap::with_capacity(NUM_LINES_PER_BLOCK),
            unavailable_blocks: RingBuf::new(),
            recyclable_blocks: RingBuf::new(),
            current_block: None,
            overflow_block: None,
            current_live_mark: false,
        };
    }

    pub fn set_gc_object(&mut self, object: GCObjectRef) {
        self.object_map.insert(object);
    }

    pub fn unset_gc_object(&mut self, object: GCObjectRef) {
        self.object_map.remove(&object);
    }

    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        return self.object_map.contains(&object);
    }

    pub fn current_live_mark(&self) -> bool {
        return self.current_live_mark;
    }

    pub fn decrement_lines(&mut self, object: GCObjectRef) {
        unsafe{ (*self.get_block_ptr(object)).decrement_lines(object); }
    }

    pub fn increment_lines(&mut self, object: GCObjectRef) {
        unsafe{ (*self.get_block_ptr(object)).increment_lines(object); }
    }

    pub fn allocate(&mut self, rtti: *const GCRTTI) -> Option<GCObjectRef> {
        let size = unsafe{ (*rtti).object_size() };
        debug!("Request to allocate an object of size {}", size);
        if let Some(object) = self.raw_allocate(size) {
            unsafe { ptr::write(object, GCObject::new(rtti, self.current_live_mark)); }
            valgrind_malloclike!(object, size);
            return Some(object);
        }
        return None;
    }

    pub fn prepare_collection(&mut self) -> bool {
        self.unavailable_blocks.extend(self.recyclable_blocks.drain());
        self.unavailable_blocks.extend(self.current_block.take()
                                           .map(|b| b.0).into_iter());

        let perform_cycle_collection = true;
        return perform_cycle_collection;
    }

    pub fn complete_collection(&mut self) {
        self.mark_histogram.clear();
        let mut unavailable_blocks = RingBuf::new();
        for block in self.unavailable_blocks.drain() {
            if unsafe{ (*block).is_empty() } {
                debug!("Return block {:p} to global block allocator", block);
                self.block_allocator.return_block(block);
            } else {
                let (holes, marked_lines) = unsafe{ (*block).count_holes_and_marked_lines() };
                if self.mark_histogram.contains_key(&(holes as uint)) {
                    if let Some(val) = self.mark_histogram.get_mut(&(holes as uint)) {
                        *val += marked_lines;
                    }
                } else { self.mark_histogram.insert(holes as uint, marked_lines); }
                debug!("Found {} holes and {} marked lines in block {}",
                       holes, marked_lines, block);
                match holes {
                    0 => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        unavailable_blocks.push_back(block);
                    },
                    _ => {
                        debug!("Push block {:p} into recyclable_blocks", block);
                        self.recyclable_blocks.push_back(block);
                    }
                }
            }
        }
        self.unavailable_blocks.extend(unavailable_blocks.into_iter());
    }

    pub fn prepare_immix_collection(&mut self) {
        for block in self.unavailable_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
        }

        if cfg!(feature = "valgrind") {
            self.object_map_backup = self.object_map.clone();
        }
        self.object_map.clear();
    }

    pub fn complete_immix_collection(&mut self) {
        self.current_live_mark = !self.current_live_mark;
        if cfg!(feature = "valgrind") {
            for &object in self.object_map_backup.difference(&self.object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }
}

impl LineAllocator {
    unsafe fn get_block_ptr(&mut self, object: GCObjectRef) -> *mut BlockInfo {
        let block_offset = object as uint % BLOCK_SIZE;
        return mem::transmute((object as *mut u8).offset(-(block_offset as int)));
    }

    fn raw_allocate(&mut self, size: uint) -> Option<GCObjectRef> {
        return if size < LINE_SIZE {
            self.current_block.take()
                              .and_then(|tp| self.scan_for_hole(size, tp))
        } else {
            self.overflow_block.take()
                               .and_then(|tp| self.scan_for_hole(size, tp))
                               .or_else(|| self.get_new_block())
        }.or_else(|| self.scan_recyclables(size))
         .or_else(|| self.get_new_block())
         .map(|tp| self.allocate_from_block(size, tp))
         .map(|(tp, object)| {
             if size < LINE_SIZE { self.current_block = Some(tp);
             } else { self.overflow_block = Some(tp); }
             object
         });
    }

    fn scan_for_hole(&mut self, size: uint, block_tuple: BlockTuple)
        -> Option<BlockTuple> {
            let (block, low, high) = block_tuple;
            return match (high - low) as uint >= size {
                true => {
                    debug!("Found hole in block {:p}", block);
                    Some(block_tuple)
                },
                false => match unsafe{ (*block).scan_block(high) } {
                    None => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        self.unavailable_blocks.push_back(block);
                        None
                    },
                    Some((low, high)) =>
                        self.scan_for_hole(size, (block, low, high)),
                }
            };
        }

    fn scan_recyclables(&mut self, size: uint) -> Option<BlockTuple> {
        return match self.recyclable_blocks.pop_front() {
            None => None,
            Some(block) => match unsafe{ (*block).scan_block((LINE_SIZE - 1) as u16) } {
                None => {
                    debug!("Push block {:p} into unavailable_blocks", block);
                    self.unavailable_blocks.push_back(block);
                    self.scan_recyclables(size)
                },
                Some((low, high)) => self.scan_for_hole(size, (block, low, high))
                                         .or_else(|| self.scan_recyclables(size)),
            }
        };
    }

    fn get_new_block(&mut self) -> Option<BlockTuple> {
        debug!("Request new block from block_allocator");
        return self.block_allocator.get_block()
                   .map(|block| (block, LINE_SIZE as u16, (BLOCK_SIZE - 1) as u16));
    }

    fn allocate_from_block(&mut self, size: uint, block_tuple: BlockTuple)
        -> (BlockTuple, GCObjectRef) {
            let (block, low, high) = block_tuple;
            let object = unsafe { (*block).offset(low as uint) };
            self.set_gc_object(object);
            debug!("Allocated object {} of size {} in {} (object={})",
            object, size, block, size >= LINE_SIZE);
            return ((block, low + size as u16, high), object);
        }
}
<commit_msg>LineAllocator: move sweeping of unavailable blocks into own method<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::{RingBuf, HashSet, VecMap};
use std::mem;
use std::ptr;

use block_allocator::BlockAllocator;
use block_info::BlockInfo;
use constants::{BLOCK_SIZE, LINE_SIZE, NUM_LINES_PER_BLOCK};
use gc_object::{GCRTTI, GCObject, GCObjectRef};

type BlockTuple = (*mut BlockInfo, u16, u16);

pub struct LineAllocator {
    block_allocator: BlockAllocator,
    object_map: HashSet<GCObjectRef>,
    object_map_backup: HashSet<GCObjectRef>,
    mark_histogram: VecMap<u8>,
    unavailable_blocks: RingBuf<*mut BlockInfo>,
    recyclable_blocks: RingBuf<*mut BlockInfo>,
    current_block: Option<BlockTuple>,
    overflow_block: Option<BlockTuple>,
    current_live_mark: bool,
}

impl LineAllocator {
    pub fn new(block_allocator: BlockAllocator) -> LineAllocator {
        return LineAllocator {
            block_allocator: block_allocator,
            object_map: HashSet::new(),
            object_map_backup: HashSet::new(),
            mark_histogram: VecMap::with_capacity(NUM_LINES_PER_BLOCK),
            unavailable_blocks: RingBuf::new(),
            recyclable_blocks: RingBuf::new(),
            current_block: None,
            overflow_block: None,
            current_live_mark: false,
        };
    }

    pub fn set_gc_object(&mut self, object: GCObjectRef) {
        self.object_map.insert(object);
    }

    pub fn unset_gc_object(&mut self, object: GCObjectRef) {
        self.object_map.remove(&object);
    }

    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        return self.object_map.contains(&object);
    }

    pub fn current_live_mark(&self) -> bool {
        return self.current_live_mark;
    }

    pub fn decrement_lines(&mut self, object: GCObjectRef) {
        unsafe{ (*self.get_block_ptr(object)).decrement_lines(object); }
    }

    pub fn increment_lines(&mut self, object: GCObjectRef) {
        unsafe{ (*self.get_block_ptr(object)).increment_lines(object); }
    }

    pub fn allocate(&mut self, rtti: *const GCRTTI) -> Option<GCObjectRef> {
        let size = unsafe{ (*rtti).object_size() };
        debug!("Request to allocate an object of size {}", size);
        if let Some(object) = self.raw_allocate(size) {
            unsafe { ptr::write(object, GCObject::new(rtti, self.current_live_mark)); }
            valgrind_malloclike!(object, size);
            return Some(object);
        }
        return None;
    }

    pub fn prepare_collection(&mut self) -> bool {
        self.unavailable_blocks.extend(self.recyclable_blocks.drain());
        self.unavailable_blocks.extend(self.current_block.take()
                                           .map(|b| b.0).into_iter());

        let perform_cycle_collection = true;
        return perform_cycle_collection;
    }

    pub fn complete_collection(&mut self) {
        self.mark_histogram.clear();
        self.perform_defrag = false;
        self.sweep_unavailable_blocks();
    }

    pub fn prepare_immix_collection(&mut self) {
        for block in self.unavailable_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
        }

        if cfg!(feature = "valgrind") {
            self.object_map_backup = self.object_map.clone();
        }
        self.object_map.clear();
    }

    pub fn complete_immix_collection(&mut self) {
        self.current_live_mark = !self.current_live_mark;
        if cfg!(feature = "valgrind") {
            for &object in self.object_map_backup.difference(&self.object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }
}

impl LineAllocator {
    unsafe fn get_block_ptr(&mut self, object: GCObjectRef) -> *mut BlockInfo {
        let block_offset = object as uint % BLOCK_SIZE;
        return mem::transmute((object as *mut u8).offset(-(block_offset as int)));
    }

    fn raw_allocate(&mut self, size: uint) -> Option<GCObjectRef> {
        return if size < LINE_SIZE {
            self.current_block.take()
                              .and_then(|tp| self.scan_for_hole(size, tp))
        } else {
            self.overflow_block.take()
                               .and_then(|tp| self.scan_for_hole(size, tp))
                               .or_else(|| self.get_new_block())
        }.or_else(|| self.scan_recyclables(size))
         .or_else(|| self.get_new_block())
         .map(|tp| self.allocate_from_block(size, tp))
         .map(|(tp, object)| {
             if size < LINE_SIZE { self.current_block = Some(tp);
             } else { self.overflow_block = Some(tp); }
             object
         });
    }

    fn scan_for_hole(&mut self, size: uint, block_tuple: BlockTuple)
        -> Option<BlockTuple> {
            let (block, low, high) = block_tuple;
            return match (high - low) as uint >= size {
                true => {
                    debug!("Found hole in block {:p}", block);
                    Some(block_tuple)
                },
                false => match unsafe{ (*block).scan_block(high) } {
                    None => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        self.unavailable_blocks.push_back(block);
                        None
                    },
                    Some((low, high)) =>
                        self.scan_for_hole(size, (block, low, high)),
                }
            };
        }

    fn scan_recyclables(&mut self, size: uint) -> Option<BlockTuple> {
        return match self.recyclable_blocks.pop_front() {
            None => None,
            Some(block) => match unsafe{ (*block).scan_block((LINE_SIZE - 1) as u16) } {
                None => {
                    debug!("Push block {:p} into unavailable_blocks", block);
                    self.unavailable_blocks.push_back(block);
                    self.scan_recyclables(size)
                },
                Some((low, high)) => self.scan_for_hole(size, (block, low, high))
                                         .or_else(|| self.scan_recyclables(size)),
            }
        };
    }

    fn get_new_block(&mut self) -> Option<BlockTuple> {
        debug!("Request new block from block_allocator");
        return self.block_allocator.get_block()
                   .map(|block| (block, LINE_SIZE as u16, (BLOCK_SIZE - 1) as u16));
    }

    fn allocate_from_block(&mut self, size: uint, block_tuple: BlockTuple)
        -> (BlockTuple, GCObjectRef) {
            let (block, low, high) = block_tuple;
            let object = unsafe { (*block).offset(low as uint) };
            self.set_gc_object(object);
            debug!("Allocated object {} of size {} in {} (object={})",
            object, size, block, size >= LINE_SIZE);
            return ((block, low + size as u16, high), object);
        }

    fn sweep_unavailable_blocks(&mut self) {
        let mut unavailable_blocks = RingBuf::new();
        for block in self.unavailable_blocks.drain() {
            if unsafe{ (*block).is_empty() } {
                debug!("Return block {:p} to global block allocator", block);
                self.block_allocator.return_block(block);
            } else {
                unsafe{ (*block).count_holes(); }
                let (holes, marked_lines) = unsafe{ (*block).count_holes_and_marked_lines() };
                if self.mark_histogram.contains_key(&(holes as uint)) {
                    if let Some(val) = self.mark_histogram.get_mut(&(holes as uint)) {
                        *val += marked_lines;
                    }
                } else { self.mark_histogram.insert(holes as uint, marked_lines); }
                debug!("Found {} holes and {} marked lines in block {}",
                       holes, marked_lines, block);
                match holes {
                    0 => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        unavailable_blocks.push_back(block);
                    },
                    _ => {
                        debug!("Push block {:p} into recyclable_blocks", block);
                        self.recyclable_blocks.push_back(block);
                    }
                }
            }
        }
        self.unavailable_blocks.extend(unavailable_blocks.into_iter());
    }
}
<|endoftext|>


<commit_before>pub struct Soundex;

static MAX_CODE_LENGTH: uint = 4;

impl Soundex {
    pub fn new() -> Soundex {
        return Soundex;
    }

    pub fn encode(&self, word: &str) -> ~str {
        let encoded = zero_pad(upper_front(head(word)) + encoded_digits(tail(word)));
        return encoded;
    }
}

fn zero_pad(string: &str) -> ~str {
    let zeros_needed = MAX_CODE_LENGTH - string.len();
    return string + "000".slice_to(zeros_needed);
}

fn upper_front(string: &str) -> ~str {
    let mut upper_string = ~"";
    upper_string.push_char(string.char_at(0).to_uppercase());
    upper_string.push_str(string.slice_from(1));
    return upper_string
}

fn head<'a>(string: &'a str) -> &'a str {
    return string.slice_to(1);
}

fn tail<'a>(string: &'a str) -> &'a str {
    return string.slice_from(1);
}

fn encoded_digits(word: &str) -> ~str {
    let mut encoding = ~"";

    for letter in word.chars() {
        if is_complete(encoding) {
            break;
        }

        if encoded_digit(letter) != last_digit(encoding) {
            encoding.push_str(encoded_digit(letter));
        }
    }

    return encoding;
}

pub fn encoded_digit(letter: char) -> ~str {
    return match letter {
        'b' | 'f' | 'p' | 'v' => ~"1",
        'c' | 'g' | 'j' | 'k' | 'q' | 's' | 'x' | 'z' => ~"2",
        'd' | 't' => ~"3",
        'l' => ~"4",
        'm' | 'n' => ~"5",
        'r' => ~"6",
        _ => ~"",
    };
}

fn is_complete(encoding: &str) -> bool {
    return encoding.len() == MAX_CODE_LENGTH -1;
}

fn last_digit(encoding: &str) -> ~str {
    if encoding.len() == 0 {
        return ~"";
    }
    return encoding.slice(encoding.len()-1, encoding.len()).to_owned();
}
<commit_msg>Refactor. Return special NOT_A_DIGIT instead of empty string.<commit_after>use std::str;

pub struct Soundex;

static MAX_CODE_LENGTH: uint = 4;
static NOT_A_DIGIT: &'static str = "*";

impl Soundex {
    pub fn new() -> Soundex {
        return Soundex;
    }

    pub fn encode(&self, word: &str) -> ~str {
        let encoded = zero_pad(upper_front(head(word)) + encoded_digits(tail(word)));
        return encoded;
    }
}

fn zero_pad(string: &str) -> ~str {
    let zeros_needed = MAX_CODE_LENGTH - string.len();
    return string + "000".slice_to(zeros_needed);
}

fn upper_front(string: &str) -> ~str {
    let mut upper_string = ~"";
    upper_string.push_char(string.char_at(0).to_uppercase());
    upper_string.push_str(string.slice_from(1));
    return upper_string
}

fn head<'a>(string: &'a str) -> &'a str {
    return string.slice_to(1);
}

fn tail<'a>(string: &'a str) -> &'a str {
    return string.slice_from(1);
}

fn encoded_digits(word: &str) -> ~str {
    let mut encoding = ~"";

    for letter in word.chars() {
        if is_complete(encoding) {
            break;
        }

        let digit = encoded_digit(letter);
        if !str::eq_slice(digit, NOT_A_DIGIT) && encoded_digit(letter) != last_digit(encoding) {
            encoding.push_str(encoded_digit(letter));
        }
    }

    return encoding;
}

pub fn encoded_digit(letter: char) -> ~str {
    return match letter {
        'b' | 'f' | 'p' | 'v' => ~"1",
        'c' | 'g' | 'j' | 'k' | 'q' | 's' | 'x' | 'z' => ~"2",
        'd' | 't' => ~"3",
        'l' => ~"4",
        'm' | 'n' => ~"5",
        'r' => ~"6",
        _ => NOT_A_DIGIT.to_owned(),
    };
}

fn is_complete(encoding: &str) -> bool {
    return encoding.len() == MAX_CODE_LENGTH -1;
}

fn last_digit(encoding: &str) -> ~str {
    if encoding.len() == 0 {
        return NOT_A_DIGIT.to_owned();
    }
    return encoding.slice(encoding.len()-1, encoding.len()).to_owned();
}
<|endoftext|>


<commit_before>
use sdl2::rect::Rect;
use sdl2::video::Window;
use sdl2::render::{Texture, Canvas};

pub struct Sprite<'a> {
    rect: Rect,
    pub size: Rect,
    texture: &'a Texture,
}

impl<'a> Sprite<'a> {
    pub fn draw(&self, x: i32, y: i32, canvas: &mut Canvas<Window>) {
        let pos_rect = Rect::new(x, y, self.size.width(), self.size.height());
        canvas.copy(&self.texture, Some(self.rect), Some(pos_rect));
    }
}

pub struct SpriteSheet {
    pub sprite_width: u32,
    pub sprite_height: u32,
    padding: u32,
    pub texture: Texture,
}

impl SpriteSheet {
    pub fn new(texture: Texture, sprite_width: u32, sprite_height: u32, padding: u32) -> Self {
        SpriteSheet {
            sprite_width,
            sprite_height,
            padding,
            texture: texture,
        }
    }

    // Creates a sprite object
    pub fn get_sprite(&self, index: usize) -> Sprite {
        let texture_query = self.texture.query();
        let sheet_width = texture_query.width;

        let columns = sheet_width / (self.sprite_width + self.padding);

        let sheet_x = index as u32 % columns;
        let sheet_y = index as u32 / columns;

        let px = sheet_x * (self.sprite_width + self.padding);
        let py = sheet_y * (self.sprite_height + self.padding);

        Sprite {
            rect: Rect::new(px as i32, py as i32, self.sprite_width, self.sprite_height),
            size: Rect::new(0, 0, self.sprite_width, self.sprite_height),
            texture: &self.texture,
        }
    }
}
<commit_msg>handling canvas copy results to get rid of more compiler warnings<commit_after>
use sdl2::rect::Rect;
use sdl2::video::Window;
use sdl2::render::{Texture, Canvas};

pub struct Sprite<'a> {
    rect: Rect,
    pub size: Rect,
    texture: &'a Texture,
}

impl<'a> Sprite<'a> {
    pub fn draw(&self, x: i32, y: i32, canvas: &mut Canvas<Window>) {
        let pos_rect = Rect::new(x, y, self.size.width(), self.size.height());
        match canvas.copy(&self.texture, Some(self.rect), Some(pos_rect)) {
            Err(e) => println!("canvas copy error: {}", e),
            _ => {}
        }
    }
}

pub struct SpriteSheet {
    pub sprite_width: u32,
    pub sprite_height: u32,
    padding: u32,
    pub texture: Texture,
}

impl SpriteSheet {
    pub fn new(texture: Texture, sprite_width: u32, sprite_height: u32, padding: u32) -> Self {
        SpriteSheet {
            sprite_width,
            sprite_height,
            padding,
            texture: texture,
        }
    }

    // Creates a sprite object
    pub fn get_sprite(&self, index: usize) -> Sprite {
        let texture_query = self.texture.query();
        let sheet_width = texture_query.width;

        let columns = sheet_width / (self.sprite_width + self.padding);

        let sheet_x = index as u32 % columns;
        let sheet_y = index as u32 / columns;

        let px = sheet_x * (self.sprite_width + self.padding);
        let py = sheet_y * (self.sprite_height + self.padding);

        Sprite {
            rect: Rect::new(px as i32, py as i32, self.sprite_width, self.sprite_height),
            size: Rect::new(0, 0, self.sprite_width, self.sprite_height),
            texture: &self.texture,
        }
    }
}
<|endoftext|>