<commit_before>//! The `print-cfg` sub-command.
//!
//! Read a series of Cretonne IL files and print their control flow graphs
//! in graphviz format.
use std::io::{Write, stdout};

use CommandResult;
use utils::read_to_string;
use cretonne::ir::Function;
use cretonne::cfg::ControlFlowGraph;
use cretonne::ir::instructions::InstructionData;
use cton_reader::parse_functions;

pub fn run(files: Vec<String>) -> CommandResult {
    for (i, f) in files.into_iter().enumerate() {
        if i != 0 {
            println!("");
        }
        try!(print_cfg(f))
    }
    Ok(())
}

struct CFGPrinter<T: Write> {
    level: usize,
    writer: T,
    buffer: String,
}

impl<T: Write> CFGPrinter<T> {
    pub fn new(writer: T) -> CFGPrinter<T> {
        CFGPrinter {
            level: 0,
            writer: writer,
            buffer: String::new(),
        }
    }

    pub fn print(&mut self, func: &Function) -> Result<(), String> {
        self.level = 0;
        self.header(func);
        self.push_indent();
        self.ebb_subgraphs(func);
        let cfg = ControlFlowGraph::new(func);
        self.cfg_connections(func, &cfg);
        self.pop_indent();
        self.footer();
        self.write()
    }

    fn write(&mut self) -> Result<(), String> {
        match self.writer.write(self.buffer.as_bytes()) {
            Err(_) => return Err("Write failed!".to_string()),
            _ => (),
        };
        match self.writer.flush() {
            Err(_) => return Err("Flush failed!".to_string()),
            _ => (),
        };
        Ok(())
    }

    fn append(&mut self, s: &str) {
        let mut indent = String::new();
        for _ in 0..self.level {
            indent = indent + "    ";
        }
        self.buffer.push_str(&(indent + s));
    }

    fn push_indent(&mut self) {
        self.level += 1;
    }

    fn pop_indent(&mut self) {
        if self.level > 0 {
            self.level -= 1;
        }
    }

    fn open_paren(&mut self) {
        self.append("{");
    }

    fn close_paren(&mut self) {
        self.append("}");
    }

    fn newline(&mut self) {
        self.append("\n");
    }

    fn header(&mut self, func: &Function) {
        self.append(&format!("digraph {} ", func.name));
        self.open_paren();
        self.newline();
        self.push_indent();
        self.append("{rank=min; ebb0}");
        self.pop_indent();
        self.newline();
    }

    fn footer(&mut self) {
        self.close_paren();
        self.newline();
    }

    fn ebb_subgraphs(&mut self, func: &Function) {
        for ebb in &func.layout {
            let inst_data = func.layout
                .ebb_insts(ebb)
                .filter(|inst| {
                    match func.dfg[*inst] {
                        InstructionData::Branch { ty: _, opcode: _, data: _ } => true,
                        InstructionData::Jump { ty: _, opcode: _, data: _ } => true,
                        _ => false,
                    }
                })
                .map(|inst| {
                    let op = match func.dfg[inst] {
                        InstructionData::Branch { ty: _, opcode, ref data } => {
                            Some((opcode, data.destination))
                        }
                        InstructionData::Jump { ty: _, opcode, ref data } => {
                            Some((opcode, data.destination))
                        }
                        _ => None,
                    };
                    (inst, op)
                })
                .collect::<Vec<_>>();

            let mut insts = vec![format!("{}", ebb)];
            for (inst, data) in inst_data {
                let (op, dest) = data.unwrap();
                insts.push(format!("<{}>{} {}", inst, op, dest));
            }

            self.append(&format!("{} [shape=record, label=\"{}{}{}\"]",
                                 ebb,
                                 "{",
                                 insts.join(" | "),
                                 "}"));
            self.newline();
        }
    }

    fn cfg_connections(&mut self, func: &Function, cfg: &ControlFlowGraph) {
        for ebb in &func.layout {
            for &(parent, inst) in cfg.get_predecessors(ebb) {
                self.append(&format!("{}:{} -> {}", parent, inst, ebb));
                self.newline();
            }
        }
    }
}

fn print_cfg(filename: String) -> CommandResult {
    let buffer = try!(read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e)));
    let items = try!(parse_functions(&buffer).map_err(|e| format!("{}: {}", filename, e)));

    let mut cfg_printer = CFGPrinter::new(stdout());
    for (idx, func) in items.into_iter().enumerate() {
        if idx != 0 {
            println!("");
        }

        try!(cfg_printer.print(&func));
    }

    Ok(())
}
<commit_msg>Also use fmt::Write for the print-cfg command.<commit_after>//! The `print-cfg` sub-command.
//!
//! Read a series of Cretonne IL files and print their control flow graphs
//! in graphviz format.
use std::fmt::{Result, Write, Display, Formatter};

use CommandResult;
use utils::read_to_string;
use cretonne::ir::Function;
use cretonne::cfg::ControlFlowGraph;
use cretonne::ir::instructions::BranchInfo;
use cton_reader::parse_functions;

pub fn run(files: Vec<String>) -> CommandResult {
    for (i, f) in files.into_iter().enumerate() {
        if i != 0 {
            println!("");
        }
        try!(print_cfg(f))
    }
    Ok(())
}

struct CFGPrinter<'a> {
    func: &'a Function,
    cfg: ControlFlowGraph,
}

impl<'a> CFGPrinter<'a> {
    pub fn new(func: &'a Function) -> CFGPrinter<'a> {
        CFGPrinter {
            func: func,
            cfg: ControlFlowGraph::new(func),
        }
    }

    /// Write the CFG for this function to `w`.
    pub fn write(&self, w: &mut Write) -> Result {
        try!(self.header(w));
        try!(self.ebb_nodes(w));
        try!(self.cfg_connections(w));
        writeln!(w, "}}")
    }

    fn header(&self, w: &mut Write) -> Result {
        try!(writeln!(w, "digraph {} {{", self.func.name));
        if let Some(entry) = self.func.layout.entry_block() {
            try!(writeln!(w, "    {{rank=min; {}}}", entry));
        }
        Ok(())
    }

    fn ebb_nodes(&self, w: &mut Write) -> Result {
        for ebb in &self.func.layout {
            try!(write!(w, "    {} [shape=record, label=\"{{{}", ebb, ebb));
            // Add all outgoing branch instructions to the label.
            for inst in self.func.layout.ebb_insts(ebb) {
                let idata = &self.func.dfg[inst];
                match idata.analyze_branch() {
                    BranchInfo::SingleDest(dest, _) => {
                        try!(write!(w, " | <{}>{} {}", inst, idata.opcode(), dest))
                    }
                    BranchInfo::Table(table) => {
                        try!(write!(w, " | <{}>{} {}", inst, idata.opcode(), table))
                    }
                    BranchInfo::NotABranch => {}
                }
            }
            try!(writeln!(w, "}}\"]"))
        }
        Ok(())
    }

    fn cfg_connections(&self, w: &mut Write) -> Result {
        for ebb in &self.func.layout {
            for &(parent, inst) in self.cfg.get_predecessors(ebb) {
                try!(writeln!(w, "    {}:{} -> {}", parent, inst, ebb));
            }
        }
        Ok(())
    }
}

impl<'a> Display for CFGPrinter<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        self.write(f)
    }
}

fn print_cfg(filename: String) -> CommandResult {
    let buffer = try!(read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e)));
    let items = try!(parse_functions(&buffer).map_err(|e| format!("{}: {}", filename, e)));

    for (idx, func) in items.into_iter().enumerate() {
        if idx != 0 {
            println!("");
        }
        print!("{}", CFGPrinter::new(&func));
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::fs::{OpenOptions, File};
use crc::crc32::checksum_ieee;
use memmap::{Mmap, Protection};
use byteorder::{BigEndian, ByteOrder};
use std::io::{self, Write};
use std;

// TODO: ...
// pub type Offset(u64);

/// Messages are appended to the log with the following encoding:
///
/// +-----------+--------------+
/// | Bytes     | Value        |
/// +-----------+--------------+
/// | 0-7       | Offset       |
/// | 8-11      | Payload Size |
/// | 12-15     | CRC32 (IEEE) |
/// | 16+       | Payload      |
/// +-----------+--------------+
pub struct Message {
    bytes: Vec<u8>,
}

impl Message {
    pub fn new(payload: &[u8], offset: u64) -> Message {
        let mut bytes = vec![0; 16 + payload.len()];
        BigEndian::write_u64(&mut bytes[0..8], offset);
        BigEndian::write_u32(&mut bytes[8..12], payload.len() as u32);
        BigEndian::write_u32(&mut bytes[12..16], checksum_ieee(&payload));
        bytes[16..].copy_from_slice(payload);
        Message { bytes: bytes }
    }

    #[inline]
    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    #[inline]
    pub fn crc(&self) -> u32 {
        BigEndian::read_u32(&self.bytes[12..16])
    }

    #[inline]
    pub fn size(&self) -> u32 {
        BigEndian::read_u32(&self.bytes[8..12])
    }

    #[inline]
    pub fn offset(&self) -> u64 {
        BigEndian::read_u64(&self.bytes[0..8])
    }

    #[inline]
    pub fn payload(&self) -> &[u8] {
        &self.bytes[16..]
    }
}

/// An index is a file with pairs of relative offset to file position offset
/// messages. Both are stored as 4 bytes.
pub struct Index {
    file: File,
    mmap: Mmap,

    /// next starting byte in index file offset to write
    next_write_offset: usize,
    base_offset: u64,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct IndexEntry {
    rel_offset: u32,
    base_offset: u64,
    file_pos: u32,
}

impl IndexEntry {
    pub fn relative_offset(&self) -> u32 {
        self.rel_offset
    }

    #[inline]
    pub fn offset(&self) -> u64 {
        self.base_offset + (self.rel_offset as u64)
    }

    #[inline]
    pub fn file_position(&self) -> u32 {
        self.file_pos
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum IndexWriteError {
    IndexFull,
    OffsetLessThanBase,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum IndexReadError {
    OutOfBounds,
}

impl Index {
    pub fn new<P>(path: P, file_bytes: u64, base_offset: u64) -> io::Result<Index>
        where P: AsRef<Path>
    {
        // open the file, expecting to create it
        let index_file = try!(OpenOptions::new()
            .read(true)
            .write(true)
            .append(true)
            .create(true)
            .open(path));

        // read the metadata and truncate
        let meta = try!(index_file.metadata());
        let len = meta.len();
        if len == 0 {
            try!(index_file.set_len(file_bytes));
        }

        let mmap = try!(Mmap::open(&index_file, Protection::ReadWrite));

        Ok(Index {
            file: index_file,
            mmap: mmap,
            next_write_offset: len as usize,
            base_offset: base_offset,
        })
    }

    #[inline]
    pub fn can_write(&self) -> bool {
        self.len() >= (self.next_write_offset + 8)
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.mmap.len()
    }

    pub fn append(&mut self, abs_offset: u64, position: u32) -> Result<(), IndexWriteError> {
        // TODO: test to ensure abs_offset is < last offset written (invariant of the index)
        if !self.can_write() {
            return Err(IndexWriteError::IndexFull);
        }

        if abs_offset < self.base_offset {
            return Err(IndexWriteError::OffsetLessThanBase);
        }

        unsafe {
            let mem_slice: &mut [u8] = self.mmap.as_mut_slice();
            let offset = (abs_offset - self.base_offset) as u32;
            let buf_pos = self.next_write_offset;

            BigEndian::write_u32(&mut mem_slice[buf_pos..buf_pos + 4], offset);
            BigEndian::write_u32(&mut mem_slice[buf_pos + 4..buf_pos + 8], position);

            self.next_write_offset += 8;

            Ok(())
        }
    }

    pub fn flush_sync(&mut self) -> io::Result<()> {
        try!(self.mmap.flush());
        self.file.flush()
    }

    pub fn read_entry(&self, i: usize) -> Result<Option<IndexEntry>, IndexReadError> {
        if self.len() < (i + 1) * 8 {
            return Err(IndexReadError::OutOfBounds);
        }

        unsafe {
            let mem_slice = self.mmap.as_slice();
            let start = i * 8;
            let offset = BigEndian::read_u32(&mem_slice[start..start + 4]);
            if offset == 0 && i > 0 {
                Ok(None)
            } else {
                let pos = BigEndian::read_u32(&mem_slice[start + 4..start + 8]);
                Ok(Some(IndexEntry {
                    rel_offset: offset,
                    base_offset: self.base_offset,
                    file_pos: pos,
                }))
            }
        }
    }
}

pub struct Log {
    f: File,
    max_bytes: usize,
    pos: usize,
}

#[derive(Debug)]
pub enum LogAppendError {
    LogFull,
    IoError(io::Error),
}

impl From<std::io::Error> for LogAppendError {
    #[inline]
    fn from(e: std::io::Error) -> LogAppendError {
        LogAppendError::IoError(e)
    }
}

impl Log {
    pub fn new<P>(p: P, max_bytes: usize) -> io::Result<Log>
        where P: AsRef<Path>
    {
        let f = try!(OpenOptions::new()
            .write(true)
            .read(true)
            .create(true)
            .append(true)
            .open(p));
        Ok(Log {
            f: f,
            max_bytes: max_bytes,
            pos: 0,
        })
    }

    pub fn can_write(&self) -> bool {
        self.pos < self.max_bytes
    }

    pub fn append(&mut self, msg: Message) -> Result<u32, LogAppendError> {
        // ensure we won't go over the max
        if msg.bytes().len() + self.pos > self.max_bytes {
            return Err(LogAppendError::LogFull);
        }

        try!(self.f.write_all(msg.bytes()));
        let write_pos = self.pos;
        self.pos += msg.bytes().len();
        Ok(write_pos as u32)
    }

    pub fn flush(&mut self) -> io::Result<()> {
        self.f.flush()
    }
}

pub struct Segment {
    log: Log,
    index: Index,
    next_offset: u64,
}

#[derive(Debug)]
pub enum SegmentAppendError {
    LogFull,
    IndexError(IndexWriteError),
    IoError(io::Error),
}

impl From<io::Error> for SegmentAppendError {
    #[inline]
    fn from(e: io::Error) -> SegmentAppendError {
        SegmentAppendError::IoError(e)
    }
}

impl From<LogAppendError> for SegmentAppendError {
    #[inline]
    fn from(e: LogAppendError) -> SegmentAppendError {
        match e {
            LogAppendError::LogFull => SegmentAppendError::LogFull,
            LogAppendError::IoError(e) => SegmentAppendError::IoError(e),
        }
    }
}

impl From<IndexWriteError> for SegmentAppendError {
    #[inline]
    fn from(e: IndexWriteError) -> SegmentAppendError {
        SegmentAppendError::IndexError(e)
    }
}

impl Segment {
    // TODO: open variant for reading

    pub fn new<P>(log_dir: P,
                  base_offset: u64,
                  max_bytes: usize,
                  index_bytes: usize)
                  -> io::Result<Segment>
        where P: AsRef<Path>
    {
        let log = {
            let mut path_buf = PathBuf::new();
            path_buf.push(&log_dir);
            path_buf.push(format!("{:020}", base_offset));
            path_buf.set_extension("log");
            try!(Log::new(&path_buf, max_bytes))
        };

        let index = {
            let mut path_buf = PathBuf::new();
            path_buf.push(&log_dir);
            path_buf.push(format!("{:020}", base_offset));
            path_buf.set_extension("index");
            try!(Index::new(&path_buf, index_bytes as u64, base_offset))
        };

        Ok(Segment {
            log: log,
            index: index,
            next_offset: base_offset,
        })
    }

    #[inline]
    pub fn next_offset(&self) -> u64 {
        self.next_offset
    }

    pub fn append(&mut self, msg: &[u8]) -> Result<u64, SegmentAppendError> {
        if !self.index.can_write() || !self.log.can_write() {
            return Err(SegmentAppendError::LogFull);
        }

        let off = self.next_offset;
        let pos = try!(self.log.append(Message::new(msg, off)));
        try!(self.index.append(off, pos));

        self.next_offset += 1;
        Ok(off)
    }

    // TODO: async flush strategy
    pub fn flush_sync(&mut self) -> io::Result<()> {
        try!(self.index.flush_sync());
        self.log.flush()
    }
}

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

    #[test]
    fn message_construction() {
        let msg = Message::new(b"123456789", 1234567u64);
        assert_eq!(msg.offset(), 1234567u64);
        assert_eq!(msg.payload(), b"123456789");
        assert_eq!(msg.crc(), 0xcbf43926);
        assert_eq!(msg.size(), 9u32);
    }

    #[test]
    fn index() {
        let index_path = "target/.test-index";

        // remove before running (may be around from failing test run)
        fs::remove_file(index_path).unwrap_or(());

        {
            let mut index = Index::new(index_path, 1000u64, 10u64).unwrap();
            assert_eq!(1000, index.len());
            index.append(11u64, 0xffff).unwrap();
            index.append(12u64, 0xeeee).unwrap();
            index.flush_sync().unwrap();
        }

        // reopen it for read
        {
            let index = Index::new(index_path, 1000u64, 10u64).unwrap();

            let e0 = index.read_entry(0).unwrap().unwrap();
            assert_eq!(1u32, e0.relative_offset());
            assert_eq!(11u64, e0.offset());
            assert_eq!(0xffff, e0.file_position());

            let e1 = index.read_entry(1).unwrap().unwrap();
            assert_eq!(2u32, e1.relative_offset());
            assert_eq!(12u64, e1.offset());
            assert_eq!(0xeeee, e1.file_position());

            // read an entry that does not exist
            let e2 = index.read_entry(2).unwrap();
            assert_eq!(None, e2);
        }
        fs::remove_file(index_path).unwrap();
    }

    #[test]
    pub fn log() {
        let log_path = "target/.test-log";
        fs::remove_file(log_path).unwrap_or(());
        {
            let mut f = Log::new(log_path, 1024).unwrap();

            let m0 = Message::new(b"12345", 1000);
            assert_eq!(m0.bytes().len(), 21);
            let p0 = f.append(m0).unwrap();
            assert_eq!(p0, 0);

            let m1 = Message::new(b"66666", 1001);
            let p1 = f.append(m1).unwrap();
            assert_eq!(p1, 21);

            f.flush().unwrap();
        }
        fs::remove_file(log_path).unwrap();
    }

    #[bench]
    fn bench_segment_append(b: &mut Bencher) {
        let log_path = "target/test-log";
        fs::remove_dir_all(log_path).unwrap_or(());
        fs::create_dir_all(log_path).unwrap_or(());

        let mut seg = Segment::new(log_path, 100u64, 100 * 1024 * 1024, 10 * 1024 * 1024).unwrap();
        let buf = b"01234567891011121314151617181920";

        b.iter(|| {
            seg.append(buf).unwrap();
        });

        fs::remove_dir_all(log_path).unwrap_or(());
    }
}
<commit_msg>Clippy<commit_after>use std::path::{Path, PathBuf};
use std::fs::{OpenOptions, File};
use crc::crc32::checksum_ieee;
use memmap::{Mmap, Protection};
use byteorder::{BigEndian, ByteOrder};
use std::io::{self, Write};
use std;

// TODO: ...
// pub type Offset(u64);

/// Messages are appended to the log with the following encoding:
///
/// +-----------+--------------+
/// | Bytes     | Value        |
/// +-----------+--------------+
/// | 0-7       | Offset       |
/// | 8-11      | Payload Size |
/// | 12-15     | CRC32 (IEEE) |
/// | 16+       | Payload      |
/// +-----------+--------------+
pub struct Message {
    bytes: Vec<u8>,
}

impl Message {
    pub fn new(payload: &[u8], offset: u64) -> Message {
        let mut bytes = vec![0; 16 + payload.len()];
        BigEndian::write_u64(&mut bytes[0..8], offset);
        BigEndian::write_u32(&mut bytes[8..12], payload.len() as u32);
        BigEndian::write_u32(&mut bytes[12..16], checksum_ieee(payload));
        bytes[16..].copy_from_slice(payload);
        Message { bytes: bytes }
    }

    #[inline]
    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    #[inline]
    pub fn crc(&self) -> u32 {
        BigEndian::read_u32(&self.bytes[12..16])
    }

    #[inline]
    pub fn size(&self) -> u32 {
        BigEndian::read_u32(&self.bytes[8..12])
    }

    #[inline]
    pub fn offset(&self) -> u64 {
        BigEndian::read_u64(&self.bytes[0..8])
    }

    #[inline]
    pub fn payload(&self) -> &[u8] {
        &self.bytes[16..]
    }
}

/// An index is a file with pairs of relative offset to file position offset
/// messages. Both are stored as 4 bytes.
pub struct Index {
    file: File,
    mmap: Mmap,

    /// next starting byte in index file offset to write
    next_write_offset: usize,
    base_offset: u64,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct IndexEntry {
    rel_offset: u32,
    base_offset: u64,
    file_pos: u32,
}

impl IndexEntry {
    pub fn relative_offset(&self) -> u32 {
        self.rel_offset
    }

    #[inline]
    pub fn offset(&self) -> u64 {
        self.base_offset + (self.rel_offset as u64)
    }

    #[inline]
    pub fn file_position(&self) -> u32 {
        self.file_pos
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum IndexWriteError {
    IndexFull,
    OffsetLessThanBase,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum IndexReadError {
    OutOfBounds,
}

impl Index {
    pub fn new<P>(path: P, file_bytes: u64, base_offset: u64) -> io::Result<Index>
        where P: AsRef<Path>
    {
        // open the file, expecting to create it
        let index_file = try!(OpenOptions::new()
            .read(true)
            .write(true)
            .append(true)
            .create(true)
            .open(path));

        // read the metadata and truncate
        let meta = try!(index_file.metadata());
        let len = meta.len();
        if len == 0 {
            try!(index_file.set_len(file_bytes));
        }

        let mmap = try!(Mmap::open(&index_file, Protection::ReadWrite));

        Ok(Index {
            file: index_file,
            mmap: mmap,
            next_write_offset: len as usize,
            base_offset: base_offset,
        })
    }

    #[inline]
    pub fn can_write(&self) -> bool {
        self.size() >= (self.next_write_offset + 8)
    }

    #[inline]
    pub fn size(&self) -> usize {
        self.mmap.len()
    }

    pub fn append(&mut self, abs_offset: u64, position: u32) -> Result<(), IndexWriteError> {
        // TODO: test to ensure abs_offset is < last offset written (invariant of the index)
        if !self.can_write() {
            return Err(IndexWriteError::IndexFull);
        }

        if abs_offset < self.base_offset {
            return Err(IndexWriteError::OffsetLessThanBase);
        }

        unsafe {
            let mem_slice: &mut [u8] = self.mmap.as_mut_slice();
            let offset = (abs_offset - self.base_offset) as u32;
            let buf_pos = self.next_write_offset;

            BigEndian::write_u32(&mut mem_slice[buf_pos..buf_pos + 4], offset);
            BigEndian::write_u32(&mut mem_slice[buf_pos + 4..buf_pos + 8], position);

            self.next_write_offset += 8;

            Ok(())
        }
    }

    pub fn flush_sync(&mut self) -> io::Result<()> {
        try!(self.mmap.flush());
        self.file.flush()
    }

    pub fn read_entry(&self, i: usize) -> Result<Option<IndexEntry>, IndexReadError> {
        if self.size() < (i + 1) * 8 {
            return Err(IndexReadError::OutOfBounds);
        }

        unsafe {
            let mem_slice = self.mmap.as_slice();
            let start = i * 8;
            let offset = BigEndian::read_u32(&mem_slice[start..start + 4]);
            if offset == 0 && i > 0 {
                Ok(None)
            } else {
                let pos = BigEndian::read_u32(&mem_slice[start + 4..start + 8]);
                Ok(Some(IndexEntry {
                    rel_offset: offset,
                    base_offset: self.base_offset,
                    file_pos: pos,
                }))
            }
        }
    }
}

pub struct Log {
    f: File,
    max_bytes: usize,
    pos: usize,
}

#[derive(Debug)]
pub enum LogAppendError {
    LogFull,
    IoError(io::Error),
}

impl From<std::io::Error> for LogAppendError {
    #[inline]
    fn from(e: std::io::Error) -> LogAppendError {
        LogAppendError::IoError(e)
    }
}

impl Log {
    pub fn new<P>(p: P, max_bytes: usize) -> io::Result<Log>
        where P: AsRef<Path>
    {
        let f = try!(OpenOptions::new()
            .write(true)
            .read(true)
            .create(true)
            .append(true)
            .open(p));
        Ok(Log {
            f: f,
            max_bytes: max_bytes,
            pos: 0,
        })
    }

    pub fn can_write(&self) -> bool {
        self.pos < self.max_bytes
    }

    pub fn append(&mut self, msg: Message) -> Result<u32, LogAppendError> {
        // ensure we won't go over the max
        if msg.bytes().len() + self.pos > self.max_bytes {
            return Err(LogAppendError::LogFull);
        }

        try!(self.f.write_all(msg.bytes()));
        let write_pos = self.pos;
        self.pos += msg.bytes().len();
        Ok(write_pos as u32)
    }

    pub fn flush(&mut self) -> io::Result<()> {
        self.f.flush()
    }
}

pub struct Segment {
    log: Log,
    index: Index,
    next_offset: u64,
}

#[derive(Debug)]
pub enum SegmentAppendError {
    LogFull,
    IndexError(IndexWriteError),
    IoError(io::Error),
}

impl From<io::Error> for SegmentAppendError {
    #[inline]
    fn from(e: io::Error) -> SegmentAppendError {
        SegmentAppendError::IoError(e)
    }
}

impl From<LogAppendError> for SegmentAppendError {
    #[inline]
    fn from(e: LogAppendError) -> SegmentAppendError {
        match e {
            LogAppendError::LogFull => SegmentAppendError::LogFull,
            LogAppendError::IoError(e) => SegmentAppendError::IoError(e),
        }
    }
}

impl From<IndexWriteError> for SegmentAppendError {
    #[inline]
    fn from(e: IndexWriteError) -> SegmentAppendError {
        SegmentAppendError::IndexError(e)
    }
}

impl Segment {
    // TODO: open variant for reading

    pub fn new<P>(log_dir: P,
                  base_offset: u64,
                  max_bytes: usize,
                  index_bytes: usize)
                  -> io::Result<Segment>
        where P: AsRef<Path>
    {
        let log = {
            let mut path_buf = PathBuf::new();
            path_buf.push(&log_dir);
            path_buf.push(format!("{:020}", base_offset));
            path_buf.set_extension("log");
            try!(Log::new(&path_buf, max_bytes))
        };

        let index = {
            let mut path_buf = PathBuf::new();
            path_buf.push(&log_dir);
            path_buf.push(format!("{:020}", base_offset));
            path_buf.set_extension("index");
            try!(Index::new(&path_buf, index_bytes as u64, base_offset))
        };

        Ok(Segment {
            log: log,
            index: index,
            next_offset: base_offset,
        })
    }

    #[inline]
    pub fn next_offset(&self) -> u64 {
        self.next_offset
    }

    pub fn append(&mut self, msg: &[u8]) -> Result<u64, SegmentAppendError> {
        if !self.index.can_write() || !self.log.can_write() {
            return Err(SegmentAppendError::LogFull);
        }

        let off = self.next_offset;
        let pos = try!(self.log.append(Message::new(msg, off)));
        try!(self.index.append(off, pos));

        self.next_offset += 1;
        Ok(off)
    }

    // TODO: async flush strategy
    pub fn flush_sync(&mut self) -> io::Result<()> {
        try!(self.index.flush_sync());
        self.log.flush()
    }
}

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

    #[test]
    fn message_construction() {
        let msg = Message::new(b"123456789", 1234567u64);
        assert_eq!(msg.offset(), 1234567u64);
        assert_eq!(msg.payload(), b"123456789");
        assert_eq!(msg.crc(), 0xcbf43926);
        assert_eq!(msg.size(), 9u32);
    }

    #[test]
    fn index() {
        let index_path = "target/.test-index";

        // remove before running (may be around from failing test run)
        fs::remove_file(index_path).unwrap_or(());

        {
            let mut index = Index::new(index_path, 1000u64, 10u64).unwrap();
            assert_eq!(1000, index.size());
            index.append(11u64, 0xffff).unwrap();
            index.append(12u64, 0xeeee).unwrap();
            index.flush_sync().unwrap();
        }

        // reopen it for read
        {
            let index = Index::new(index_path, 1000u64, 10u64).unwrap();

            let e0 = index.read_entry(0).unwrap().unwrap();
            assert_eq!(1u32, e0.relative_offset());
            assert_eq!(11u64, e0.offset());
            assert_eq!(0xffff, e0.file_position());

            let e1 = index.read_entry(1).unwrap().unwrap();
            assert_eq!(2u32, e1.relative_offset());
            assert_eq!(12u64, e1.offset());
            assert_eq!(0xeeee, e1.file_position());

            // read an entry that does not exist
            let e2 = index.read_entry(2).unwrap();
            assert_eq!(None, e2);
        }
        fs::remove_file(index_path).unwrap();
    }

    #[test]
    pub fn log() {
        let log_path = "target/.test-log";
        fs::remove_file(log_path).unwrap_or(());
        {
            let mut f = Log::new(log_path, 1024).unwrap();

            let m0 = Message::new(b"12345", 1000);
            assert_eq!(m0.bytes().len(), 21);
            let p0 = f.append(m0).unwrap();
            assert_eq!(p0, 0);

            let m1 = Message::new(b"66666", 1001);
            let p1 = f.append(m1).unwrap();
            assert_eq!(p1, 21);

            f.flush().unwrap();
        }
        fs::remove_file(log_path).unwrap();
    }

    #[bench]
    fn bench_segment_append(b: &mut Bencher) {
        let log_path = "target/test-log";
        fs::remove_dir_all(log_path).unwrap_or(());
        fs::create_dir_all(log_path).unwrap_or(());

        let mut seg = Segment::new(log_path, 100u64, 100 * 1024 * 1024, 10 * 1024 * 1024).unwrap();
        let buf = b"01234567891011121314151617181920";

        b.iter(|| {
            seg.append(buf).unwrap();
        });

        fs::remove_dir_all(log_path).unwrap_or(());
    }
}
<|endoftext|>


<commit_before>//! The [name (Naming)](https://docs.microsoft.com/en-us/typography/opentype/spec/name) table

use crate::FontData;

/// 'name'
pub const TAG: Tag = Tag::new(b"name");

include!("../../generated/generated_name.rs");

impl<'a> Name<'a> {
    /// The FontData containing the encoded name strings.
    pub fn string_data(&self) -> FontData<'a> {
        let base = self.offset_data();
        let off = self.storage_offset();
        base.split_off(off as usize).unwrap_or_default()
    }
}

impl NameRecord {
    /// Return a type that can decode the string data for this name entry.
    pub fn string<'a>(&self, data: FontData<'a>) -> Result<NameString<'a>, ReadError> {
        let start = self.string_offset().non_null().unwrap_or(0);
        let end = start + self.length() as usize;

        let data = data
            .as_bytes()
            .get(start..end)
            .ok_or(ReadError::OutOfBounds)?;

        let encoding = Encoding::new(self.platform_id(), self.encoding_id());
        Ok(NameString { data, encoding })
    }
}

impl LangTagRecord {
    /// Return a type that can decode the string data for this name entry.
    pub fn lang_tag<'a>(&self, data: FontData<'a>) -> Result<NameString<'a>, ReadError> {
        let start = self.lang_tag_offset().non_null().unwrap_or(0);
        let end = start + self.length() as usize;

        let data = data
            .as_bytes()
            .get(start..end)
            .ok_or(ReadError::OutOfBounds)?;

        let encoding = Encoding::Utf16Be;
        Ok(NameString { data, encoding })
    }
}

//-- all this is from pinot https://github.com/dfrg/pinot/blob/eff5239018ca50290fb890a84da3dd51505da364/src/name.rs
/// Entry for a name in the naming table.
///
/// This provides an iterator over characters.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct NameString<'a> {
    data: &'a [u8],
    encoding: Encoding,
}

impl<'a> NameString<'a> {
    /// An iterator over the `char`s in this name.
    pub fn chars(&self) -> CharIter<'a> {
        CharIter {
            data: self.data,
            encoding: self.encoding,
            pos: 0,
        }
    }
}

#[cfg(feature = "traversal")]
impl<'a> traversal::SomeString<'a> for NameString<'a> {
    fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a> {
        Box::new(self.into_iter())
    }
}

#[cfg(feature = "traversal")]
impl NameRecord {
    fn traverse_string<'a>(&self, data: FontData<'a>) -> traversal::FieldType<'a> {
        FieldType::StringOffset(traversal::StringOffset {
            offset: self.string_offset().into(),
            target: self.string(data).map(|s| Box::new(s) as _),
        })
    }
}

#[cfg(feature = "traversal")]
impl LangTagRecord {
    fn traverse_lang_tag<'a>(&self, data: FontData<'a>) -> traversal::FieldType<'a> {
        FieldType::StringOffset(traversal::StringOffset {
            offset: self.lang_tag_offset().into(),
            target: self.lang_tag(data).map(|s| Box::new(s) as _),
        })
    }
}

impl<'a> IntoIterator for NameString<'a> {
    type Item = char;
    type IntoIter = CharIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.chars()
    }
}

impl<'a> std::fmt::Display for NameString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        for c in self.chars() {
            c.fmt(f)?;
        }
        Ok(())
    }
}

impl<'a> std::fmt::Debug for NameString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "\"{self}\"")
    }
}

/// An iterator over the chars of a name record.
pub struct CharIter<'a> {
    data: &'a [u8],
    encoding: Encoding,
    pos: usize,
}

impl CharIter<'_> {
    fn bump_u16(&mut self) -> Option<u16> {
        let result = self
            .data
            .get(self.pos..self.pos + 2)
            .map(|x| u16::from_be_bytes(x.try_into().unwrap()))?;
        self.pos += 2;
        Some(result)
    }

    fn bump_u8(&mut self) -> Option<u8> {
        let result = self.data.get(self.pos)?;
        self.pos += 1;
        Some(*result)
    }
}

impl<'a> Iterator for CharIter<'a> {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.data.len() {
            return None;
        }
        let rep = core::char::REPLACEMENT_CHARACTER;
        let raw_c = match self.encoding {
            Encoding::Utf16Be => {
                let c1 = self.bump_u16()? as u32;
                if (0xD800..0xDC00).contains(&c1) {
                    let c2 = self.bump_u16()? as u32;
                    ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
                } else {
                    c1
                }
            }
            Encoding::MacRoman => {
                let c = self.bump_u8()?;
                MacRomanMapping.decode(c) as u32
            }
            _ => return None,
        };
        Some(std::char::from_u32(raw_c).unwrap_or(rep))
    }
}

/// The encoding used by the name table.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Encoding {
    Utf16Be,
    MacRoman,
    Unknown,
}

impl Encoding {
    /// Determine the coding from the platform and encoding id.
    pub fn new(platform_id: u16, encoding_id: u16) -> Encoding {
        match (platform_id, encoding_id) {
            (0, _) => Encoding::Utf16Be,
            (1, 0) => Encoding::MacRoman,
            (3, 0) => Encoding::Utf16Be,
            (3, 1) => Encoding::Utf16Be,
            (3, 10) => Encoding::Utf16Be,
            _ => Encoding::Unknown,
        }
    }
}

/// A helper for encoding and decoding Mac OS Roman encoded strings.
pub struct MacRomanMapping;

impl MacRomanMapping {
    const START_REMAP: u8 = 128;
    /// Convert from a mac-roman encoded byte to a `char`
    pub fn decode(self, raw: u8) -> char {
        if raw < Self::START_REMAP {
            raw as char
        } else {
            let idx = raw - Self::START_REMAP;
            char::from_u32(MAC_ROMAN[idx as usize] as u32).unwrap()
        }
    }

    /// convert from a char to a mac-roman encoded byte, if the char is in the mac-roman charset.
    pub fn encode(self, c: char) -> Option<u8> {
        let raw_c = c as u32;
        let raw_c: u16 = raw_c.try_into().ok()?;
        if raw_c < Self::START_REMAP as u16 {
            Some(raw_c as u8)
        } else {
            match MAC_ROMAN.binary_search(&raw_c) {
                Ok(idx) => Some(idx as u8 + Self::START_REMAP),
                Err(_) => None,
            }
        }
    }
}

/// a lookup table for the mac-roman encoding. this matches the values 128..=255
/// to specific unicode values.
#[rustfmt::skip]
static MAC_ROMAN: [u16; 128] = [
    196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233,
    232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249,
    251, 252, 8224, 176, 162, 163, 167, 8226, 182, 223, 174, 169, 8482, 180,
    168, 8800, 198, 216, 8734, 177, 8804, 8805, 165, 181, 8706, 8721, 8719,
    960, 8747, 170, 186, 937, 230, 248, 191, 161, 172, 8730, 402, 8776, 8710,
    171, 187, 8230, 160, 192, 195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216,
    8217, 247, 9674, 255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183,
    8218, 8222, 8240, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212,
    63743, 210, 218, 219, 217, 305, 710, 732, 175, 728, 729, 730, 184, 733,
    731, 711,
];
<commit_msg>Fix logic around the Mac Roman encoding<commit_after>//! The [name (Naming)](https://docs.microsoft.com/en-us/typography/opentype/spec/name) table

use crate::FontData;

/// 'name'
pub const TAG: Tag = Tag::new(b"name");

include!("../../generated/generated_name.rs");

impl<'a> Name<'a> {
    /// The FontData containing the encoded name strings.
    pub fn string_data(&self) -> FontData<'a> {
        let base = self.offset_data();
        let off = self.storage_offset();
        base.split_off(off as usize).unwrap_or_default()
    }
}

impl NameRecord {
    /// Return a type that can decode the string data for this name entry.
    pub fn string<'a>(&self, data: FontData<'a>) -> Result<NameString<'a>, ReadError> {
        let start = self.string_offset().non_null().unwrap_or(0);
        let end = start + self.length() as usize;

        let data = data
            .as_bytes()
            .get(start..end)
            .ok_or(ReadError::OutOfBounds)?;

        let encoding = Encoding::new(self.platform_id(), self.encoding_id());
        Ok(NameString { data, encoding })
    }
}

impl LangTagRecord {
    /// Return a type that can decode the string data for this name entry.
    pub fn lang_tag<'a>(&self, data: FontData<'a>) -> Result<NameString<'a>, ReadError> {
        let start = self.lang_tag_offset().non_null().unwrap_or(0);
        let end = start + self.length() as usize;

        let data = data
            .as_bytes()
            .get(start..end)
            .ok_or(ReadError::OutOfBounds)?;

        let encoding = Encoding::Utf16Be;
        Ok(NameString { data, encoding })
    }
}

//-- all this is from pinot https://github.com/dfrg/pinot/blob/eff5239018ca50290fb890a84da3dd51505da364/src/name.rs
/// Entry for a name in the naming table.
///
/// This provides an iterator over characters.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct NameString<'a> {
    data: &'a [u8],
    encoding: Encoding,
}

impl<'a> NameString<'a> {
    /// An iterator over the `char`s in this name.
    pub fn chars(&self) -> CharIter<'a> {
        CharIter {
            data: self.data,
            encoding: self.encoding,
            pos: 0,
        }
    }
}

#[cfg(feature = "traversal")]
impl<'a> traversal::SomeString<'a> for NameString<'a> {
    fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a> {
        Box::new(self.into_iter())
    }
}

#[cfg(feature = "traversal")]
impl NameRecord {
    fn traverse_string<'a>(&self, data: FontData<'a>) -> traversal::FieldType<'a> {
        FieldType::StringOffset(traversal::StringOffset {
            offset: self.string_offset().into(),
            target: self.string(data).map(|s| Box::new(s) as _),
        })
    }
}

#[cfg(feature = "traversal")]
impl LangTagRecord {
    fn traverse_lang_tag<'a>(&self, data: FontData<'a>) -> traversal::FieldType<'a> {
        FieldType::StringOffset(traversal::StringOffset {
            offset: self.lang_tag_offset().into(),
            target: self.lang_tag(data).map(|s| Box::new(s) as _),
        })
    }
}

impl<'a> IntoIterator for NameString<'a> {
    type Item = char;
    type IntoIter = CharIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.chars()
    }
}

impl<'a> std::fmt::Display for NameString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        for c in self.chars() {
            c.fmt(f)?;
        }
        Ok(())
    }
}

impl<'a> std::fmt::Debug for NameString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "\"{self}\"")
    }
}

/// An iterator over the chars of a name record.
pub struct CharIter<'a> {
    data: &'a [u8],
    encoding: Encoding,
    pos: usize,
}

impl CharIter<'_> {
    fn bump_u16(&mut self) -> Option<u16> {
        let result = self
            .data
            .get(self.pos..self.pos + 2)
            .map(|x| u16::from_be_bytes(x.try_into().unwrap()))?;
        self.pos += 2;
        Some(result)
    }

    fn bump_u8(&mut self) -> Option<u8> {
        let result = self.data.get(self.pos)?;
        self.pos += 1;
        Some(*result)
    }
}

impl<'a> Iterator for CharIter<'a> {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.data.len() {
            return None;
        }
        let rep = core::char::REPLACEMENT_CHARACTER;
        let raw_c = match self.encoding {
            Encoding::Utf16Be => {
                let c1 = self.bump_u16()? as u32;
                if (0xD800..0xDC00).contains(&c1) {
                    let c2 = self.bump_u16()? as u32;
                    ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
                } else {
                    c1
                }
            }
            Encoding::MacRoman => {
                let c = self.bump_u8()?;
                MacRomanMapping.decode(c) as u32
            }
            _ => return None,
        };
        Some(std::char::from_u32(raw_c).unwrap_or(rep))
    }
}

/// The encoding used by the name table.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Encoding {
    Utf16Be,
    MacRoman,
    Unknown,
}

impl Encoding {
    /// Determine the coding from the platform and encoding id.
    pub fn new(platform_id: u16, encoding_id: u16) -> Encoding {
        match (platform_id, encoding_id) {
            (0, _) => Encoding::Utf16Be,
            (1, 0) => Encoding::MacRoman,
            (3, 0) => Encoding::Utf16Be,
            (3, 1) => Encoding::Utf16Be,
            (3, 10) => Encoding::Utf16Be,
            _ => Encoding::Unknown,
        }
    }
}

/// A helper for encoding and decoding Mac OS Roman encoded strings.
pub struct MacRomanMapping;

impl MacRomanMapping {
    const START_REMAP: u8 = 128;
    /// Convert from a mac-roman encoded byte to a `char`
    pub fn decode(self, raw: u8) -> char {
        if raw < Self::START_REMAP {
            raw as char
        } else {
            let idx = raw - Self::START_REMAP;
            char::from_u32(MAC_ROMAN_DECODE[idx as usize] as u32).unwrap()
        }
    }

    /// convert from a char to a mac-roman encoded byte, if the char is in the mac-roman charset.
    pub fn encode(self, c: char) -> Option<u8> {
        let raw_c = c as u32;
        let raw_c: u16 = raw_c.try_into().ok()?;
        if raw_c < Self::START_REMAP as u16 {
            Some(raw_c as u8)
        } else {
            match MAC_ROMAN_ENCODE.binary_search_by_key(&raw_c, |(unic, _)| *unic) {
                Ok(idx) => Some(MAC_ROMAN_ENCODE[idx].1),
                Err(_) => None,
            }
        }
    }
}

/// a lookup table for the Mac Moman encoding. this matches the values 128..=255
/// to specific unicode values.
#[rustfmt::skip]
static MAC_ROMAN_DECODE: [u16; 128] = [
    196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233,
    232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249,
    251, 252, 8224, 176, 162, 163, 167, 8226, 182, 223, 174, 169, 8482, 180,
    168, 8800, 198, 216, 8734, 177, 8804, 8805, 165, 181, 8706, 8721, 8719,
    960, 8747, 170, 186, 937, 230, 248, 191, 161, 172, 8730, 402, 8776, 8710,
    171, 187, 8230, 160, 192, 195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216,
    8217, 247, 9674, 255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183,
    8218, 8222, 8240, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212,
    63743, 210, 218, 219, 217, 305, 710, 732, 175, 728, 729, 730, 184, 733,
    731, 711,
];

/// A lookup pairing (sorted) unicode values to Mac Roman values
#[rustfmt::skip]
static MAC_ROMAN_ENCODE: [(u16, u8); 128] = [
    (160, 202), (161, 193), (162, 162), (163, 163),
    (165, 180), (167, 164), (168, 172), (169, 169),
    (170, 187), (171, 199), (172, 194), (174, 168),
    (175, 248), (176, 161), (177, 177), (180, 171),
    (181, 181), (182, 166), (183, 225), (184, 252),
    (186, 188), (187, 200), (191, 192), (192, 203),
    (193, 231), (194, 229), (195, 204), (196, 128),
    (197, 129), (198, 174), (199, 130), (200, 233),
    (201, 131), (202, 230), (203, 232), (204, 237),
    (205, 234), (206, 235), (207, 236), (209, 132),
    (210, 241), (211, 238), (212, 239), (213, 205),
    (214, 133), (216, 175), (217, 244), (218, 242),
    (219, 243), (220, 134), (223, 167), (224, 136),
    (225, 135), (226, 137), (227, 139), (228, 138),
    (229, 140), (230, 190), (231, 141), (232, 143),
    (233, 142), (234, 144), (235, 145), (236, 147),
    (237, 146), (238, 148), (239, 149), (241, 150),
    (242, 152), (243, 151), (244, 153), (245, 155),
    (246, 154), (247, 214), (248, 191), (249, 157),
    (250, 156), (251, 158), (252, 159), (255, 216),
    (305, 245), (338, 206), (339, 207), (376, 217),
    (402, 196), (710, 246), (711, 255), (728, 249),
    (729, 250), (730, 251), (731, 254), (732, 247),
    (733, 253), (937, 189), (960, 185), (8211, 208),
    (8212, 209), (8216, 212), (8217, 213), (8218, 226),
    (8220, 210), (8221, 211), (8222, 227), (8224, 160),
    (8225, 224), (8226, 165), (8230, 201), (8240, 228),
    (8249, 220), (8250, 221), (8260, 218), (8364, 219),
    (8482, 170), (8706, 182), (8710, 198), (8719, 184),
    (8721, 183), (8730, 195), (8734, 176), (8747, 186),
    (8776, 197), (8800, 173), (8804, 178), (8805, 179),
    (9674, 215), (63743, 240), (64257, 222), (64258, 223),
];

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

    #[test]
    fn mac_roman() {
        static INPUT: &str = "Joachim Müller-Lancé";
        for c in INPUT.chars() {
            let enc = MacRomanMapping.encode(c).unwrap();
            assert_eq!(MacRomanMapping.decode(enc), c);
        }
    }
}
<|endoftext|>


<commit_before>use base64::{decode, encode};
use std::collections::HashMap;
use std::str;

use conduit::RequestExt;
use conduit_middleware::{AfterResult, BeforeResult};
use cookie::{Cookie, Key};

use super::RequestCookies;

pub struct SessionMiddleware {
    cookie_name: String,
    key: Key,
    secure: bool,
}

pub struct Session {
    pub data: HashMap<String, String>,
}

impl SessionMiddleware {
    pub fn new(cookie: &str, key: Key, secure: bool) -> SessionMiddleware {
        SessionMiddleware {
            cookie_name: cookie.to_string(),
            key,
            secure,
        }
    }

    pub fn decode(&self, cookie: Cookie<'_>) -> HashMap<String, String> {
        let mut ret = HashMap::new();
        let bytes = decode(cookie.value().as_bytes()).unwrap_or_default();
        let mut parts = bytes.split(|&a| a == 0xff);
        while let (Some(key), Some(value)) = (parts.next(), parts.next()) {
            if key.is_empty() {
                break;
            }
            if let (Ok(key), Ok(value)) = (str::from_utf8(key), str::from_utf8(value)) {
                ret.insert(key.to_string(), value.to_string());
            }
        }
        ret
    }

    pub fn encode(&self, h: &HashMap<String, String>) -> String {
        let mut ret = Vec::new();
        for (i, (k, v)) in h.iter().enumerate() {
            if i != 0 {
                ret.push(0xff)
            }
            ret.extend(k.bytes());
            ret.push(0xff);
            ret.extend(v.bytes());
        }
        while ret.len() * 8 % 6 != 0 {
            ret.push(0xff);
        }
        encode(&ret[..])
    }
}

impl conduit_middleware::Middleware for SessionMiddleware {
    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
        let session = {
            let jar = req.cookies_mut().signed(&self.key);
            jar.get(&self.cookie_name)
                .map(|cookie| self.decode(cookie))
                .unwrap_or_else(HashMap::new)
        };
        req.mut_extensions().insert(Session { data: session });
        Ok(())
    }

    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
        let cookie = {
            let session = req.mut_extensions().find::<Session>();
            let session = session.expect("session must be present after request");
            let encoded = self.encode(&session.data);
            Cookie::build(self.cookie_name.to_string(), encoded)
                .http_only(true)
                .secure(self.secure)
                .path("/")
                .finish()
        };
        req.cookies_mut().signed(&self.key).add(cookie);
        res
    }
}

pub trait RequestSession {
    fn session(&mut self) -> &mut HashMap<String, String>;
}

impl<T: RequestExt + ?Sized> RequestSession for T {
    fn session(&mut self) -> &mut HashMap<String, String> {
        &mut self
            .mut_extensions()
            .find_mut::<Session>()
            .expect("missing cookie session")
            .data
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use conduit::{header, Body, Handler, HttpResult, Method, RequestExt, Response};
    use conduit_middleware::MiddlewareBuilder;
    use conduit_test::MockRequest;
    use cookie::{Cookie, Key};

    use {Middleware, RequestSession, SessionMiddleware};

    fn test_key() -> Key {
        let master_key: Vec<u8> = (0..32).collect();
        Key::from_master(&master_key)
    }

    #[test]
    fn simple() {
        let mut req = MockRequest::new(Method::POST, "/articles");
        let key = test_key();

        // Set the session cookie
        let mut app = MiddlewareBuilder::new(set_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        let response = app.call(&mut req).ok().unwrap();

        let v = response
            .headers()
            .get(header::SET_COOKIE)
            .unwrap()
            .to_str()
            .unwrap();
        assert!(v.starts_with("lol"));

        // Use the session cookie
        req.header(header::COOKIE, v);
        let key = test_key();
        let mut app = MiddlewareBuilder::new(use_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        assert!(app.call(&mut req).is_ok());

        fn set_session(req: &mut dyn RequestExt) -> HttpResult {
            assert!(req
                .session()
                .insert("foo".to_string(), "bar".to_string())
                .is_none());
            Response::builder().body(Body::empty())
        }
        fn use_session(req: &mut dyn RequestExt) -> HttpResult {
            assert_eq!(*req.session().get("foo").unwrap(), "bar");
            Response::builder().body(Body::empty())
        }
    }

    #[test]
    fn no_equals() {
        let key = test_key();
        let m = SessionMiddleware::new("test", key, false);
        let e = {
            let mut map = HashMap::new();
            map.insert("a".to_string(), "bc".to_string());
            m.encode(&map)
        };
        assert!(!e.ends_with("="));
        let m = m.decode(Cookie::new("foo", e));
        assert_eq!(*m.get("a").unwrap(), "bc");
    }
}
<commit_msg>Add dirty tracking to SessionMiddleware<commit_after>use base64::{decode, encode};
use std::collections::HashMap;
use std::str;

use conduit::RequestExt;
use conduit_middleware::{AfterResult, BeforeResult};
use cookie::{Cookie, Key};

use super::RequestCookies;

pub struct SessionMiddleware {
    cookie_name: String,
    key: Key,
    secure: bool,
}

pub struct Session {
    data: HashMap<String, String>,
    dirty: bool,
}

impl SessionMiddleware {
    pub fn new(cookie: &str, key: Key, secure: bool) -> SessionMiddleware {
        SessionMiddleware {
            cookie_name: cookie.to_string(),
            key,
            secure,
        }
    }

    pub fn decode(&self, cookie: Cookie<'_>) -> HashMap<String, String> {
        let mut ret = HashMap::new();
        let bytes = decode(cookie.value().as_bytes()).unwrap_or_default();
        let mut parts = bytes.split(|&a| a == 0xff);
        while let (Some(key), Some(value)) = (parts.next(), parts.next()) {
            if key.is_empty() {
                break;
            }
            if let (Ok(key), Ok(value)) = (str::from_utf8(key), str::from_utf8(value)) {
                ret.insert(key.to_string(), value.to_string());
            }
        }
        ret
    }

    pub fn encode(&self, h: &HashMap<String, String>) -> String {
        let mut ret = Vec::new();
        for (i, (k, v)) in h.iter().enumerate() {
            if i != 0 {
                ret.push(0xff)
            }
            ret.extend(k.bytes());
            ret.push(0xff);
            ret.extend(v.bytes());
        }
        while ret.len() * 8 % 6 != 0 {
            ret.push(0xff);
        }
        encode(&ret[..])
    }
}

impl conduit_middleware::Middleware for SessionMiddleware {
    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
        let session = {
            let jar = req.cookies_mut().signed(&self.key);
            jar.get(&self.cookie_name)
                .map(|cookie| self.decode(cookie))
                .unwrap_or_else(HashMap::new)
        };
        req.mut_extensions().insert(Session {
            data: session,
            dirty: false,
        });
        Ok(())
    }

    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
        let session = req.extensions().find::<Session>();
        let session = session.expect("session must be present after request");
        if session.dirty {
            let encoded = self.encode(&session.data);
            let cookie = Cookie::build(self.cookie_name.to_string(), encoded)
                .http_only(true)
                .secure(self.secure)
                .path("/")
                .finish();
            req.cookies_mut().signed(&self.key).add(cookie);
        }
        res
    }
}

pub trait RequestSession {
    fn session(&self) -> &HashMap<String, String>;
    fn session_mut(&mut self) -> &mut HashMap<String, String>;
}

impl<T: RequestExt + ?Sized> RequestSession for T {
    fn session(&self) -> &HashMap<String, String> {
        &self
            .extensions()
            .find::<Session>()
            .expect("missing cookie session")
            .data
    }

    fn session_mut(&mut self) -> &mut HashMap<String, String> {
        let session = self
            .mut_extensions()
            .find_mut::<Session>()
            .expect("missing cookie session");
        session.dirty = true;
        &mut session.data
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use conduit::{header, Body, Handler, HttpResult, Method, RequestExt, Response};
    use conduit_middleware::MiddlewareBuilder;
    use conduit_test::MockRequest;
    use cookie::{Cookie, Key};

    use {Middleware, RequestSession, SessionMiddleware};

    fn test_key() -> Key {
        let master_key: Vec<u8> = (0..32).collect();
        Key::from_master(&master_key)
    }

    #[test]
    fn simple() {
        let mut req = MockRequest::new(Method::POST, "/articles");
        let key = test_key();

        // Set the session cookie
        let mut app = MiddlewareBuilder::new(set_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        let response = app.call(&mut req).unwrap();

        let v = response
            .headers()
            .get(header::SET_COOKIE)
            .unwrap()
            .to_str()
            .unwrap();
        assert!(v.starts_with("lol"));

        // Use the session cookie
        req.header(header::COOKIE, v);
        let key = test_key();
        let mut app = MiddlewareBuilder::new(use_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        assert!(app.call(&mut req).is_ok());

        fn set_session(req: &mut dyn RequestExt) -> HttpResult {
            assert!(req
                .session_mut()
                .insert("foo".to_string(), "bar".to_string())
                .is_none());
            Response::builder().body(Body::empty())
        }
        fn use_session(req: &mut dyn RequestExt) -> HttpResult {
            assert_eq!(*req.session().get("foo").unwrap(), "bar");
            Response::builder().body(Body::empty())
        }
    }

    #[test]
    fn no_equals() {
        let key = test_key();
        let m = SessionMiddleware::new("test", key, false);
        let e = {
            let mut map = HashMap::new();
            map.insert("a".to_string(), "bc".to_string());
            m.encode(&map)
        };
        assert!(!e.ends_with('='));
        let m = m.decode(Cookie::new("foo", e));
        assert_eq!(*m.get("a").unwrap(), "bc");
    }

    #[test]
    fn dirty_tracking() {
        let mut req = MockRequest::new(Method::GET, "/");

        let mut app = MiddlewareBuilder::new(read_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("dirty", test_key(), false));
        let response = app.call(&mut req).unwrap();

        assert!(response.headers().get(header::SET_COOKIE).is_none());

        let mut app = MiddlewareBuilder::new(modify_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("dirty", test_key(), false));
        let response = app.call(&mut req).unwrap();

        assert!(response.headers().get(header::SET_COOKIE).is_some());

        fn read_session(req: &mut dyn RequestExt) -> HttpResult {
            req.session();
            Response::builder().body(Body::empty())
        }
        fn modify_session(req: &mut dyn RequestExt) -> HttpResult {
            req.session_mut();
            Response::builder().body(Body::empty())
        }
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]

use std::str::from_utf8;
use std::collections::HashMap;
use std::thread;

use rustc_serialize::json;
use curl::http;

pub const SESSION_TTL: &'static str = "10s";

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionCreate {
    Name: String,
    TTL: String
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionID {
    ID: String
}

pub struct Session {
    endpoint: String
}

impl Session {
    pub fn new(address: &str) ->  Session {
        Session {
            endpoint: format!("http://{}/v1/session", address)
        }
    }

    pub fn create(&self, name: String) -> String{
        let url = format!("{}/create", self.endpoint);
        let session = SessionCreate {
            Name: name,
            TTL: self::SESSION_TTL.to_owned()
        };
        let json_str = json::encode(&session).unwrap();

        let resp = http::handle()
            .put(url, &json_str)
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Consul: Error creating a session!");
        }
        let result = from_utf8(resp.get_body()).unwrap();
        let json_data = match json::Json::from_str(result) {
            Ok(value) => value,
            Err(err) => panic!("consul: Could not convert to json: {:?}", result)
        };
        super::get_string(&json_data, &["ID"])
    }

    pub fn renew(&self, session_id: &String) {
        for i in 0..10 {
            let url = format!("{}/renew/{}", self.endpoint, session_id);
            let resp = http::handle()
                .put(url, "")
                .content_type("application/json")
                .exec().unwrap();
            if resp.get_code() != 200 {
                println!("Could not renew ession: {}, returned HTTP code: {:?}. Sleeping for 1 sec", session_id, resp.get_code());
                if i == 10 {
                    panic!("Cound not renew session: {} after 10 tries. Panicing.", session_id);
                }
                thread::sleep_ms(1000);
            }
        }

    }

    pub fn end(&self, session_id: &String) {
        let url = format!("{}/destroy/{}", self.endpoint, session_id);
        let resp = http::handle()
            .put(url, "")
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Cound not destroy session: {}", session_id);
        }

    }

}
<commit_msg>Bugfix<commit_after>#![allow(non_snake_case)]

use std::str::from_utf8;
use std::collections::HashMap;
use std::thread;

use rustc_serialize::json;
use curl::http;

pub const SESSION_TTL: &'static str = "10s";

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionCreate {
    Name: String,
    TTL: String
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionID {
    ID: String
}

pub struct Session {
    endpoint: String
}

impl Session {
    pub fn new(address: &str) ->  Session {
        Session {
            endpoint: format!("http://{}/v1/session", address)
        }
    }

    pub fn create(&self, name: String) -> String{
        let url = format!("{}/create", self.endpoint);
        let session = SessionCreate {
            Name: name,
            TTL: self::SESSION_TTL.to_owned()
        };
        let json_str = json::encode(&session).unwrap();

        let resp = http::handle()
            .put(url, &json_str)
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Consul: Error creating a session!");
        }
        let result = from_utf8(resp.get_body()).unwrap();
        let json_data = match json::Json::from_str(result) {
            Ok(value) => value,
            Err(err) => panic!("consul: Could not convert to json: {:?}", result)
        };
        super::get_string(&json_data, &["ID"])
    }

    pub fn renew(&self, session_id: &String) {
        for i in 0..10 {
            let url = format!("{}/renew/{}", self.endpoint, session_id);
            let resp = http::handle()
                .put(url, "")
                .content_type("application/json")
                .exec().unwrap();
            if resp.get_code() != 200 {
                println!("Could not renew ession: {}, returned HTTP code: {:?}. Sleeping for 1 sec", session_id, resp.get_code());
                if i == 10 {
                    panic!("Cound not renew session: {} after 10 tries. Panicing.", session_id);
                }
                thread::sleep_ms(1000);
            }
            else {
                break;
            }
        }

    }

    pub fn end(&self, session_id: &String) {
        let url = format!("{}/destroy/{}", self.endpoint, session_id);
        let resp = http::handle()
            .put(url, "")
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Cound not destroy session: {}", session_id);
        }

    }

}
<|endoftext|>


<commit_before>use std::fs;
use std::io::prelude::*;
use std::process::Command;
use std::collections::HashMap;
use std::io::BufReader;
use problem::{LpProblem, Problem};
use problem::{LpFileFormat};
use std::fs::File;

#[derive(Debug, PartialEq)]
pub enum Status {
    Optimal,
    SubOptimal,
    Infeasible,
    Unbounded,
    NotSolved,
}

pub trait SolverTrait {
    type P: Problem;
    fn run(&self, problem: &Self::P) -> Result<(Status, HashMap<String,f32>), String>;
}

pub struct GurobiSolver {
    name: String,
    command_name: String,
    temp_solution_file: String,
}
pub struct CbcSolver {
    name: String,
    command_name: String,
    temp_solution_file: String,
}

impl GurobiSolver {
    pub fn new() -> GurobiSolver {
        GurobiSolver { name: "Gurobi".to_string(), command_name: "gurobi_cl".to_string(), temp_solution_file: "sol.sol".to_string() }
    }
    pub fn command_name(&self, command_name: String) -> GurobiSolver {
        GurobiSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
    }
    fn read_solution(&self) -> Result<(Status, HashMap<String, f32>), String> {
        fn read_specific_solution(f: &File) -> Result<(Status, HashMap<String, f32>), String> {

            let mut vars_value: HashMap<_,_> = HashMap::new();
            let mut file = BufReader::new(f);
            let mut buffer = String::new();
            let _ = file.read_line(&mut buffer);

            if let Some(_) = buffer.split(" ").next() {
                for line in file.lines() {
                    let l = line.unwrap();
                    let result_line: Vec<_> = l.split_whitespace().collect();
                    if result_line.len() == 2 {
                        match result_line[1].parse::<f32>() {
                            Ok(n) => {
                                vars_value.insert(result_line[0].to_string(), n);
                            },
                            Err(e) => return Err(format!("{}", e.to_string()))
                        }
                    }else{
                        return Err("Incorrect solution format".to_string())
                    }
                }
            }else{
                return Err("Incorrect solution format".to_string())
            }
            Ok((Status::Optimal, vars_value))
        }

        match File::open(&self.temp_solution_file) {
            Ok(f) => {
                let res = try!(read_specific_solution(&f));
                let _ = fs::remove_file(&self.temp_solution_file);
                Ok(res)
            },
            Err(_) => return Err("Cannot open file".to_string())
        }
    }
}
impl CbcSolver {
    pub fn new() -> CbcSolver {
        CbcSolver { name: "Cbc".to_string(), command_name: "cbc".to_string(), temp_solution_file: "sol.sol".to_string() }
    }
    pub fn command_name(&self, command_name: String) -> CbcSolver {
        CbcSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
    }

    fn read_solution(&self) -> Result<(Status, HashMap<String, f32>), String> {
        fn read_specific_solution(f: &File) -> Result<(Status, HashMap<String, f32>), String> {
            let mut vars_value: HashMap<_, _> = HashMap::new();

            let mut file = BufReader::new(f);
            let mut buffer = String::new();
            let _ = file.read_line(&mut buffer);
            let mut status = Status::SubOptimal;

            if let Some(status_line) = buffer.split(" ").next() {
                if status_line.contains("Optimal") {
                    status = Status::Optimal;
                }
                for line in file.lines() {
                    let l = line.unwrap();
                    let result_line: Vec<_> = l.split_whitespace().collect();
                    if result_line.len() == 4 {
                        match result_line[2].parse::<f32>() {
                            Ok(n) => {
                                vars_value.insert(result_line[1].to_string(), n);
                            },
                            Err(e) => return Err(format!("{}", e.to_string()))
                        }
                    } else {
                        return Err("Incorrect solution format".to_string())
                    }
                }
            } else {
                return Err("Incorrect solution format".to_string())
            }
            Ok((status, vars_value))
        }

        match File::open(&self.temp_solution_file) {
            Ok(f) => {
                let res = try!(read_specific_solution(&f));
                let _ = fs::remove_file(&self.temp_solution_file);
                Ok(res)
            },
            Err(_) => return Err("Cannot open file".to_string())
        }
    }
}

impl SolverTrait for GurobiSolver {
    type P = LpProblem;
    fn run(&self, problem: &Self::P) -> Result<(Status, HashMap<String,f32>), String> {

        let file_model = "test.lp";

        match problem.write_lp(file_model) {
            Ok(_) => {
                match Command::new(&self.command_name).arg(format!("ResultFile={}", self.temp_solution_file)).arg(file_model).output() {
                    Ok(r) => {
                        let mut status = Status::SubOptimal;
                        if String::from_utf8(r.stdout).expect("").contains("Optimal solution found") {
                            status = Status::Optimal;
                        }
                        if r.status.success() {
                            let (_, res) = try!(self.read_solution());
                            Ok((status, res))
                        } else {
                            Err(r.status.to_string())
                        }
                    },
                    Err(_) => Err(format!("Error running the {} solver", self.name)),
                }
            },
            Err(e) => Err(e.to_string()),
        }
    }
}

impl SolverTrait for CbcSolver {
    type P = LpProblem;
    fn run(&self, problem: &Self::P) -> Result<(Status, HashMap<String,f32>), String> {

        let file_model = "test.lp";

        match problem.write_lp(file_model) {
            Ok(_) => {
                match Command::new(&self.command_name).arg(format!("ResultFile={}", self.temp_solution_file)).arg(file_model).output() {
                    Ok(r) => {
                        if r.status.success(){
                            self.read_solution()
                        }else{
                            Err(r.status.to_string())
                        }
                    },
                    Err(_) => Err(format!("Error running the {} solver", self.name)),
                }
            },
            Err(e) => Err(e.to_string()),
        }
    }
}

<commit_msg>Fix arguments in Cbc command<commit_after>use std::fs;
use std::io::prelude::*;
use std::process::Command;
use std::collections::HashMap;
use std::io::BufReader;
use problem::{LpProblem, Problem};
use problem::{LpFileFormat};
use std::fs::File;

#[derive(Debug, PartialEq)]
pub enum Status {
    Optimal,
    SubOptimal,
    Infeasible,
    Unbounded,
    NotSolved,
}

pub trait SolverTrait {
    type P: Problem;
    fn run(&self, problem: &Self::P) -> Result<(Status, HashMap<String,f32>), String>;
}

pub struct GurobiSolver {
    name: String,
    command_name: String,
    temp_solution_file: String,
}
pub struct CbcSolver {
    name: String,
    command_name: String,
    temp_solution_file: String,
}

impl GurobiSolver {
    pub fn new() -> GurobiSolver {
        GurobiSolver { name: "Gurobi".to_string(), command_name: "gurobi_cl".to_string(), temp_solution_file: "sol.sol".to_string() }
    }
    pub fn command_name(&self, command_name: String) -> GurobiSolver {
        GurobiSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
    }
    fn read_solution(&self) -> Result<(Status, HashMap<String, f32>), String> {
        fn read_specific_solution(f: &File) -> Result<(Status, HashMap<String, f32>), String> {

            let mut vars_value: HashMap<_,_> = HashMap::new();
            let mut file = BufReader::new(f);
            let mut buffer = String::new();
            let _ = file.read_line(&mut buffer);

            if let Some(_) = buffer.split(" ").next() {
                for line in file.lines() {
                    let l = line.unwrap();
                    let result_line: Vec<_> = l.split_whitespace().collect();
                    if result_line.len() == 2 {
                        match result_line[1].parse::<f32>() {
                            Ok(n) => {
                                vars_value.insert(result_line[0].to_string(), n);
                            },
                            Err(e) => return Err(format!("{}", e.to_string()))
                        }
                    }else{
                        return Err("Incorrect solution format".to_string())
                    }
                }
            }else{
                return Err("Incorrect solution format".to_string())
            }
            Ok((Status::Optimal, vars_value))
        }

        match File::open(&self.temp_solution_file) {
            Ok(f) => {
                let res = try!(read_specific_solution(&f));
                let _ = fs::remove_file(&self.temp_solution_file);
                Ok(res)
            },
            Err(_) => return Err("Cannot open file".to_string())
        }
    }
}
impl CbcSolver {
    pub fn new() -> CbcSolver {
        CbcSolver { name: "Cbc".to_string(), command_name: "cbc".to_string(), temp_solution_file: "sol.sol".to_string() }
    }
    pub fn command_name(&self, command_name: String) -> CbcSolver {
        CbcSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
    }

    fn read_solution(&self) -> Result<(Status, HashMap<String, f32>), String> {
        fn read_specific_solution(f: &File) -> Result<(Status, HashMap<String, f32>), String> {
            let mut vars_value: HashMap<_, _> = HashMap::new();

            let mut file = BufReader::new(f);
            let mut buffer = String::new();
            let _ = file.read_line(&mut buffer);
            let mut status = Status::SubOptimal;

            if let Some(status_line) = buffer.split(" ").next() {
                if status_line.contains("Optimal") {
                    status = Status::Optimal;
                }
                for line in file.lines() {
                    let l = line.unwrap();
                    let result_line: Vec<_> = l.split_whitespace().collect();
                    if result_line.len() == 4 {
                        match result_line[2].parse::<f32>() {
                            Ok(n) => {
                                vars_value.insert(result_line[1].to_string(), n);
                            },
                            Err(e) => return Err(format!("{}", e.to_string()))
                        }
                    } else {
                        return Err("Incorrect solution format".to_string())
                    }
                }
            } else {
                return Err("Incorrect solution format".to_string())
            }
            Ok((status, vars_value))
        }

        match File::open(&self.temp_solution_file) {
            Ok(f) => {
                let res = try!(read_specific_solution(&f));
                let _ = fs::remove_file(&self.temp_solution_file);
                Ok(res)
            },
            Err(_) => return Err("Cannot open file".to_string())
        }
    }
}

impl SolverTrait for GurobiSolver {
    type P = LpProblem;
    fn run(&self, problem: &Self::P) -> Result<(Status, HashMap<String,f32>), String> {

        let file_model = "test.lp";

        match problem.write_lp(file_model) {
            Ok(_) => {
                match Command::new(&self.command_name).arg(format!("ResultFile={}", self.temp_solution_file)).arg(file_model).output() {
                    Ok(r) => {
                        let mut status = Status::SubOptimal;
                        if String::from_utf8(r.stdout).expect("").contains("Optimal solution found") {
                            status = Status::Optimal;
                        }
                        if r.status.success() {
                            let (_, res) = try!(self.read_solution());
                            Ok((status, res))
                        } else {
                            Err(r.status.to_string())
                        }
                    },
                    Err(_) => Err(format!("Error running the {} solver", self.name)),
                }
            },
            Err(e) => Err(e.to_string()),
        }
    }
}

impl SolverTrait for CbcSolver {
    type P = LpProblem;
    fn run(&self, problem: &Self::P) -> Result<(Status, HashMap<String,f32>), String> {

        let file_model = "test.lp";

        match problem.write_lp(file_model) {
            Ok(_) => {
                match Command::new(&self.command_name).arg(file_model).arg("solve").arg("solution").arg(&self.temp_solution_file).output() {
                    Ok(r) => {
                        if r.status.success(){
                            self.read_solution()
                        }else{
                            Err(r.status.to_string())
                        }
                    },
                    Err(_) => Err(format!("Error running the {} solver", self.name)),
                }
            },
            Err(e) => Err(e.to_string()),
        }
    }
}

<|endoftext|>


<commit_before>/* Copyright 2017 David Li.

 * This file is part of BigRedRPG.

 * BigRedRPG is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.

 * BigRedRPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU Affero General Public License
 * along with BigRedRPG.  If not, see <http://www.gnu.org/licenses/>.
 */

use std::cell::RefCell;
use std::collections::HashMap;

use chance::{Chance, Disposition};
use goal::Goal;
use location::LocationId;
use nature::Nature;
use util::rng;

pub type StudentId = u32;

#[derive(Clone,Copy,Debug,Eq,PartialEq)]
pub enum StatusKind {
    Stress,
    Boredom,
    Exhaustion,
    Grades,
}

#[derive(Clone)]
pub struct StatusModifier {
    pub description: String,
    pub modifiers: Vec<(StatusKind, i32)>,
}

pub struct Status {
    base: u32,
    range: (u32, u32),
    current: i32,
}

impl Status {
    pub fn new(base: u32) -> Status {
        if base > 100 {
            panic!("Base status value outside range (0, 100): {}", base);
        }
        Status {
            base: base,
            range: (0, 100),
            current: base as i32,
        }
    }

    pub fn value(&self) -> u32 {
        if self.current < self.range.0 as i32 {
            self.range.0
        }
        else if self.current > self.range.1 as i32 {
            self.range.1
        }
        else {
            self.current as u32
        }
    }
}

pub struct Student {
    pub id: StudentId,
    pub name: String,
    pub major: String,
    pub dorm: LocationId,

    /// The current goals of the student. Invariant: should be
    /// maintained in sorted order by chance. Chances do not need to
    /// add to 100.
    goals: RefCell<Vec<(Goal, Chance, Vec<Disposition>)>>,

    stress: Status,
    boredom: Status,
    exhaustion: Status,
    grades: Status,

    modifiers: Vec<StatusModifier>,
    natures: HashMap<String, Nature>,
}

impl Student {
    pub fn new<S: Into<String>>(id: StudentId, name: S, major: S, dorm: LocationId) -> Student {
        Student {
            id: id,
            name: name.into(),
            major: major.into(),
            dorm: dorm,

            goals: RefCell::new(Vec::new()),

            stress: Status::new(0),
            boredom: Status::new(0),
            exhaustion: Status::new(0),
            grades: Status::new(100),

            modifiers: Vec::new(),
            natures: HashMap::new(),
        }
    }

    pub fn apply_modifier(&mut self, modifier: StatusModifier) {
        for &(status, amount) in modifier.modifiers.iter() {
            let mut stat = match status {
                StatusKind::Stress => &mut self.stress,
                StatusKind::Boredom => &mut self.boredom,
                StatusKind::Exhaustion => &mut self.exhaustion,
                StatusKind::Grades => &mut self.grades,
            };
            stat.current += amount;
        }

        self.modifiers.push(modifier);
    }

    pub fn has_nature<'a, S: Into<&'a str>>(&self, nature: S) -> bool {
        self.natures.contains_key(nature.into())
    }

    pub fn get_status(&self, kind: StatusKind) -> &Status {
        use self::StatusKind::*;

        match kind {
            Stress => &self.stress,
            Boredom => &self.boredom,
            Exhaustion => &self.exhaustion,
            Grades => &self.grades,
        }
    }

    pub fn check_goals(&mut self, location: LocationId) {
        let mut goals = self.goals.borrow_mut();
        goals.retain(|&(ref goal, _, _)| {
            !goal.is_fulfilled(&self, location)
        });
    }

    pub fn with_weighted_goals<F, R>(&self, f: F) -> R
        where F: FnOnce(Vec<(&Goal, Chance)>) -> R
    {
        let goals = self.goals.borrow_mut();
        let weighted_goals = rng::convert_disposition_list(goals.as_slice(), self);

        f(weighted_goals)
    }
}
<commit_msg>feat(Student): allow adding goals to student<commit_after>/* Copyright 2017 David Li.

 * This file is part of BigRedRPG.

 * BigRedRPG is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.

 * BigRedRPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU Affero General Public License
 * along with BigRedRPG.  If not, see <http://www.gnu.org/licenses/>.
 */

use std::cell::RefCell;
use std::collections::HashMap;

use chance::{Chance, Disposition};
use goal::Goal;
use location::LocationId;
use nature::Nature;
use util::rng;

pub type StudentId = u32;

#[derive(Clone,Copy,Debug,Eq,PartialEq)]
pub enum StatusKind {
    Stress,
    Boredom,
    Exhaustion,
    Grades,
}

#[derive(Clone)]
pub struct StatusModifier {
    pub description: String,
    pub modifiers: Vec<(StatusKind, i32)>,
}

pub struct Status {
    base: u32,
    range: (u32, u32),
    current: i32,
}

impl Status {
    pub fn new(base: u32) -> Status {
        if base > 100 {
            panic!("Base status value outside range (0, 100): {}", base);
        }
        Status {
            base: base,
            range: (0, 100),
            current: base as i32,
        }
    }

    pub fn value(&self) -> u32 {
        if self.current < self.range.0 as i32 {
            self.range.0
        }
        else if self.current > self.range.1 as i32 {
            self.range.1
        }
        else {
            self.current as u32
        }
    }
}

pub struct Student {
    pub id: StudentId,
    pub name: String,
    pub major: String,
    pub dorm: LocationId,

    /// The current goals of the student. Invariant: should be
    /// maintained in sorted order by chance. Chances do not need to
    /// add to 100.
    goals: RefCell<Vec<(Goal, Chance, Vec<Disposition>)>>,

    stress: Status,
    boredom: Status,
    exhaustion: Status,
    grades: Status,

    modifiers: Vec<StatusModifier>,
    natures: HashMap<String, Nature>,
}

impl Student {
    pub fn new<S: Into<String>>(id: StudentId, name: S, major: S, dorm: LocationId) -> Student {
        Student {
            id: id,
            name: name.into(),
            major: major.into(),
            dorm: dorm,

            goals: RefCell::new(Vec::new()),

            stress: Status::new(0),
            boredom: Status::new(0),
            exhaustion: Status::new(0),
            grades: Status::new(100),

            modifiers: Vec::new(),
            natures: HashMap::new(),
        }
    }

    pub fn apply_modifier(&mut self, modifier: StatusModifier) {
        for &(status, amount) in modifier.modifiers.iter() {
            let mut stat = match status {
                StatusKind::Stress => &mut self.stress,
                StatusKind::Boredom => &mut self.boredom,
                StatusKind::Exhaustion => &mut self.exhaustion,
                StatusKind::Grades => &mut self.grades,
            };
            stat.current += amount;
        }

        self.modifiers.push(modifier);
    }

    pub fn has_nature<'a, S: Into<&'a str>>(&self, nature: S) -> bool {
        self.natures.contains_key(nature.into())
    }

    pub fn get_status(&self, kind: StatusKind) -> &Status {
        use self::StatusKind::*;

        match kind {
            Stress => &self.stress,
            Boredom => &self.boredom,
            Exhaustion => &self.exhaustion,
            Grades => &self.grades,
        }
    }

    pub fn add_goal(&mut self, goal: Goal, chance: Chance, disposition: Vec<Disposition>) {
        self.goals.borrow_mut().push((goal, chance, disposition));
        self.goals.borrow_mut().sort_by_key(|&(_, chance, _)| chance);
    }

    pub fn check_goals(&mut self, location: LocationId) {
        let mut goals = self.goals.borrow_mut();
        goals.retain(|&(ref goal, _, _)| {
            !goal.is_fulfilled(&self, location)
        });
    }

    pub fn with_weighted_goals<F, R>(&self, f: F) -> R
        where F: FnOnce(Vec<(&Goal, Chance)>) -> R
    {
        let goals = self.goals.borrow_mut();
        let weighted_goals = rng::convert_disposition_list(goals.as_slice(), self);

        f(weighted_goals)
    }
}
<|endoftext|>


<commit_before>#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod epoll;

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
    target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
pub mod event;

// TODO: switch from feature flags to conditional builds
#[cfg(feature = "eventfd")]
pub mod eventfd;

#[cfg(target_os = "linux")]
pub mod memfd;

#[cfg(not(any(target_os = "ios", target_os = "freebsd", target_os = "dragonfly")))]
pub mod ioctl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod sendfile;

pub mod signal;

#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(feature = "signalfd")]
pub mod signalfd;

pub mod socket;

pub mod stat;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod syscall;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod reboot;

#[cfg(not(target_os = "ios"))]
pub mod termios;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod utsname;

pub mod wait;

pub mod mman;

pub mod uio;

pub mod time;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod ptrace;

pub mod select;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod quota;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statfs;

#[cfg(all(any(target_os = "linux",
              target_os = "macos"),
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statvfs;
<commit_msg>Stop targeting Android<commit_after>#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod epoll;

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
    target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
pub mod event;

// TODO: switch from feature flags to conditional builds
#[cfg(feature = "eventfd")]
pub mod eventfd;

#[cfg(target_os = "linux")]
pub mod memfd;

#[cfg(not(any(target_os = "ios", target_os = "freebsd", target_os = "dragonfly")))]
pub mod ioctl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod sendfile;

pub mod signal;

#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(feature = "signalfd")]
pub mod signalfd;

pub mod socket;

pub mod stat;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod syscall;

#[cfg(any(target_os = "linux"))]
pub mod reboot;

#[cfg(not(target_os = "ios"))]
pub mod termios;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod utsname;

pub mod wait;

pub mod mman;

pub mod uio;

pub mod time;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod ptrace;

pub mod select;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod quota;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statfs;

#[cfg(all(any(target_os = "linux",
              target_os = "macos"),
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statvfs;
<|endoftext|>