<commit_before>use activation_function::ActivationFunction;
use acyclic_network::{NodeType, Network, Node};
pub use acyclic_network::NodeIndex as CppnNodeIndex;
use fixedbitset::FixedBitSet;

#[derive(Clone, Debug)]
pub enum CppnNodeType<A: ActivationFunction> {
    Bias,
    Input,
    Output,
    Hidden(A),
}

const CPPN_BIAS_WEIGHT: f64 = 1.0;

impl<A: ActivationFunction> NodeType for CppnNodeType<A> {
    fn accept_incoming_links(&self) -> bool {
        match *self {
            CppnNodeType::Input | CppnNodeType::Bias => false,
            _ => true,
        }
    }

    fn accept_outgoing_links(&self) -> bool {
        match *self {
            CppnNodeType::Output => false,
            _ => true,
        }
    }
}

pub type CppnNode<A: ActivationFunction> = Node<CppnNodeType<A>, f64>;
pub type CppnGraph<A: ActivationFunction> = Network<CppnNodeType<A>, f64>;

/// Represents a Compositional Pattern Producing Network (CPPN)
pub struct Cppn<'a, A: ActivationFunction + 'a> {
    graph: &'a CppnGraph<A>,
    inputs: Vec<CppnNodeIndex>,
    outputs: Vec<CppnNodeIndex>,

    // For each node in `graph` there exists a corresponding field in `incoming_signals` describing
    // the sum of all input signals for that node.  We could store it inline in the `CppnNode`, but
    // this would require to make the whole CppnGraph mutable.
    incoming_signals: Vec<f64>,
}

impl<'a, A: ActivationFunction> Cppn<'a, A> {
    pub fn new(graph: &'a CppnGraph<A>) -> Cppn<'a, A> {
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        for (i, node) in graph.nodes().iter().enumerate() {
            match *node.node_type() {
                CppnNodeType::Input => {
                    inputs.push(CppnNodeIndex::new(i));
                }
                CppnNodeType::Output => {
                    outputs.push(CppnNodeIndex::new(i));
                }
                _ => {}
            }
        }

        Cppn {
            graph: graph,
            inputs: inputs,
            outputs: outputs,
            incoming_signals: graph.nodes().iter().map(|_| 0.0).collect(),
        }
    }

    fn set_signal(&mut self, node_idx: CppnNodeIndex, value: f64) {
        self.incoming_signals[node_idx.index()] = value;
    }

    fn reset_signals(&mut self) {
        for value in self.incoming_signals.iter_mut() {
            *value = 0.0;
        }
    }

    /// Forward-propagate the signals starting from `from_nodes`. We use
    /// breadth-first-search (BFS).
    fn propagate_signals(&mut self, mut nodes: Vec<CppnNodeIndex>, mut seen: FixedBitSet) {
        while let Some(node_idx) = nodes.pop() {
            let node = &self.graph.nodes()[node_idx.index()];
            let input = self.incoming_signals[node_idx.index()];
            let output = match *node.node_type() {
                CppnNodeType::Hidden(ref activation_function) => {
                    // apply activation function on `input`
                    activation_function.calculate(input)
                }
                CppnNodeType::Input | CppnNodeType::Output => {
                    // Simply pass on the input signal.
                    input
                }
                CppnNodeType::Bias => CPPN_BIAS_WEIGHT,
            };

            // propagate output signal to outgoing links.
            node.each_active_forward_link(|out_node_idx, weight| {
                let out_node = out_node_idx.index();
                self.incoming_signals[out_node] += weight * output;
                if !seen.contains(out_node) {
                    seen.insert(out_node);
                    nodes.push(out_node_idx);
                }
            });
        }
    }

    /// Calculate all outputs
    pub fn calculate(&mut self, inputs: &[&[f64]]) -> Vec<f64> {
        assert!(self.incoming_signals.len() == self.graph.nodes().len());
        self.reset_signals();

        let mut nodes = Vec::new(); // XXX: worst case capacity
        let mut seen = FixedBitSet::with_capacity(self.incoming_signals.len());

        // assign all inputs
        let mut i = 0;
        for input_list in inputs.iter() {
            for &input in input_list.iter() {
                let input_idx = self.inputs[i];
                self.set_signal(input_idx, input);
                nodes.push(input_idx);
                seen.insert(input_idx.index());
                i += 1;
            }
        }
        assert!(i == self.inputs.len());

        // propagate the signals starting from the input signals.
        self.propagate_signals(nodes, seen);

        self.outputs
            .iter()
            .map(|&node_idx| self.incoming_signals[node_idx.index()])
            .collect()
    }
}

#[test]
fn test_cycle() {
    use super::bipolar::BipolarActivationFunction as AF;
    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    let h2 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    assert_eq!(true, g.valid_link(i1, i1).is_err());
    assert_eq!(true, g.valid_link(h1, h1).is_err());

    assert_eq!(true, g.valid_link(h1, i1).is_err());
    assert_eq!(Ok(()), g.valid_link(i1, h1));
    assert_eq!(Ok(()), g.valid_link(i1, h2));
    assert_eq!(Ok(()), g.valid_link(h1, h2));

    g.add_link(i1, h1, 0.0);
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(false, g.link_would_cycle(i1, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
    assert_eq!(true, g.link_would_cycle(i1, i1));
    assert_eq!(false, g.link_would_cycle(h1, h2));
    assert_eq!(false, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(h2, i1));

    g.add_link(h1, h2, 0.0);
    assert_eq!(true, g.link_would_cycle(h2, i1));
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(true, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
}

#[test]
fn test_simple_cppn() {
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Linear));
    let o1 = g.add_node(CppnNodeType::Output);
    g.add_link(i1, h1, 0.5);
    g.add_link(h1, o1, 1.0);

    let mut cppn = Cppn::new(&g);

    assert_eq!(vec![0.5 * 0.5], cppn.calculate(&[&[0.5]]));
    assert_eq!(vec![1.0], cppn.calculate(&[&[4.0]]));
    assert_eq!(vec![-1.0], cppn.calculate(&[&[-4.0]]));
}

#[test]
fn test_find_random_unconnected_link_no_cycle() {
    use rand;
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::<AF>::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let o1 = g.add_node(CppnNodeType::Output);
    let o2 = g.add_node(CppnNodeType::Output);

    let mut rng = rand::thread_rng();

    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    let l = link.unwrap();
    assert!((i1, o1) == l || (i1, o2) == l);

    g.add_link(i1, o2, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    assert_eq!((i1, o1), link.unwrap());

    g.add_link(i1, o1, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(false, link.is_some());
}
<commit_msg>Use newest version of acyclic-network-rs<commit_after>use activation_function::ActivationFunction;
use acyclic_network::{NodeType, Network, Node};
pub use acyclic_network::NodeIndex as CppnNodeIndex;
use fixedbitset::FixedBitSet;

#[derive(Clone, Debug)]
pub enum CppnNodeType<A: ActivationFunction> {
    Bias,
    Input,
    Output,
    Hidden(A),
}

const CPPN_BIAS_WEIGHT: f64 = 1.0;

impl<A: ActivationFunction> NodeType for CppnNodeType<A> {
    fn accept_incoming_links(&self) -> bool {
        match *self {
            CppnNodeType::Input | CppnNodeType::Bias => false,
            _ => true,
        }
    }

    fn accept_outgoing_links(&self) -> bool {
        match *self {
            CppnNodeType::Output => false,
            _ => true,
        }
    }
}

pub type CppnNode<A: ActivationFunction> = Node<CppnNodeType<A>>;
pub type CppnGraph<A: ActivationFunction> = Network<CppnNodeType<A>, f64>;

/// Represents a Compositional Pattern Producing Network (CPPN)
pub struct Cppn<'a, A: ActivationFunction + 'a> {
    graph: &'a CppnGraph<A>,
    inputs: Vec<CppnNodeIndex>,
    outputs: Vec<CppnNodeIndex>,

    // For each node in `graph` there exists a corresponding field in `incoming_signals` describing
    // the sum of all input signals for that node.  We could store it inline in the `CppnNode`, but
    // this would require to make the whole CppnGraph mutable.
    incoming_signals: Vec<f64>,
}

impl<'a, A: ActivationFunction> Cppn<'a, A> {
    pub fn new(graph: &'a CppnGraph<A>) -> Cppn<'a, A> {
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        graph.each_node_with_index(|node, index| {
            match *node.node_type() {
                CppnNodeType::Input => {
                    inputs.push(index);
                }
                CppnNodeType::Output => {
                    outputs.push(index);
                }
                _ => {}
            }
        });

        Cppn {
            graph: graph,
            inputs: inputs,
            outputs: outputs,
            incoming_signals: graph.nodes().iter().map(|_| 0.0).collect(),
        }
    }

    fn set_signal(&mut self, node_idx: CppnNodeIndex, value: f64) {
        self.incoming_signals[node_idx.index()] = value;
    }

    fn reset_signals(&mut self) {
        for value in self.incoming_signals.iter_mut() {
            *value = 0.0;
        }
    }

    /// Forward-propagate the signals starting from `from_nodes`. We use
    /// breadth-first-search (BFS).
    fn propagate_signals(&mut self, mut nodes: Vec<CppnNodeIndex>, mut seen: FixedBitSet) {
        while let Some(node_idx) = nodes.pop() {
            let input = self.incoming_signals[node_idx.index()];
            let output = match *self.graph.node_type_of(node_idx) {
                CppnNodeType::Hidden(ref activation_function) => {
                    // apply activation function on `input`
                    activation_function.calculate(input)
                }
                CppnNodeType::Input | CppnNodeType::Output => {
                    // Simply pass on the input signal.
                    input
                }
                CppnNodeType::Bias => CPPN_BIAS_WEIGHT,
            };

            // propagate output signal to outgoing links.
            self.graph.each_active_forward_link_of_node(node_idx, |out_node_idx, weight| {
                let out_node = out_node_idx.index();
                self.incoming_signals[out_node] += weight * output;
                if !seen.contains(out_node) {
                    seen.insert(out_node);
                    nodes.push(out_node_idx);
                }
            });
        }
    }

    /// Calculate all outputs
    pub fn calculate(&mut self, inputs: &[&[f64]]) -> Vec<f64> {
        assert!(self.incoming_signals.len() == self.graph.nodes().len());
        self.reset_signals();

        let mut nodes = Vec::new(); // XXX: worst case capacity
        let mut seen = FixedBitSet::with_capacity(self.incoming_signals.len());

        // assign all inputs
        let mut i = 0;
        for input_list in inputs.iter() {
            for &input in input_list.iter() {
                let input_idx = self.inputs[i];
                self.set_signal(input_idx, input);
                nodes.push(input_idx);
                seen.insert(input_idx.index());
                i += 1;
            }
        }
        assert!(i == self.inputs.len());

        // propagate the signals starting from the input signals.
        self.propagate_signals(nodes, seen);

        self.outputs
            .iter()
            .map(|&node_idx| self.incoming_signals[node_idx.index()])
            .collect()
    }
}

#[test]
fn test_cycle() {
    use super::bipolar::BipolarActivationFunction as AF;
    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    let h2 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    assert_eq!(true, g.valid_link(i1, i1).is_err());
    assert_eq!(true, g.valid_link(h1, h1).is_err());

    assert_eq!(true, g.valid_link(h1, i1).is_err());
    assert_eq!(Ok(()), g.valid_link(i1, h1));
    assert_eq!(Ok(()), g.valid_link(i1, h2));
    assert_eq!(Ok(()), g.valid_link(h1, h2));

    g.add_link(i1, h1, 0.0);
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(false, g.link_would_cycle(i1, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
    assert_eq!(true, g.link_would_cycle(i1, i1));
    assert_eq!(false, g.link_would_cycle(h1, h2));
    assert_eq!(false, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(h2, i1));

    g.add_link(h1, h2, 0.0);
    assert_eq!(true, g.link_would_cycle(h2, i1));
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(true, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
}

#[test]
fn test_simple_cppn() {
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Linear));
    let o1 = g.add_node(CppnNodeType::Output);
    g.add_link(i1, h1, 0.5);
    g.add_link(h1, o1, 1.0);

    let mut cppn = Cppn::new(&g);

    assert_eq!(vec![0.5 * 0.5], cppn.calculate(&[&[0.5]]));
    assert_eq!(vec![1.0], cppn.calculate(&[&[4.0]]));
    assert_eq!(vec![-1.0], cppn.calculate(&[&[-4.0]]));
}

#[test]
fn test_find_random_unconnected_link_no_cycle() {
    use rand;
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::<AF>::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let o1 = g.add_node(CppnNodeType::Output);
    let o2 = g.add_node(CppnNodeType::Output);

    let mut rng = rand::thread_rng();

    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    let l = link.unwrap();
    assert!((i1, o1) == l || (i1, o2) == l);

    g.add_link(i1, o2, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    assert_eq!((i1, o1), link.unwrap());

    g.add_link(i1, o1, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(false, link.is_some());
}
<|endoftext|>


<commit_before>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 4;
const DATA_READY: usize = 1 << 5;
const RESERVED: usize = 1 << 6;
const ERROR: usize = 1 << 7;
const DISK_MASK: usize = (MAX_DISK - 1) as usize;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(0)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                buffer.bytes[buffer.i as usize] = value;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            let mut buffer = self.buffer.lock().unwrap();
            match parameters.command {
                NOP => (),
                SEL_DSK => {
                    if buffer.bytes[0] < MAX_DISK {
                        parameters.disk = buffer.bytes[0];
                    } else {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                    }
                },
                SEL_TRK => {
                    let track = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            if track < disk.tracks {
                                parameters.track = track;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    }
                },
                SEL_SEC => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let sector = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                            if sector < disk.spt {
                                parameters.sector = sector;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                READ => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_slice() };
                            for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                *byte = bytes[off + i];
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                WRITE => {
                    match disks[parameters.disk as usize] {
                        Some(ref mut disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_mut_slice() };
                            for (i, byte) in buffer.bytes.iter().enumerate() {
                                bytes[i] = *byte;
                            };
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }

                }
                _ => (),
            }
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
        }
    }
}
<commit_msg>Remove disk number from status register for now.<commit_after>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(0)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                buffer.bytes[buffer.i as usize] = value;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            let mut buffer = self.buffer.lock().unwrap();
            match parameters.command {
                NOP => (),
                SEL_DSK => {
                    if buffer.bytes[0] < MAX_DISK {
                        parameters.disk = buffer.bytes[0];
                    } else {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                    }
                },
                SEL_TRK => {
                    let track = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            if track < disk.tracks {
                                parameters.track = track;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    }
                },
                SEL_SEC => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let sector = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                            if sector < disk.spt {
                                parameters.sector = sector;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                READ => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_slice() };
                            for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                *byte = bytes[off + i];
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                WRITE => {
                    match disks[parameters.disk as usize] {
                        Some(ref mut disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_mut_slice() };
                            for (i, byte) in buffer.bytes.iter().enumerate() {
                                bytes[i] = *byte;
                            };
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }

                }
                _ => (),
            }
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
        }
    }
}
<|endoftext|>


<commit_before>use hb;
use std;
use std::ptr::NonNull;

use std::os::raw::c_void;

use common::{HarfbuzzObject, Owned, Shared};
use face::Face;
pub use font_funcs::FontFuncs;
use font_funcs::FontFuncsImpl;

use std::ffi::CStr;
use std::marker::PhantomData;

pub type Glyph = u32;
pub type FontExtents = hb::hb_font_extents_t;
pub type GlyphExtents = hb::hb_glyph_extents_t;
pub type Position = hb::hb_position_t;

pub(crate) extern "C" fn destroy_box<U>(ptr: *mut c_void) {
    unsafe { Box::from_raw(ptr as *mut U) };
}

/// A font is the most important concept in harfbuzz.
///
/// A font can be created either as a subfont of an existing font or directly from a `Face` using
/// its `create_font` function.
#[derive(Debug)]
#[repr(C)]
pub struct Font<'a> {
    raw: NonNull<hb::hb_font_t>,
    marker: PhantomData<&'a hb::hb_font_t>,
}

impl<'a> Font<'a> {
    /// Create a new font with a specified `Face`.
    pub fn new<T: Into<Shared<Face<'a>>>>(face: T) -> Owned<Self> {
        unsafe {
            let face = face.into();
            let raw_font = hb::hb_font_create(Shared::into_raw(face));
            // set default font funcs for a completely new font
            // hb::hb_ot_font_set_funcs(raw_font);
            Owned::from_raw(raw_font)
        }
    }

    /// Create a new sub font from the current font that by default inherits its parent font's
    /// face, scale, ppem and font funcs.
    pub fn create_sub_font<T: Into<Shared<Self>>>(font: T) -> Owned<Self> {
        unsafe { Owned::from_raw(hb::hb_font_create_sub_font(font.into().as_raw())) }
    }

    /// Returns the parent font.
    pub fn parent(&self) -> Option<Shared<Self>> {
        unsafe {
            let parent = hb::hb_font_get_parent(self.as_raw());
            if parent.is_null() {
                // hb_font_get_parent returns null-ptr if called on the empty font.
                None
            } else {
                Some(Shared::from_raw_ref(parent))
            }
        }
    }

    /// Returns the face which was used to create the font.
    pub fn face(&self) -> Shared<Face<'a>> {
        unsafe { Shared::from_raw_ref(hb::hb_font_get_face(self.as_raw())) }
    }

    pub fn scale(&self) -> (i32, i32) {
        let mut result = (0i32, 0i32);
        unsafe { hb::hb_font_get_scale(self.as_raw(), &mut result.0, &mut result.1) };
        result
    }

    pub fn set_scale(&mut self, x: i32, y: i32) {
        unsafe { hb::hb_font_set_scale(self.as_raw(), x, y) };
    }

    pub fn ppem(&self) -> (u32, u32) {
        let mut result = (0u32, 0u32);
        unsafe { hb::hb_font_get_ppem(self.as_raw(), &mut result.0, &mut result.1) };
        result
    }

    pub fn set_ppem(&mut self, x: u32, y: u32) {
        unsafe { hb::hb_font_set_ppem(self.as_raw(), x, y) };
    }

    pub fn set_font_funcs<FuncsType>(&mut self, funcs: FuncsType)
    where
        FuncsType: 'a + Send + Sync + FontFuncs,
    {
        let funcs_impl: Owned<FontFuncsImpl<FuncsType>> = FontFuncsImpl::from_trait_impl();
        let font_data = Box::new(funcs);
        unsafe {
            hb::hb_font_set_funcs(
                self.as_raw(),
                funcs_impl.as_raw(),
                Box::into_raw(font_data) as *mut _,
                Some(destroy_box::<FuncsType>),
            )
        };
    }

    // scale from parent font
    pub(crate) fn parent_scale_x_distance(&self, f: impl Fn(&Font) -> Position) -> Position {
        let x_scale = self.scale().0;
        if let Some(parent) = self.parent() {
            let parent_x_scale = parent.scale().0;

            if parent_x_scale != x_scale {
                (f(&parent) as i64 * x_scale as i64 / parent_x_scale as i64) as Position
            } else {
                f(&parent)
            }
        } else {
            0
        }
    }

    // scale from parent font
    pub(crate) fn parent_scale_y_distance(&self, f: impl Fn(&Font) -> Position) -> Position {
        let y_scale = self.scale().0;
        if let Some(parent) = self.parent() {
            let parent_y_scale = parent.scale().0;

            if parent_y_scale != y_scale {
                (f(&parent) as i64 * y_scale as i64 / parent_y_scale as i64) as Position
            } else {
                f(&parent)
            }
        } else {
            0
        }
    }

    // scale from parent font
    pub(crate) fn parent_scale_position(&self, v: (Position, Position)) -> (Position, Position) {
        (
            self.parent_scale_x_distance(|_| v.0),
            self.parent_scale_y_distance(|_| v.1),
        )
    }

    pub fn get_font_h_extents(&self) -> Option<FontExtents> {
        unsafe {
            let mut extents = std::mem::uninitialized::<FontExtents>();
            let result = hb::hb_font_get_h_extents(self.as_raw(), &mut extents);
            if result == 1 {
                Some(extents)
            } else {
                None
            }
        }
    }

    pub fn get_font_v_extents(&self) -> Option<FontExtents> {
        unsafe {
            let mut extents = std::mem::uninitialized::<FontExtents>();
            let result = hb::hb_font_get_v_extents(self.as_raw(), &mut extents);
            if result == 1 {
                Some(extents)
            } else {
                None
            }
        }
    }

    pub fn get_nominal_glyph(&self, c: char) -> Option<Glyph> {
        unsafe {
            let mut glyph = 0;
            let result = hb::hb_font_get_nominal_glyph(self.as_raw(), c as u32, &mut glyph);
            if result == 1 {
                Some(glyph)
            } else {
                None
            }
        }
    }

    pub fn get_variation_glyph(&self, c: char, v: char) -> Option<Glyph> {
        unsafe {
            let mut glyph = 0;
            let result =
                hb::hb_font_get_variation_glyph(self.as_raw(), c as u32, v as u32, &mut glyph);
            if result == 1 {
                Some(glyph)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_h_advance(&self, glyph: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_h_advance(self.as_raw(), glyph) }
    }

    pub fn get_glyph_v_advance(&self, glyph: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_v_advance(self.as_raw(), glyph) }
    }

    pub fn get_glyph_h_origin(&self, glyph: Glyph) -> Option<(Position, Position)> {
        unsafe {
            let mut pos = (0, 0);
            let result =
                hb::hb_font_get_glyph_h_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1);
            if result == 1 {
                Some(pos)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_v_origin(&self, glyph: Glyph) -> Option<(Position, Position)> {
        unsafe {
            let mut pos = (0, 0);
            let result =
                hb::hb_font_get_glyph_v_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1);
            if result == 1 {
                Some(pos)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_h_kerning(&self, left: Glyph, right: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_h_kerning(self.as_raw(), left, right) }
    }

    pub fn get_glyph_v_kerning(&self, before: Glyph, after: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_v_kerning(self.as_raw(), before, after) }
    }

    pub fn get_glyph_extents(&self, glyph: Glyph) -> Option<GlyphExtents> {
        unsafe {
            let mut extents = std::mem::uninitialized::<GlyphExtents>();
            let result = hb::hb_font_get_glyph_extents(self.as_raw(), glyph, &mut extents);
            if result == 1 {
                Some(extents)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_contour_point(
        &self,
        glyph: Glyph,
        point_index: u32,
    ) -> Option<(Position, Position)> {
        unsafe {
            let mut pos = (0, 0);
            let result = hb::hb_font_get_glyph_contour_point(
                self.as_raw(),
                glyph,
                point_index,
                &mut pos.0,
                &mut pos.1,
            );
            if result == 1 {
                Some(pos)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_name(&self, glyph: Glyph) -> Option<String> {
        let mut buffer = [0; 256];
        let result = unsafe {
            hb::hb_font_get_glyph_name(
                self.as_raw(),
                glyph,
                buffer.as_mut_ptr() as *mut _,
                buffer.len() as u32,
            )
        };
        if result == 1 {
            let cstr = unsafe { CStr::from_ptr(buffer.as_ptr()) };
            cstr.to_str().ok().map(|y| y.to_string())
        } else {
            None
        }
    }

    pub fn get_glyph_from_name(&self, name: &str) -> Option<Glyph> {
        unsafe {
            let mut glyph = 0;
            let result = hb::hb_font_get_glyph_from_name(
                self.as_raw(),
                name.as_ptr() as *mut _,
                name.len() as i32,
                &mut glyph,
            );
            if result == 1 {
                Some(glyph)
            } else {
                None
            }
        }
    }
}

unsafe impl<'a> HarfbuzzObject for Font<'a> {
    type Raw = hb::hb_font_t;

    unsafe fn from_raw(raw: *const Self::Raw) -> Self {
        Font {
            raw: NonNull::new_unchecked(raw as *mut _),
            marker: PhantomData,
        }
    }

    fn as_raw(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }

    unsafe fn reference(&self) {
        hb::hb_font_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_font_destroy(self.as_raw());
    }
}
<commit_msg>Add Font::empty constructor<commit_after>use hb;
use std;
use std::ptr::NonNull;

use std::os::raw::c_void;

use common::{HarfbuzzObject, Owned, Shared};
use face::Face;
pub use font_funcs::FontFuncs;
use font_funcs::FontFuncsImpl;

use std::ffi::CStr;
use std::marker::PhantomData;

pub type Glyph = u32;
pub type FontExtents = hb::hb_font_extents_t;
pub type GlyphExtents = hb::hb_glyph_extents_t;
pub type Position = hb::hb_position_t;

pub(crate) extern "C" fn destroy_box<U>(ptr: *mut c_void) {
    unsafe { Box::from_raw(ptr as *mut U) };
}

/// A font is the most important concept in harfbuzz.
///
/// A font can be created either as a subfont of an existing font or directly from a `Face` using
/// its `create_font` function.
#[derive(Debug)]
#[repr(C)]
pub struct Font<'a> {
    raw: NonNull<hb::hb_font_t>,
    marker: PhantomData<&'a hb::hb_font_t>,
}

impl<'a> Font<'a> {
    /// Create a new font with a specified `Face`.
    pub fn new<T: Into<Shared<Face<'a>>>>(face: T) -> Owned<Self> {
        unsafe {
            let face = face.into();
            let raw_font = hb::hb_font_create(Shared::into_raw(face));
            // set default font funcs for a completely new font

    /// Returns an empty font.
    ///
    /// This can be useful when you need a dummy font for whatever reason. Any
    /// function you call on the empty font will return some reasonable default
    /// value.
    pub fn empty() -> Owned<Self> {
        unsafe {
            let raw_font = hb::hb_font_get_empty();
            Owned::from_raw(raw_font)
        }
    }

    /// Create a new sub font from the current font that by default inherits its parent font's
    /// face, scale, ppem and font funcs.
    pub fn create_sub_font<T: Into<Shared<Self>>>(font: T) -> Owned<Self> {
        unsafe { Owned::from_raw(hb::hb_font_create_sub_font(font.into().as_raw())) }
    }

    /// Returns the parent font.
    pub fn parent(&self) -> Option<Shared<Self>> {
        unsafe {
            let parent = hb::hb_font_get_parent(self.as_raw());
            if parent.is_null() {
                // hb_font_get_parent returns null-ptr if called on the empty font.
                None
            } else {
                Some(Shared::from_raw_ref(parent))
            }
        }
    }

    /// Returns the face which was used to create the font.
    pub fn face(&self) -> Shared<Face<'a>> {
        unsafe { Shared::from_raw_ref(hb::hb_font_get_face(self.as_raw())) }
    }

    pub fn scale(&self) -> (i32, i32) {
        let mut result = (0i32, 0i32);
        unsafe { hb::hb_font_get_scale(self.as_raw(), &mut result.0, &mut result.1) };
        result
    }

    pub fn set_scale(&mut self, x: i32, y: i32) {
        unsafe { hb::hb_font_set_scale(self.as_raw(), x, y) };
    }

    pub fn ppem(&self) -> (u32, u32) {
        let mut result = (0u32, 0u32);
        unsafe { hb::hb_font_get_ppem(self.as_raw(), &mut result.0, &mut result.1) };
        result
    }

    pub fn set_ppem(&mut self, x: u32, y: u32) {
        unsafe { hb::hb_font_set_ppem(self.as_raw(), x, y) };
    }

    pub fn set_font_funcs<FuncsType>(&mut self, funcs: FuncsType)
    where
        FuncsType: 'a + Send + Sync + FontFuncs,
    {
        let funcs_impl: Owned<FontFuncsImpl<FuncsType>> = FontFuncsImpl::from_trait_impl();
        let font_data = Box::new(funcs);
        unsafe {
            hb::hb_font_set_funcs(
                self.as_raw(),
                funcs_impl.as_raw(),
                Box::into_raw(font_data) as *mut _,
                Some(destroy_box::<FuncsType>),
            )
        };
    }

    // scale from parent font
    pub(crate) fn parent_scale_x_distance(&self, f: impl Fn(&Font) -> Position) -> Position {
        let x_scale = self.scale().0;
        if let Some(parent) = self.parent() {
            let parent_x_scale = parent.scale().0;

            if parent_x_scale != x_scale {
                (f(&parent) as i64 * x_scale as i64 / parent_x_scale as i64) as Position
            } else {
                f(&parent)
            }
        } else {
            0
        }
    }

    // scale from parent font
    pub(crate) fn parent_scale_y_distance(&self, f: impl Fn(&Font) -> Position) -> Position {
        let y_scale = self.scale().0;
        if let Some(parent) = self.parent() {
            let parent_y_scale = parent.scale().0;

            if parent_y_scale != y_scale {
                (f(&parent) as i64 * y_scale as i64 / parent_y_scale as i64) as Position
            } else {
                f(&parent)
            }
        } else {
            0
        }
    }

    // scale from parent font
    pub(crate) fn parent_scale_position(&self, v: (Position, Position)) -> (Position, Position) {
        (
            self.parent_scale_x_distance(|_| v.0),
            self.parent_scale_y_distance(|_| v.1),
        )
    }

    pub fn get_font_h_extents(&self) -> Option<FontExtents> {
        unsafe {
            let mut extents = std::mem::uninitialized::<FontExtents>();
            let result = hb::hb_font_get_h_extents(self.as_raw(), &mut extents);
            if result == 1 {
                Some(extents)
            } else {
                None
            }
        }
    }

    pub fn get_font_v_extents(&self) -> Option<FontExtents> {
        unsafe {
            let mut extents = std::mem::uninitialized::<FontExtents>();
            let result = hb::hb_font_get_v_extents(self.as_raw(), &mut extents);
            if result == 1 {
                Some(extents)
            } else {
                None
            }
        }
    }

    pub fn get_nominal_glyph(&self, c: char) -> Option<Glyph> {
        unsafe {
            let mut glyph = 0;
            let result = hb::hb_font_get_nominal_glyph(self.as_raw(), c as u32, &mut glyph);
            if result == 1 {
                Some(glyph)
            } else {
                None
            }
        }
    }

    pub fn get_variation_glyph(&self, c: char, v: char) -> Option<Glyph> {
        unsafe {
            let mut glyph = 0;
            let result =
                hb::hb_font_get_variation_glyph(self.as_raw(), c as u32, v as u32, &mut glyph);
            if result == 1 {
                Some(glyph)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_h_advance(&self, glyph: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_h_advance(self.as_raw(), glyph) }
    }

    pub fn get_glyph_v_advance(&self, glyph: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_v_advance(self.as_raw(), glyph) }
    }

    pub fn get_glyph_h_origin(&self, glyph: Glyph) -> Option<(Position, Position)> {
        unsafe {
            let mut pos = (0, 0);
            let result =
                hb::hb_font_get_glyph_h_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1);
            if result == 1 {
                Some(pos)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_v_origin(&self, glyph: Glyph) -> Option<(Position, Position)> {
        unsafe {
            let mut pos = (0, 0);
            let result =
                hb::hb_font_get_glyph_v_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1);
            if result == 1 {
                Some(pos)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_h_kerning(&self, left: Glyph, right: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_h_kerning(self.as_raw(), left, right) }
    }

    pub fn get_glyph_v_kerning(&self, before: Glyph, after: Glyph) -> Position {
        unsafe { hb::hb_font_get_glyph_v_kerning(self.as_raw(), before, after) }
    }

    pub fn get_glyph_extents(&self, glyph: Glyph) -> Option<GlyphExtents> {
        unsafe {
            let mut extents = std::mem::uninitialized::<GlyphExtents>();
            let result = hb::hb_font_get_glyph_extents(self.as_raw(), glyph, &mut extents);
            if result == 1 {
                Some(extents)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_contour_point(
        &self,
        glyph: Glyph,
        point_index: u32,
    ) -> Option<(Position, Position)> {
        unsafe {
            let mut pos = (0, 0);
            let result = hb::hb_font_get_glyph_contour_point(
                self.as_raw(),
                glyph,
                point_index,
                &mut pos.0,
                &mut pos.1,
            );
            if result == 1 {
                Some(pos)
            } else {
                None
            }
        }
    }

    pub fn get_glyph_name(&self, glyph: Glyph) -> Option<String> {
        let mut buffer = [0; 256];
        let result = unsafe {
            hb::hb_font_get_glyph_name(
                self.as_raw(),
                glyph,
                buffer.as_mut_ptr() as *mut _,
                buffer.len() as u32,
            )
        };
        if result == 1 {
            let cstr = unsafe { CStr::from_ptr(buffer.as_ptr()) };
            cstr.to_str().ok().map(|y| y.to_string())
        } else {
            None
        }
    }

    pub fn get_glyph_from_name(&self, name: &str) -> Option<Glyph> {
        unsafe {
            let mut glyph = 0;
            let result = hb::hb_font_get_glyph_from_name(
                self.as_raw(),
                name.as_ptr() as *mut _,
                name.len() as i32,
                &mut glyph,
            );
            if result == 1 {
                Some(glyph)
            } else {
                None
            }
        }
    }
}

unsafe impl<'a> HarfbuzzObject for Font<'a> {
    type Raw = hb::hb_font_t;

    unsafe fn from_raw(raw: *const Self::Raw) -> Self {
        Font {
            raw: NonNull::new_unchecked(raw as *mut _),
            marker: PhantomData,
        }
    }

    fn as_raw(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }

    unsafe fn reference(&self) {
        hb::hb_font_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_font_destroy(self.as_raw());
    }
}

impl<'a> Default for Owned<Font<'a>> {
    fn default() -> Self {
        Font::empty()
    }
}

impl<'a> Default for Shared<Font<'a>> {
    fn default() -> Self {
        Font::empty().into()
    }
}
<|endoftext|>


<commit_before>pub use self::{Error as GameError, State as GameState};

use std::collections::{HashMap, HashSet};

use ::event::{Event, Info, Player};

#[derive(Clone, Debug, PartialEq)]
/// An error that occurs while processing a game.
pub enum Error {
    /// The event provided is not valid in the current state.
    InvalidEvent(State, Event),
    /// A portion of processing is not yet implemented.
    Unimplemented,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::InvalidEvent(ref state, ref event) => {
                write!(f, "the event {:?} is not valid in the {:?} state", event, state)
            }
            Error::Unimplemented => {
                write!(f, "the requested command is not yet implemented")
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// The parsing state of the game.
pub enum State {
    /// Parsing info events.
    Info,
    /// Parsing start events.
    Starters,
    /// Parsing plays.
    Plays,
    /// Parsing end-of-game data.
    Data,
}

#[derive(Clone, Debug, PartialEq)]
/// An MLB game.
pub struct Game {
    /// The game id.
    pub id: String,
    /// Metadata information about the game.
    pub info: HashMap<Info, String>,
    /// The players that started the game.
    pub starters: HashSet<Player>,
    state: State,
}

impl Game {
    /// Instantiate a new game object in the initial state.
    pub fn new<S: Into<String>>(id: S) -> Game {
        Game {
            id: id.into(),
            info: HashMap::new(),
            state: State::Info,
            starters: HashSet::new(),
        }
    }

    fn process_info_event(&mut self, event: Event) -> Result<(), Error> {
        match event {
            Event::Info { key, data } => {
                self.info.insert(key, data);
                Ok(())
            }
            Event::Start { .. } => {
                // Done with info, so process the event in the starters state.
                self.state = State::Starters;
                self.process_event(event)
            }
            _ => {
                Err(Error::InvalidEvent(self.state, event.clone()))
            }
        }
    }

    fn process_starter_event(&mut self, event: Event) -> Result<(), Error> {
        match event {
            Event::Start { player } => {
                self.starters.insert(player);
                Ok(())
            }
            Event::Play { .. } => {
                // Done with starters, so start play-by-play.
                self.state = State::Plays;
                self.process_event(event)
            }
            _ => {
                Err(Error::InvalidEvent(self.state, event.clone()))
            }
        }
    }

    /// Process an event in the context of the game.
    pub fn process_event(&mut self, event: Event) -> Result<(), Error> {
        match self.state {
            State::Info => self.process_info_event(event),
            State::Starters => self.process_starter_event(event),
            _ => Err(Error::Unimplemented),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashSet;
    use std::iter::FromIterator;

    use ::event::{Event, Info, Player, PlayDescription, PlayEvent, Team};

    #[test]
    fn test_process_info() {
        let mut game = Game::new("foo");

        assert_eq!(Ok(()), game.process_event(Event::Info { key: Info::HomeTeam, data: "bar".into() }));
        assert_eq!(Some(&"bar".into()), game.info.get(&Info::HomeTeam));

        let evt = Event::GameId { id: "blah".into() };
        assert_eq!(Err(Error::InvalidEvent(State::Info, evt.clone())), game.process_event(evt.clone()));

        let player = Player {
            id: "fred103".into(),
            name: "fred".into(),
            team: Team::Home,
            batting_pos: 7,
            fielding_pos: 6,
        };

        assert_eq!(Ok(()), game.process_event(Event::Start { player: player }));
        assert_eq!(State::Starters, game.state);
    }

    #[test]
    fn test_process_starters() {
        let mut game = Game::new("foo");
        let player1 = Player {
            id: "fred103".into(),
            name: "fred".into(),
            team: Team::Home,
            batting_pos: 7,
            fielding_pos: 6,
        };
        let player2 = Player {
            id: "jim104".into(),
            name: "jim".into(),
            team: Team::Visiting,
            batting_pos: 3,
            fielding_pos: 1,
        };

        assert_eq!(Ok(()), game.process_event(Event::Start { player: player1.clone() }));
        assert_eq!(Ok(()), game.process_event(Event::Start { player: player2.clone() }));
        assert_eq!(HashSet::from_iter(vec![player1.clone(), player2.clone()].into_iter()), game.starters);

        let evt = Event::Info { key: Info::HomeTeam, data: "bar".into() };
        assert_eq!(Err(Error::InvalidEvent(State::Starters, evt.clone())), game.process_event(evt.clone()));

        assert_eq!(Err(Error::Unimplemented), game.process_event(Event::Play {
            inning: 2,
            team: Team::Visiting,
            player: "foo".into(),
            count: None,
            pitches: vec![],
            event: PlayEvent {
                description: PlayDescription::Balk,
                modifiers: vec![],
                advances: vec![],
            }
        }));
        assert_eq!(State::Plays, game.state);
    }
}<commit_msg>Handle plays and substitutions for game processing.<commit_after>pub use self::{Error as GameError, State as GameState};

use std::collections::{HashMap, HashSet};

use ::event::{Event, Info, Player, Team};

#[derive(Clone, Debug, PartialEq)]
/// An error that occurs while processing a game.
pub enum Error {
    /// The event provided is not valid in the current state.
    InvalidEvent(State, Event),
    /// The event before a substitution should always be a play, not something else.
    InvalidEventBeforeSub(Event),
    /// The event before a substitution should always be a play.
    NoEventBeforeSub,
    /// A portion of processing is not yet implemented.
    Unimplemented,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::InvalidEvent(ref state, ref event) => {
                write!(f, "the event {:?} is not valid in the {:?} state", event, state)
            },
            Error::InvalidEventBeforeSub(ref event) => {
                write!(f, "substitutions must be preceded by a play event; got {:?}", event)
            },
            Error::NoEventBeforeSub => {
                write!(f, "substitutions must be preceded by a play event")
            },
            Error::Unimplemented => {
                write!(f, "the requested command is not yet implemented")
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
/// A substitution. Denotes what inning and batting team (i.e. bottom or top of inning) and which
/// player was subbed in.
pub struct Substitution {
    inning: u8,
    batting_team: Team,
    player: Player,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// The parsing state of the game.
pub enum State {
    /// Parsing info events.
    Info,
    /// Parsing start events.
    Starters,
    /// Parsing plays.
    Plays,
    /// Parsing end-of-game data.
    Data,
}

#[derive(Clone, Debug, PartialEq)]
/// An MLB game.
pub struct Game {
    /// The game id.
    pub id: String,
    /// Metadata information about the game.
    pub info: HashMap<Info, String>,
    /// The players that started the game.
    pub starters: HashSet<Player>,
    /// The sequence of plays that occurred in the game.
    pub plays: Vec<Event>,
    /// The set of substitutions that happened during the game. This can be used to construct a full
    /// list of who was playing during what parts of the game.
    pub substitutions: Vec<Substitution>,
    state: State,
}

impl Game {
    /// Instantiate a new game object in the initial state.
    pub fn new<S: Into<String>>(id: S) -> Game {
        Game {
            id: id.into(),
            info: HashMap::new(),
            state: State::Info,
            starters: HashSet::new(),
            plays: vec![],
            substitutions: vec![],
        }
    }

    fn process_info_event(&mut self, event: Event) -> Result<(), Error> {
        match event {
            Event::Info { key, data } => {
                self.info.insert(key, data);
                Ok(())
            }
            Event::Start { .. } => {
                // Done with info, so process the event in the starters state.
                self.state = State::Starters;
                self.process_event(event)
            }
            _ => {
                Err(Error::InvalidEvent(self.state, event.clone()))
            }
        }
    }

    fn process_starter_event(&mut self, event: Event) -> Result<(), Error> {
        match event {
            Event::Start { player } => {
                self.starters.insert(player);
                Ok(())
            }
            Event::Play { .. } => {
                // Done with starters, so start play-by-play.
                self.state = State::Plays;
                self.process_event(event)
            }
            _ => {
                Err(Error::InvalidEvent(self.state, event.clone()))
            }
        }
    }

    fn process_play_event(&mut self, event: Event) -> Result<(), Error> {
        match event {
            Event::Play { .. } => {
                self.plays.push(event);
                Ok(())
            },
            Event::Sub { player } => {
                // We actually get rid of the last play, because it will always be a NoPlay without
                // any pitch count. If not, it's an error and parsing failed anyway.
                let last_evt = self.plays.pop();
                match last_evt {
                    Some(event) => {
                        match event {
                            Event::Play { inning, team, .. } => {
                                self.substitutions.push(Substitution {
                                    inning: inning,
                                    batting_team: team,
                                    player: player
                                });
                                Ok(())
                            },
                            _ => Err(Error::InvalidEventBeforeSub(event))
                        }
                    },
                    None => Err(Error::NoEventBeforeSub),
                }
            },
            _ => {
                Err(Error::InvalidEvent(self.state, event.clone()))
            }
        }
    }

    /// Process an event in the context of the game.
    pub fn process_event(&mut self, event: Event) -> Result<(), Error> {
        match self.state {
            State::Info => self.process_info_event(event),
            State::Starters => self.process_starter_event(event),
            State::Plays => self.process_play_event(event),
            _ => Err(Error::Unimplemented),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashSet;
    use std::iter::FromIterator;

    use ::event::{Event, Info, Player, PlayDescription, PlayEvent, Team};

    #[test]
    fn test_process_info() {
        let mut game = Game::new("foo");

        assert_eq!(Ok(()), game.process_event(Event::Info { key: Info::HomeTeam, data: "bar".into() }));
        assert_eq!(Some(&"bar".into()), game.info.get(&Info::HomeTeam));

        let evt = Event::GameId { id: "blah".into() };
        assert_eq!(Err(Error::InvalidEvent(State::Info, evt.clone())), game.process_event(evt.clone()));

        let player = Player {
            id: "fred103".into(),
            name: "fred".into(),
            team: Team::Home,
            batting_pos: 7,
            fielding_pos: 6,
        };

        assert_eq!(Ok(()), game.process_event(Event::Start { player: player }));
        assert_eq!(State::Starters, game.state);
    }

    #[test]
    fn test_process_starters() {
        let mut game = Game::new("foo");
        let player1 = Player {
            id: "fred103".into(),
            name: "fred".into(),
            team: Team::Home,
            batting_pos: 7,
            fielding_pos: 6,
        };
        let player2 = Player {
            id: "jim104".into(),
            name: "jim".into(),
            team: Team::Visiting,
            batting_pos: 3,
            fielding_pos: 1,
        };

        assert_eq!(Ok(()), game.process_event(Event::Start { player: player1.clone() }));
        assert_eq!(Ok(()), game.process_event(Event::Start { player: player2.clone() }));
        assert_eq!(HashSet::from_iter(vec![player1.clone(), player2.clone()].into_iter()), game.starters);

        let evt = Event::Info { key: Info::HomeTeam, data: "bar".into() };
        assert_eq!(Err(Error::InvalidEvent(State::Starters, evt.clone())), game.process_event(evt.clone()));

        assert_eq!(Ok(()), game.process_event(Event::Play {
            inning: 2,
            team: Team::Visiting,
            player: "foo".into(),
            count: None,
            pitches: vec![],
            event: PlayEvent {
                description: PlayDescription::Balk,
                modifiers: vec![],
                advances: vec![],
            }
        }));
        assert_eq!(State::Plays, game.state);
    }
}<|endoftext|>


<commit_before>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct HashThing {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<i32>,  /* Index to hash value at this index. */
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new(window_size: usize) -> HashThing {
        HashThing {
            head: vec![-1; 65536],
            prev: (0..window_size as u16).collect(),
            hashval: vec![-1; window_size],
            val: 0,
        }
    }

    fn reset(&mut self, window_size: usize) {
        self.val = 0;
        self.head = vec![-1; 65536];
        self.prev = (0..window_size as u16).collect();
        self.hashval = vec![-1; window_size];
    }

    fn update(&mut self, hpos: usize) {
        self.hashval[hpos] = self.val;

        let index = self.val as usize;
        if self.head[index] != -1 && self.hashval[self.head[index] as usize] == self.val {
            self.prev[hpos] = self.head[index] as u16;
        } else {
            self.prev[hpos] = hpos as u16;
        }
        self.head[index] = hpos as i32;
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: Vec<u16>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: usize) -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(window_size),
            hash2: HashThing::new(window_size),
            same: vec![0; window_size],
        }
    }

    pub fn reset(&mut self, window_size: usize) {
        self.hash1.reset(window_size);
        self.hash2.reset(window_size);
        self.same = vec![0; window_size];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;

        self.hash1.update(hpos);

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same as i32 - 1;
        }

        let mut another_index = pos + amount as usize + 1;
        let array_pos = array[pos];
        while another_index < array.len() && array_pos == array[another_index] && amount < -1 {
            amount += 1;
            another_index += 1;
        }
        let amount_u16 = amount as u16;
        self.same[hpos] = amount_u16;

        self.hash2.val = ((amount_u16.wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;

        self.hash2.update(hpos);
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev[index],
            Which::Hash2 => self.hash2.prev[index],
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.hashval[index],
            Which::Hash2 => self.hash2.hashval[index],
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<commit_msg>Extract access of head[index] into a variable<commit_after>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct HashThing {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<i32>,  /* Index to hash value at this index. */
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new(window_size: usize) -> HashThing {
        HashThing {
            head: vec![-1; 65536],
            prev: (0..window_size as u16).collect(),
            hashval: vec![-1; window_size],
            val: 0,
        }
    }

    fn reset(&mut self, window_size: usize) {
        self.val = 0;
        self.head = vec![-1; 65536];
        self.prev = (0..window_size as u16).collect();
        self.hashval = vec![-1; window_size];
    }

    fn update(&mut self, hpos: usize) {
        self.hashval[hpos] = self.val;

        let index = self.val as usize;
        let head_index = self.head[index];
        if head_index != -1 && self.hashval[head_index as usize] == self.val {
            self.prev[hpos] = head_index as u16;
        } else {
            self.prev[hpos] = hpos as u16;
        }
        self.head[index] = hpos as i32;
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: Vec<u16>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: usize) -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(window_size),
            hash2: HashThing::new(window_size),
            same: vec![0; window_size],
        }
    }

    pub fn reset(&mut self, window_size: usize) {
        self.hash1.reset(window_size);
        self.hash2.reset(window_size);
        self.same = vec![0; window_size];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;

        self.hash1.update(hpos);

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same as i32 - 1;
        }

        let mut another_index = pos + amount as usize + 1;
        let array_pos = array[pos];
        while another_index < array.len() && array_pos == array[another_index] && amount < -1 {
            amount += 1;
            another_index += 1;
        }
        let amount_u16 = amount as u16;
        self.same[hpos] = amount_u16;

        self.hash2.val = ((amount_u16.wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;

        self.hash2.update(hpos);
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev[index],
            Which::Hash2 => self.hash2.prev[index],
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.hashval[index],
            Which::Hash2 => self.hash2.hashval[index],
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>disable excess debug messages<commit_after><|endoftext|>


<commit_before>// Copyright 2013 Luis de Bethencourt <luis@debethencourt.com>
// Copyright 2013 The Rust Project Developers
// http://rust-lang.org

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
 * Gregorian Calendar module for the datetime library of the Rust programming
 * language
 *
 * Implements a hybrid calendar that supports both the Julian and Gregorian
 * calendar systems.
 *
 * http://en.wikipedia.org/wiki/Gregorian_calendar
 * http://en.wikipedia.org/wiki/Julian_calendar
 */

static YEAR_BASE: int = 1900;
static DAYSPERLYEAR: uint = 366;
static DAYSPERNYEAR: uint = 365;
static DAYSPERWEEK: uint = 7;

pub fn is_leap_year(year: uint) -> bool {
    !(year % 4 != 0) && ((year % 100 != 0) || !(year % 400 != 0))
}

pub fn year_size(year: uint) -> uint {
    if is_leap_year(year) { DAYSPERLYEAR } else { DAYSPERNYEAR }
}

pub struct GCalendar {
    /**
    */
    sec: uint,
    min: uint,
    hour: uint,
    day_of_month: uint,
    month: uint,
    year: uint,
    day_of_week: uint,
    day_of_year: uint
}

impl GCalendar {
    /**
    * Allocates a GCalendar object
    */
    pub fn new() -> GCalendar {
        GCalendar {
            sec: 0,
            min: 0,
            hour: 0,
            day_of_month: 0,
            month: 0,
            year: 0,
            day_of_week: 0,
            day_of_year: 0,
        }
    }

    pub fn new_from_epoch(since_epoch: uint) -> GCalendar {
        let epoch_year = 1970;
        let mut year = epoch_year;

        let millisecs_day = 86400000;

        let mut dayclock = since_epoch % millisecs_day;
        let mut dayno = since_epoch / millisecs_day;

        let hour = dayclock / 3600000;
        dayclock = dayclock - (hour * 3600000);

        let min = dayclock / 60000;
        dayclock = dayclock - (min * 60000);

        let sec = dayclock / 1000;

        while (dayno >= year_size(year)) {
            dayno -= year_size(year);
            year += 1;
        }
        let year = year;

        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            day_of_month: 0,
            month: 0,
            year: year,
            day_of_week: 0,
            day_of_year: dayno,
        }
    }

    pub fn get_sec(&self) -> uint {
        self.sec
    }

    pub fn get_min(&self) -> uint {
        self.min
    }

    pub fn get_hour(&self) -> uint {
        self.hour
    }

    pub fn get_year(&self) -> uint {
        self.year
    }

    pub fn get_day_of_year(&self) -> uint {
        self.day_of_year
    }

    pub fn get_date(&self) -> ~str {
        // ToDo
        ~""
    }
}

#[cfg(test)]
mod test {
    use super::GCalendar;

    #[test]
    fn first_test() {
        let gc = GCalendar::new_from_epoch(433166421023);
        assert_eq!(gc.get_sec(), 21);
        assert_eq!(gc.get_min(), 0);
        assert_eq!(gc.get_hour(), 12);
        assert_eq!(gc.get_year(), 1983);
        assert_eq!(gc.get_day_of_year(), 265);
    }
}
<commit_msg>GCalendar::new()<commit_after>// Copyright 2013 Luis de Bethencourt <luis@debethencourt.com>
// Copyright 2013 The Rust Project Developers
// http://rust-lang.org

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
 * Gregorian Calendar module for the datetime library of the Rust programming
 * language
 *
 * Implements a hybrid calendar that supports both the Julian and Gregorian
 * calendar systems.
 *
 * http://en.wikipedia.org/wiki/Gregorian_calendar
 * http://en.wikipedia.org/wiki/Julian_calendar
 */

static YEAR_BASE: int = 1900;
static DAYSPERLYEAR: uint = 366;
static DAYSPERNYEAR: uint = 365;
static DAYSPERWEEK: uint = 7;

pub fn is_leap_year(year: uint) -> bool {
    !(year % 4 != 0) && ((year % 100 != 0) || !(year % 400 != 0))
}

pub fn year_size(year: uint) -> uint {
    if is_leap_year(year) { DAYSPERLYEAR } else { DAYSPERNYEAR }
}

pub struct GCalendar {
    /**
    */
    sec: uint,
    min: uint,
    hour: uint,
    day_of_month: uint,
    month: uint,
    year: uint,
    day_of_week: uint,
    day_of_year: uint
}

impl GCalendar {
    /**
    * Allocates a GCalendar object
    */
    pub fn new_at_epoch() -> GCalendar {
        GCalendar {
            sec: 0,
            min: 0,
            hour: 0,
            day_of_month: 0,
            month: 0,
            year: 0,
            day_of_week: 0,
            day_of_year: 0,
        }
    }

    pub fn new(sec: uint, min: uint, hour: uint, day_of_month: uint, month: uint,
            year: uint, day_of_week: uint, day_of_year: uint) -> GCalendar {
        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            day_of_month: day_of_month,
            month: month,
            year: year,
            day_of_week: day_of_week,
            day_of_year: day_of_year,
        }
    }

    pub fn new_from_epoch(since_epoch: uint) -> GCalendar {
        let epoch_year = 1970;
        let mut year = epoch_year;

        let millisecs_day = 86400000;

        let mut dayclock = since_epoch % millisecs_day;
        let mut dayno = since_epoch / millisecs_day;

        let hour = dayclock / 3600000;
        dayclock = dayclock - (hour * 3600000);

        let min = dayclock / 60000;
        dayclock = dayclock - (min * 60000);

        let sec = dayclock / 1000;

        while (dayno >= year_size(year)) {
            dayno -= year_size(year);
            year += 1;
        }
        let year = year;

        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            day_of_month: 0,
            month: 0,
            year: year,
            day_of_week: 0,
            day_of_year: dayno,
        }
    }

    pub fn get_sec(&self) -> uint {
        self.sec
    }

    pub fn get_min(&self) -> uint {
        self.min
    }

    pub fn get_hour(&self) -> uint {
        self.hour
    }

    pub fn get_year(&self) -> uint {
        self.year
    }

    pub fn get_day_of_year(&self) -> uint {
        self.day_of_year
    }

    pub fn get_date(&self) -> ~str {
        // ToDo
        ~""
    }
}

#[cfg(test)]
mod test {
    use super::GCalendar;

    #[test]
    fn new() {
        let gc = GCalendar::new(21, 0, 12, 23, 9, 1983, 5, 265);
        assert_eq!(gc.get_sec(), 21);
        assert_eq!(gc.get_min(), 0);
        assert_eq!(gc.get_hour(), 12);
        assert_eq!(gc.get_year(), 1983);
        assert_eq!(gc.get_day_of_year(), 265);
    }

    fn new_from_epoch() {
        let gc = GCalendar::new_from_epoch(433166421023);
        assert_eq!(gc.get_sec(), 21);
        assert_eq!(gc.get_min(), 0);
        assert_eq!(gc.get_hour(), 12);
        assert_eq!(gc.get_year(), 1983);
        assert_eq!(gc.get_day_of_year(), 265);
    }
}
<|endoftext|>


<commit_before>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

impl Copy for GeoIp { }

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(file.to_c_str().into_inner(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn.slice_from(2).parse::<uint>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<commit_msg>uint -> ut32<commit_after>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

impl Copy for GeoIp { }

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as ui32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(file.to_c_str().into_inner(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn.slice_from(2).parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<|endoftext|>