<commit_before>//! Structs for reading a ZIP archive

use crc32::Crc32Reader;
use compression::CompressionMethod;
use spec;
use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use flate2;
use flate2::FlateReadExt;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};

#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;

mod ffi {
    pub const S_IFDIR: u32 = 0o0040000;
    pub const S_IFREG: u32 = 0o0100000;
}

/// Wrapper for reading the contents of a ZIP file.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::prelude::*;
///
///     // For demonstration purposes we read from an empty buffer.
///     // Normally a File object would be used.
///     let buf: &[u8] = &[0u8; 128];
///     let mut reader = std::io::Cursor::new(buf);
///
///     let mut zip = try!(zip::ZipArchive::new(reader));
///
///     for i in 0..zip.len()
///     {
///         let mut file = zip.by_index(i).unwrap();
///         println!("Filename: {}", file.name());
///         let first_byte = try!(file.bytes().next().unwrap());
///         println!("{}", first_byte);
///     }
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
#[derive(Debug)]
pub struct ZipArchive<R: Read + io::Seek>
{
    reader: R,
    files: Vec<ZipFileData>,
    names_map: HashMap<String, usize>,
    offset: u32,
}

enum ZipFileReader<'a> {
    Stored(Crc32Reader<io::Take<&'a mut Read>>),
    Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
    #[cfg(feature = "bzip2")]
    Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
}

/// A struct for reading a zip file
pub struct ZipFile<'a> {
    data: &'a ZipFileData,
    reader: ZipFileReader<'a>,
}

fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
{
    Err(ZipError::UnsupportedArchive(detail))
}

impl<R: Read+io::Seek> ZipArchive<R>
{
    /// Opens a Zip archive and parses the central directory
    pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
        let (footer, cde_start_pos) = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));

        if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }

        // Some zip files have data prepended to them, resulting in the offsets all being too small. Get the amount of
        // error by comparing the actual file position we found the CDE at with the offset recorded in the CDE.
        let archive_offset = cde_start_pos - footer.central_directory_size - footer.central_directory_offset;

        let directory_start = (footer.central_directory_offset + archive_offset) as u64;
        let number_of_files = footer.number_of_files_on_this_disk as usize;

        let mut files = Vec::with_capacity(number_of_files);
        let mut names_map = HashMap::new();

        try!(reader.seek(io::SeekFrom::Start(directory_start)));
        for _ in 0 .. number_of_files
        {
            let file = try!(central_header_to_zip_file(&mut reader, archive_offset));
            names_map.insert(file.file_name.clone(), files.len());
            files.push(file);
        }

        Ok(ZipArchive {
            reader: reader,
            files: files,
            names_map: names_map,
            offset: archive_offset,
        })
    }

    /// Number of files contained in this zip.
    ///
    /// ```
    /// fn iter() {
    ///     let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
    ///
    ///     for i in 0..zip.len() {
    ///         let mut file = zip.by_index(i).unwrap();
    ///         // Do something with file i
    ///     }
    /// }
    /// ```
    pub fn len(&self) -> usize
    {
        self.files.len()
    }

    /// Get the offset from the beginning of the underlying reader that this zip begins at, in bytes.
    ///
    /// Normally this value is zero, but if the zip has arbitrary data prepended to it, then this value will be the size
    /// of that prepended data.
    pub fn offset(&self) -> u32 {
        self.offset
    }

    /// Search for a file entry by name
    pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
    {
        let index = match self.names_map.get(name) {
            Some(index) => *index,
            None => { return Err(ZipError::FileNotFound); },
        };
        self.by_index(index)
    }

    /// Get a contained file by index
    pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
    {
        if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
        let ref data = self.files[file_number];
        let pos = data.data_start;

        if data.encrypted
        {
            return unsupported_zip_error("Encrypted files are not supported")
        }

        try!(self.reader.seek(io::SeekFrom::Start(pos)));
        let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);

        let reader = match data.compression_method
        {
            CompressionMethod::Stored =>
            {
                ZipFileReader::Stored(Crc32Reader::new(
                    limit_reader,
                    data.crc32))
            },
            CompressionMethod::Deflated =>
            {
                let deflate_reader = limit_reader.deflate_decode();
                ZipFileReader::Deflated(Crc32Reader::new(
                    deflate_reader,
                    data.crc32))
            },
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 =>
            {
                let bzip2_reader = BzDecoder::new(limit_reader);
                ZipFileReader::Bzip2(Crc32Reader::new(
                    bzip2_reader,
                    data.crc32))
            },
            _ => return unsupported_zip_error("Compression method not supported"),
        };
        Ok(ZipFile { reader: reader, data: data })
    }

    /// Unwrap and return the inner reader object
    ///
    /// The position of the reader is undefined.
    pub fn into_inner(self) -> R
    {
        self.reader
    }
}

fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R, archive_offset: u32) -> ZipResult<ZipFileData>
{
    // Parse central header
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = try!(reader.read_u16::<LittleEndian>());
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let _disk_number = try!(reader.read_u16::<LittleEndian>());
    let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
    let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
    let mut offset = try!(reader.read_u32::<LittleEndian>()) as u64;
    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
    let file_comment_raw  = try!(ReadPodExt::read_exact(reader, file_comment_length));

    // Account for shifted zip offsets.
    offset += archive_offset as u64;

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };
    let file_comment = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
        false => file_comment_raw.from_cp437(),
    };

    // Remember end of central header
    let return_position = try!(reader.seek(io::SeekFrom::Current(0)));

    // Parse local header
    try!(reader.seek(io::SeekFrom::Start(offset)));
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid local file header"))
    }

    try!(reader.seek(io::SeekFrom::Current(22)));
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let mut result = ZipFileData
    {
        system: System::from_u8((version_made_by >> 8) as u8),
        version_made_by: version_made_by as u8,
        encrypted: encrypted,
        compression_method: CompressionMethod::from_u16(compression_method),
        last_modified_time: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_name_raw: file_name_raw,
        file_comment: file_comment,
        header_start: offset,
        data_start: data_start,
        external_attributes: external_file_attributes,
    };

    match parse_extra_field(&mut result, &*extra_field) {
        Ok(..) | Err(ZipError::Io(..)) => {},
        Err(e) => try!(Err(e)),
    }

    // Go back after the central header
    try!(reader.seek(io::SeekFrom::Start(return_position)));

    Ok(result)
}

fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
    let mut reader = io::Cursor::new(data);

    while (reader.position() as usize) < data.len()
    {
        let kind = try!(reader.read_u16::<LittleEndian>());
        let len = try!(reader.read_u16::<LittleEndian>());
        match kind
        {
            _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
        };
    }
    Ok(())
}

/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
    fn get_reader(&mut self) -> &mut Read {
        match self.reader {
           ZipFileReader::Stored(ref mut r) => r as &mut Read,
           ZipFileReader::Deflated(ref mut r) => r as &mut Read,
           #[cfg(feature = "bzip2")]
           ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
        }
    }
    /// Get the version of the file
    pub fn version_made_by(&self) -> (u8, u8) {
        (self.data.version_made_by / 10, self.data.version_made_by % 10)
    }
    /// Get the name of the file
    pub fn name(&self) -> &str {
        &*self.data.file_name
    }
    /// Get the name of the file, in the raw (internal) byte representation.
    pub fn name_raw(&self) -> &[u8] {
        &*self.data.file_name_raw
    }
    /// Get the comment of the file
    pub fn comment(&self) -> &str {
        &*self.data.file_comment
    }
    /// Get the compression method used to store the file
    pub fn compression(&self) -> CompressionMethod {
        self.data.compression_method
    }
    /// Get the size of the file in the archive
    pub fn compressed_size(&self) -> u64 {
        self.data.compressed_size
    }
    /// Get the size of the file when uncompressed
    pub fn size(&self) -> u64 {
        self.data.uncompressed_size
    }
    /// Get the time the file was last modified
    pub fn last_modified(&self) -> ::time::Tm {
        self.data.last_modified_time
    }
    /// Get unix mode for the file
    pub fn unix_mode(&self) -> Option<u32> {
        match self.data.system {
            System::Unix => {
                Some(self.data.external_attributes >> 16)
            },
            System::Dos => {
                // Interpret MSDOS directory bit
                let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                    ffi::S_IFDIR | 0o0775
                } else {
                    ffi::S_IFREG | 0o0664
                };
                if 0x01 == (self.data.external_attributes & 0x01) {
                    // Read-only bit; strip write permissions
                    mode &= 0o0555;
                }
                Some(mode)
            },
            _ => None,
        }
    }
    /// Get the CRC32 hash of the original file
    pub fn crc32(&self) -> u32 {
        self.data.crc32
    }

    /// Get the starting offset of the data of the compressed file
    pub fn data_start(&self) -> u64 {
        self.data.data_start
    }
}

impl<'a> Read for ZipFile<'a> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_reader().read(buf)
     }
}
<commit_msg>Prevent subtract with overflow<commit_after>//! Structs for reading a ZIP archive

use crc32::Crc32Reader;
use compression::CompressionMethod;
use spec;
use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use flate2;
use flate2::FlateReadExt;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};

#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;

mod ffi {
    pub const S_IFDIR: u32 = 0o0040000;
    pub const S_IFREG: u32 = 0o0100000;
}

/// Wrapper for reading the contents of a ZIP file.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::prelude::*;
///
///     // For demonstration purposes we read from an empty buffer.
///     // Normally a File object would be used.
///     let buf: &[u8] = &[0u8; 128];
///     let mut reader = std::io::Cursor::new(buf);
///
///     let mut zip = try!(zip::ZipArchive::new(reader));
///
///     for i in 0..zip.len()
///     {
///         let mut file = zip.by_index(i).unwrap();
///         println!("Filename: {}", file.name());
///         let first_byte = try!(file.bytes().next().unwrap());
///         println!("{}", first_byte);
///     }
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
#[derive(Debug)]
pub struct ZipArchive<R: Read + io::Seek>
{
    reader: R,
    files: Vec<ZipFileData>,
    names_map: HashMap<String, usize>,
    offset: u32,
}

enum ZipFileReader<'a> {
    Stored(Crc32Reader<io::Take<&'a mut Read>>),
    Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
    #[cfg(feature = "bzip2")]
    Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
}

/// A struct for reading a zip file
pub struct ZipFile<'a> {
    data: &'a ZipFileData,
    reader: ZipFileReader<'a>,
}

fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
{
    Err(ZipError::UnsupportedArchive(detail))
}

impl<R: Read+io::Seek> ZipArchive<R>
{
    /// Opens a Zip archive and parses the central directory
    pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
        let (footer, cde_start_pos) = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));

        if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }

        // Some zip files have data prepended to them, resulting in the offsets all being too small. Get the amount of
        // error by comparing the actual file position we found the CDE at with the offset recorded in the CDE.
        let archive_offset = cde_start_pos.checked_sub(footer.central_directory_size)
            .and_then(|x| x.checked_sub(footer.central_directory_offset))
            .ok_or(ZipError::InvalidArchive("Invalid central directory size or offset"))?;

        let directory_start = (footer.central_directory_offset + archive_offset) as u64;
        let number_of_files = footer.number_of_files_on_this_disk as usize;

        let mut files = Vec::with_capacity(number_of_files);
        let mut names_map = HashMap::new();

        try!(reader.seek(io::SeekFrom::Start(directory_start)));
        for _ in 0 .. number_of_files
        {
            let file = try!(central_header_to_zip_file(&mut reader, archive_offset));
            names_map.insert(file.file_name.clone(), files.len());
            files.push(file);
        }

        Ok(ZipArchive {
            reader: reader,
            files: files,
            names_map: names_map,
            offset: archive_offset,
        })
    }

    /// Number of files contained in this zip.
    ///
    /// ```
    /// fn iter() {
    ///     let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
    ///
    ///     for i in 0..zip.len() {
    ///         let mut file = zip.by_index(i).unwrap();
    ///         // Do something with file i
    ///     }
    /// }
    /// ```
    pub fn len(&self) -> usize
    {
        self.files.len()
    }

    /// Get the offset from the beginning of the underlying reader that this zip begins at, in bytes.
    ///
    /// Normally this value is zero, but if the zip has arbitrary data prepended to it, then this value will be the size
    /// of that prepended data.
    pub fn offset(&self) -> u32 {
        self.offset
    }

    /// Search for a file entry by name
    pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
    {
        let index = match self.names_map.get(name) {
            Some(index) => *index,
            None => { return Err(ZipError::FileNotFound); },
        };
        self.by_index(index)
    }

    /// Get a contained file by index
    pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
    {
        if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
        let ref data = self.files[file_number];
        let pos = data.data_start;

        if data.encrypted
        {
            return unsupported_zip_error("Encrypted files are not supported")
        }

        try!(self.reader.seek(io::SeekFrom::Start(pos)));
        let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);

        let reader = match data.compression_method
        {
            CompressionMethod::Stored =>
            {
                ZipFileReader::Stored(Crc32Reader::new(
                    limit_reader,
                    data.crc32))
            },
            CompressionMethod::Deflated =>
            {
                let deflate_reader = limit_reader.deflate_decode();
                ZipFileReader::Deflated(Crc32Reader::new(
                    deflate_reader,
                    data.crc32))
            },
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 =>
            {
                let bzip2_reader = BzDecoder::new(limit_reader);
                ZipFileReader::Bzip2(Crc32Reader::new(
                    bzip2_reader,
                    data.crc32))
            },
            _ => return unsupported_zip_error("Compression method not supported"),
        };
        Ok(ZipFile { reader: reader, data: data })
    }

    /// Unwrap and return the inner reader object
    ///
    /// The position of the reader is undefined.
    pub fn into_inner(self) -> R
    {
        self.reader
    }
}

fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R, archive_offset: u32) -> ZipResult<ZipFileData>
{
    // Parse central header
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = try!(reader.read_u16::<LittleEndian>());
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let _disk_number = try!(reader.read_u16::<LittleEndian>());
    let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
    let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
    let mut offset = try!(reader.read_u32::<LittleEndian>()) as u64;
    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
    let file_comment_raw  = try!(ReadPodExt::read_exact(reader, file_comment_length));

    // Account for shifted zip offsets.
    offset += archive_offset as u64;

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };
    let file_comment = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
        false => file_comment_raw.from_cp437(),
    };

    // Remember end of central header
    let return_position = try!(reader.seek(io::SeekFrom::Current(0)));

    // Parse local header
    try!(reader.seek(io::SeekFrom::Start(offset)));
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid local file header"))
    }

    try!(reader.seek(io::SeekFrom::Current(22)));
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let mut result = ZipFileData
    {
        system: System::from_u8((version_made_by >> 8) as u8),
        version_made_by: version_made_by as u8,
        encrypted: encrypted,
        compression_method: CompressionMethod::from_u16(compression_method),
        last_modified_time: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_name_raw: file_name_raw,
        file_comment: file_comment,
        header_start: offset,
        data_start: data_start,
        external_attributes: external_file_attributes,
    };

    match parse_extra_field(&mut result, &*extra_field) {
        Ok(..) | Err(ZipError::Io(..)) => {},
        Err(e) => try!(Err(e)),
    }

    // Go back after the central header
    try!(reader.seek(io::SeekFrom::Start(return_position)));

    Ok(result)
}

fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
    let mut reader = io::Cursor::new(data);

    while (reader.position() as usize) < data.len()
    {
        let kind = try!(reader.read_u16::<LittleEndian>());
        let len = try!(reader.read_u16::<LittleEndian>());
        match kind
        {
            _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
        };
    }
    Ok(())
}

/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
    fn get_reader(&mut self) -> &mut Read {
        match self.reader {
           ZipFileReader::Stored(ref mut r) => r as &mut Read,
           ZipFileReader::Deflated(ref mut r) => r as &mut Read,
           #[cfg(feature = "bzip2")]
           ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
        }
    }
    /// Get the version of the file
    pub fn version_made_by(&self) -> (u8, u8) {
        (self.data.version_made_by / 10, self.data.version_made_by % 10)
    }
    /// Get the name of the file
    pub fn name(&self) -> &str {
        &*self.data.file_name
    }
    /// Get the name of the file, in the raw (internal) byte representation.
    pub fn name_raw(&self) -> &[u8] {
        &*self.data.file_name_raw
    }
    /// Get the comment of the file
    pub fn comment(&self) -> &str {
        &*self.data.file_comment
    }
    /// Get the compression method used to store the file
    pub fn compression(&self) -> CompressionMethod {
        self.data.compression_method
    }
    /// Get the size of the file in the archive
    pub fn compressed_size(&self) -> u64 {
        self.data.compressed_size
    }
    /// Get the size of the file when uncompressed
    pub fn size(&self) -> u64 {
        self.data.uncompressed_size
    }
    /// Get the time the file was last modified
    pub fn last_modified(&self) -> ::time::Tm {
        self.data.last_modified_time
    }
    /// Get unix mode for the file
    pub fn unix_mode(&self) -> Option<u32> {
        match self.data.system {
            System::Unix => {
                Some(self.data.external_attributes >> 16)
            },
            System::Dos => {
                // Interpret MSDOS directory bit
                let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                    ffi::S_IFDIR | 0o0775
                } else {
                    ffi::S_IFREG | 0o0664
                };
                if 0x01 == (self.data.external_attributes & 0x01) {
                    // Read-only bit; strip write permissions
                    mode &= 0o0555;
                }
                Some(mode)
            },
            _ => None,
        }
    }
    /// Get the CRC32 hash of the original file
    pub fn crc32(&self) -> u32 {
        self.data.crc32
    }

    /// Get the starting offset of the data of the compressed file
    pub fn data_start(&self) -> u64 {
        self.data.data_start
    }
}

impl<'a> Read for ZipFile<'a> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_reader().read(buf)
     }
}
<|endoftext|>


<commit_before>use std::fmt;

#[derive(PartialEq)]
pub struct Clock {
    hours: i32,
    minutes: i32,
}

impl Clock {
    pub fn new(hours: i32, minutes: i32) -> Self {
        return Clock { hours, minutes };
    }

    pub fn add_minutes(&self, minutes: i32) -> Self {
        return Clock::new(self.hours, self.minutes + minutes);
    }
}

impl fmt::Display for Clock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}:{})", self.hours, self.minutes)
    }
}

impl fmt::Debug for Clock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}:{})", self.hours, self.minutes)
    }
}
<commit_msg>Left pad with two zeros<commit_after>use std::fmt;

#[derive(PartialEq)]
pub struct Clock {
    hours: i32,
    minutes: i32,
}

impl Clock {
    pub fn new(hours: i32, minutes: i32) -> Self {
        return Clock { hours, minutes };
    }

    pub fn add_minutes(&self, minutes: i32) -> Self {
        return Clock::new(self.hours, self.minutes + minutes);
    }
}

impl fmt::Display for Clock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02}:{:02}", self.hours, self.minutes)
    }
}

impl fmt::Debug for Clock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02}:{:02}", self.hours, self.minutes)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Alexander Reece
//
// 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.

use std::io;

use inflections::Inflect;
use specs::{Spec, Class};

use CodeGenerator;
use common::{spec_mod_name, frame_type_name};

use common::domain::{Domain, DomainMapper};
use super::MethodModuleWriter;

pub struct SpecModuleWriter<'a> {
    mod_name: String,
    domain_mapper: DomainMapper<'a>,
    spec: &'a Spec,
}

impl<'a> CodeGenerator for SpecModuleWriter<'a> {
    fn write_rust_to<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        if self.spec.classes().is_empty() {
            return Ok(());
        }

        // some nom parsers have an unused variable e
        try!(writeln!(writer, "#![allow(unused_variables)]"));
        try!(writeln!(writer, "\nuse nom::{{IResult, be_u8, be_u16, be_u32, be_u64}};\n"));

        for class in self.spec.classes().values() {
            try!(write!(writer, "// Class {}", class.name()));
            for method in class.methods() {
                let mod_name = format!("{}::{}", self.mod_name, class.name().to_snake_case());
                let method_writer = MethodModuleWriter::new(&mod_name, method, &self.domain_mapper);
                try!(method_writer.write_rust_to(writer));
            }

            //try!(self.write_class_header_parser(class, writer));
            try!(self.write_class_method_parser(class, writer));
        }

        //try!(self.write_spec_header_parser(writer));
        try!(self.write_spec_method_parser(writer));
        try!(self.write_frame_parser(writer));

        Ok(())
    }
}

impl<'a> SpecModuleWriter<'a> {
    pub fn new(spec: &'a Spec) -> Self {
        SpecModuleWriter {
            mod_name: spec_mod_name(spec),
            domain_mapper: DomainMapper::new(spec.domains()),
            spec: spec,
        }
    }

    fn write_frame_parser<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(writeln!(writer, "\n\
            impl<'a> ::NomBytes<'a> for ::primitives::{}::Frame<'a> {{\n\
                type Output = Self;\n\
                fn nom_bytes<'pool, P>(input: &'a [u8], pool: &'pool mut P)  -> IResult<&'a [u8], Self>\n\
                    where P: ::pool::ParserPool\n\
                {{\n\
                    switch!(input, be_u8,\n",
                      self.mod_name
        ));

        let (cycle_parse, cycle_arg) = if self.spec.version().minor() == 8 {
            ("\ncycle: be_u8 >>", "cycle, ")
        } else {
            ("", "")
        };

        let mut has_parent = false;
        for ty in self.spec.frame_types().values() {
            let name = frame_type_name(ty.name());

            // FIXME: implement header parsing
            if name == "Header" || name == "OobHeader" {
                continue;
            }
            if has_parent {
                try!(writeln!(writer, " | // do_parse"))
            }
            else {
                has_parent = true;
            }

            try!(write!(writer, "{} => ", ty.value()));
            try!(writeln!(writer, "do_parse!({}", cycle_parse));

            match name.as_str() {
                "Heartbeat" => {
                    try!(write!(writer,
                                "payload: value!(\n\
                                    ::primitives::{0}::FramePayload::Heartbeat,\n\
                                    tag!(b\"\\x00\\x00\\xCE\")\n\
                                ) >>\
                                channel: value!(0) >>",
                                self.mod_name
                    ));
                },
                enum_name => {
                    try!(write!(writer, "\
                            channel: be_u16 >>\n\
                            payload: "
                    ));

                    let struct_name = match enum_name {
                        "Method" | "OobMethod" => Some("SpecMethod"),
                        "Header" | "OobHeader" => Some("SpecHeader"),
                        _ => None,
                    };

                    if let Some(struct_name) = struct_name {
                        try!(writeln!(writer,
                                      "map!(\n\
                                          length_value!(\n\
                                              be_u32,\n\
                                              call!(<::primitives::{0}::{1} as ::NomBytes>::nom_bytes, pool)\n\
                                          ),\n\
                                          ::primitives::{0}::FramePayload::{2}\n\
                                      ) >> // map",
                                      self.mod_name,
                                      struct_name,
                                      enum_name
                        ));
                    }
                        else {
                            try!(writeln!(
                                writer,
                                "map!(length_bytes!(be_u32), ::primitives::{}::FramePayload::{}) >>",
                                self.mod_name,
                                enum_name
                            ));
                        }
                }
            }

            try!(writeln!(
                writer,
                "(::primitives::{}::Frame::new(channel, {}payload))\n",
                self.mod_name,
                cycle_arg,
            ));
            try!(write!(writer, ")"));
        }

        try!(writeln!(writer, " // do_parse"));
        try!(writeln!(writer, ") // switch!"));
        try!(writeln!(writer, "}} // fn nom_bytes"));
        try!(writeln!(writer, "}} // impl NomBytes for ::primitives::{}::Frame<'a>", self.mod_name));

        Ok(())
    }

    fn write_class_method_parser<W>(&self, class: &Class, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let has_lifetimes = class.methods().iter()
            .flat_map(|method| method.fields())
            .filter(|field| !field.is_reserved())
            .map(|field| Domain::new(self.domain_mapper.map(field.domain())))
            .any(|domain| !domain.is_copy());

        let lifetimes = if has_lifetimes { "<'a>" } else { "" };

        let class_mod_name = class.name().to_snake_case();
        try!(writeln!(writer, "\n\
            impl<'a> ::NomBytes<'a> for ::primitives::{}::{}::ClassMethod{} {{\n\
                type Output = Self;\n\
                fn nom_bytes<'pool, P>(input: &'a [u8], pool: &'pool mut P)  -> IResult<&'a [u8], Self>\n\
                    where P: ::pool::ParserPool\n\
                {{\n\
                    switch!(input, be_u16,\n",
            self.mod_name,
            class_mod_name,
            lifetimes
        ));

        let mut has_parent = false;
        for method in class.methods() {
            if has_parent {
                try!(writeln!(writer, " | // map"))
            }
            else {
                has_parent = true;
            }

            try!(write!(
                writer,
                "{0} => map!(\n\
                    call!(<::primitives::{1}::{2}::{3} as ::NomBytes>::nom_bytes, pool),\n\
                    ::primitives::{1}::{2}::ClassMethod::{3}\n\
                )",
                method.index(),
                self.mod_name,
                class_mod_name,
                method.name().to_pascal_case(),
            ));
        }
        try!(writeln!(writer, " // map!"));
        try!(writeln!(writer, ") // switch!"));
        try!(writeln!(writer, "}} // fn nom_bytes"));
        try!(writeln!(
            writer,
            "}} // impl ::NomBytes for ::primitives::{}::{}::SpecMethod<'a>",
            self.mod_name,
            class_mod_name
        ));

        Ok(())
    }

    pub fn mod_name(&self) -> &str {
        &self.mod_name
    }
}<commit_msg>[parsergen] Add `write_spec_method_parser()`<commit_after>// Copyright 2016 Alexander Reece
//
// 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.

use std::io;

use inflections::Inflect;
use specs::{Spec, Class};

use CodeGenerator;
use common::{spec_mod_name, frame_type_name};

use common::domain::{Domain, DomainMapper};
use super::MethodModuleWriter;

pub struct SpecModuleWriter<'a> {
    mod_name: String,
    domain_mapper: DomainMapper<'a>,
    spec: &'a Spec,
}

impl<'a> CodeGenerator for SpecModuleWriter<'a> {
    fn write_rust_to<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        if self.spec.classes().is_empty() {
            return Ok(());
        }

        // some nom parsers have an unused variable e
        try!(writeln!(writer, "#![allow(unused_variables)]"));
        try!(writeln!(writer, "\nuse nom::{{IResult, be_u8, be_u16, be_u32, be_u64}};\n"));

        for class in self.spec.classes().values() {
            try!(write!(writer, "// Class {}", class.name()));
            for method in class.methods() {
                let mod_name = format!("{}::{}", self.mod_name, class.name().to_snake_case());
                let method_writer = MethodModuleWriter::new(&mod_name, method, &self.domain_mapper);
                try!(method_writer.write_rust_to(writer));
            }

            //try!(self.write_class_header_parser(class, writer));
            try!(self.write_class_method_parser(class, writer));
        }

        //try!(self.write_spec_header_parser(writer));
        try!(self.write_spec_method_parser(writer));
        try!(self.write_frame_parser(writer));

        Ok(())
    }
}

impl<'a> SpecModuleWriter<'a> {
    pub fn new(spec: &'a Spec) -> Self {
        SpecModuleWriter {
            mod_name: spec_mod_name(spec),
            domain_mapper: DomainMapper::new(spec.domains()),
            spec: spec,
        }
    }

    fn write_frame_parser<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(writeln!(writer, "\n\
            impl<'a> ::NomBytes<'a> for ::primitives::{}::Frame<'a> {{\n\
                type Output = Self;\n\
                fn nom_bytes<'pool, P>(input: &'a [u8], pool: &'pool mut P)  -> IResult<&'a [u8], Self>\n\
                    where P: ::pool::ParserPool\n\
                {{\n\
                    switch!(input, be_u8,\n",
                      self.mod_name
        ));

        let (cycle_parse, cycle_arg) = if self.spec.version().minor() == 8 {
            ("\ncycle: be_u8 >>", "cycle, ")
        } else {
            ("", "")
        };

        let mut has_parent = false;
        for ty in self.spec.frame_types().values() {
            let name = frame_type_name(ty.name());

            // FIXME: implement header parsing
            if name == "Header" || name == "OobHeader" {
                continue;
            }
            if has_parent {
                try!(writeln!(writer, " | // do_parse"))
            }
            else {
                has_parent = true;
            }

            try!(write!(writer, "{} => ", ty.value()));
            try!(writeln!(writer, "do_parse!({}", cycle_parse));

            match name.as_str() {
                "Heartbeat" => {
                    try!(write!(writer,
                                "payload: value!(\n\
                                    ::primitives::{0}::FramePayload::Heartbeat,\n\
                                    tag!(b\"\\x00\\x00\\xCE\")\n\
                                ) >>\
                                channel: value!(0) >>",
                                self.mod_name
                    ));
                },
                enum_name => {
                    try!(write!(writer, "\
                            channel: be_u16 >>\n\
                            payload: "
                    ));

                    let struct_name = match enum_name {
                        "Method" | "OobMethod" => Some("SpecMethod"),
                        "Header" | "OobHeader" => Some("SpecHeader"),
                        _ => None,
                    };

                    if let Some(struct_name) = struct_name {
                        try!(writeln!(writer,
                                      "map!(\n\
                                          length_value!(\n\
                                              be_u32,\n\
                                              call!(<::primitives::{0}::{1} as ::NomBytes>::nom_bytes, pool)\n\
                                          ),\n\
                                          ::primitives::{0}::FramePayload::{2}\n\
                                      ) >> // map",
                                      self.mod_name,
                                      struct_name,
                                      enum_name
                        ));
                    }
                        else {
                            try!(writeln!(
                                writer,
                                "map!(length_bytes!(be_u32), ::primitives::{}::FramePayload::{}) >>",
                                self.mod_name,
                                enum_name
                            ));
                        }
                }
            }

            try!(writeln!(
                writer,
                "(::primitives::{}::Frame::new(channel, {}payload))\n",
                self.mod_name,
                cycle_arg,
            ));
            try!(write!(writer, ")"));
        }

        try!(writeln!(writer, " // do_parse"));
        try!(writeln!(writer, ") // switch!"));
        try!(writeln!(writer, "}} // fn nom_bytes"));
        try!(writeln!(writer, "}} // impl NomBytes for ::primitives::{}::Frame<'a>", self.mod_name));

        Ok(())
    }

    fn write_spec_method_parser<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(writeln!(writer, "\n\
            impl<'a> ::NomBytes<'a> for ::primitives::{}::SpecMethod<'a> {{\n\
                type Output = Self;\n\
                fn nom_bytes<'pool, P>(input: &'a [u8], pool: &'pool mut P)  -> IResult<&'a [u8], Self>\n\
                    where P: ::pool::ParserPool\n\
                {{\n\
                    switch!(input, be_u16,\n",
            self.mod_name
        ));

        let mut has_parent = false;
        for class in self.spec.classes().values() {
            if has_parent {
                try!(writeln!(writer, " | // map"))
            }
            else {
                has_parent = true;
            }

            try!(write!(
                writer,
                "{0} => map!(\n\
                    call!(<::primitives::{1}::{2}Method as ::NomBytes>::nom_bytes, pool),\n\
                    ::primitives::{1}::SpecMethod::{2}\n\
                )",
                class.index(),
                self.mod_name,
                class.name().to_pascal_case(),
            ));
        }
        try!(writeln!(writer, " // map!"));
        try!(writeln!(writer, ") // switch!"));
        try!(writeln!(writer, "}} // fn nom_bytes"));
        try!(writeln!(writer, "}} // impl ::NomBytes for ::primitives::{}::SpecMethod<'a>", self.mod_name));

        Ok(())
    }
    fn write_class_method_parser<W>(&self, class: &Class, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let has_lifetimes = class.methods().iter()
            .flat_map(|method| method.fields())
            .filter(|field| !field.is_reserved())
            .map(|field| Domain::new(self.domain_mapper.map(field.domain())))
            .any(|domain| !domain.is_copy());

        let lifetimes = if has_lifetimes { "<'a>" } else { "" };

        let class_mod_name = class.name().to_snake_case();
        try!(writeln!(writer, "\n\
            impl<'a> ::NomBytes<'a> for ::primitives::{}::{}::ClassMethod{} {{\n\
                type Output = Self;\n\
                fn nom_bytes<'pool, P>(input: &'a [u8], pool: &'pool mut P)  -> IResult<&'a [u8], Self>\n\
                    where P: ::pool::ParserPool\n\
                {{\n\
                    switch!(input, be_u16,\n",
            self.mod_name,
            class_mod_name,
            lifetimes
        ));

        let mut has_parent = false;
        for method in class.methods() {
            if has_parent {
                try!(writeln!(writer, " | // map"))
            }
            else {
                has_parent = true;
            }

            try!(write!(
                writer,
                "{0} => map!(\n\
                    call!(<::primitives::{1}::{2}::{3} as ::NomBytes>::nom_bytes, pool),\n\
                    ::primitives::{1}::{2}::ClassMethod::{3}\n\
                )",
                method.index(),
                self.mod_name,
                class_mod_name,
                method.name().to_pascal_case(),
            ));
        }
        try!(writeln!(writer, " // map!"));
        try!(writeln!(writer, ") // switch!"));
        try!(writeln!(writer, "}} // fn nom_bytes"));
        try!(writeln!(
            writer,
            "}} // impl ::NomBytes for ::primitives::{}::{}::SpecMethod<'a>",
            self.mod_name,
            class_mod_name
        ));

        Ok(())
    }

    pub fn mod_name(&self) -> &str {
        &self.mod_name
    }
}<|endoftext|>


<commit_before>use std::os::unix::io::RawFd;

use std;
use libc;
use nix;
use nix::errno::Errno;
use nix::unistd::{read, write};
use nix::fcntl::{flock, FlockArg};
use nix::sys::termios;
use nix::sys::termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON, OPOST, CS8, ECHO, ICANON, IEXTEN, ISIG, VMIN, VTIME};

use error::Error;

pub struct RawMode {
    fd: RawFd,
    original_termios: termios::Termios
}

impl RawMode {

    fn acquire(fd: RawFd) -> Result<RawMode, nix::Error> {
        try!(flock(fd, FlockArg::LockExclusive));

        let original_termios = try!(termios::tcgetattr(fd));

        let mut raw = original_termios;
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        raw.c_oflag = raw.c_oflag & !(OPOST);
        raw.c_cflag = raw.c_cflag | (CS8);
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1;
        raw.c_cc[VTIME] = 0;

        try!(termios::tcsetattr(fd, termios::TCSAFLUSH, &raw));

        Ok(RawMode{
            fd: fd,
            original_termios: original_termios
        })
    }

    pub fn write(&mut self, bytes: &[u8]) -> Result<usize, nix::Error> {
        write(self.fd, bytes)
    }

}

impl Drop for RawMode {
    fn drop(&mut self) {
        let _ = termios::tcsetattr(self.fd, termios::TCSAFLUSH, &self.original_termios);
        let _ = flock(self.fd, FlockArg::Unlock);
    }
}

pub struct Term {
    in_fd: RawFd,
    out_fd: RawFd
}

static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb","cons25","emacs"];

impl Term {

    pub fn new(in_fd: RawFd, out_fd: RawFd) -> Term {
        Term {
            in_fd: in_fd,
            out_fd: out_fd
        }
    }

    pub fn is_unsupported_term() -> bool {
        match std::env::var("TERM") {
            Ok(term) => {
                let mut unsupported = false;
                for iter in &UNSUPPORTED_TERM {
                    unsupported = term == *iter
                }
                unsupported
            }
            Err(_) => false
        }
    }

    pub fn is_a_tty(&self) -> bool {
        unsafe { libc::isatty(self.out_fd) != 0 }
    }

    pub fn acquire_raw_mode(&self) -> Result<RawMode, Error> {
        if !self.is_a_tty() {
            return Err(Error::from(nix::Error::from_errno(Errno::ENOTTY)));
        }
        RawMode::acquire(self.out_fd).map_err(Error::from)
    }

    pub fn read_byte(&mut self) -> Result<Option<u8>, nix::Error> {
        let mut input: [u8; 1] = [0; 1];
        let n = try!(read(self.in_fd, &mut input));
        if n == 0 {
            return Ok(None);
        }
        Ok(Some(input[0]))
    }

}
<commit_msg>fix unsupported detection<commit_after>use std::os::unix::io::RawFd;

use std;
use libc;
use nix;
use nix::errno::Errno;
use nix::unistd::{read, write};
use nix::fcntl::{flock, FlockArg};
use nix::sys::termios;
use nix::sys::termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON, OPOST, CS8, ECHO, ICANON, IEXTEN, ISIG, VMIN, VTIME};

use error::Error;

pub struct RawMode {
    fd: RawFd,
    original_termios: termios::Termios
}

impl RawMode {

    fn acquire(fd: RawFd) -> Result<RawMode, nix::Error> {
        try!(flock(fd, FlockArg::LockExclusive));

        let original_termios = try!(termios::tcgetattr(fd));

        let mut raw = original_termios;
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        raw.c_oflag = raw.c_oflag & !(OPOST);
        raw.c_cflag = raw.c_cflag | (CS8);
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1;
        raw.c_cc[VTIME] = 0;

        try!(termios::tcsetattr(fd, termios::TCSAFLUSH, &raw));

        Ok(RawMode{
            fd: fd,
            original_termios: original_termios
        })
    }

    pub fn write(&mut self, bytes: &[u8]) -> Result<usize, nix::Error> {
        write(self.fd, bytes)
    }

}

impl Drop for RawMode {
    fn drop(&mut self) {
        let _ = termios::tcsetattr(self.fd, termios::TCSAFLUSH, &self.original_termios);
        let _ = flock(self.fd, FlockArg::Unlock);
    }
}

pub struct Term {
    in_fd: RawFd,
    out_fd: RawFd
}

static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb","cons25","emacs"];

impl Term {

    pub fn new(in_fd: RawFd, out_fd: RawFd) -> Term {
        Term {
            in_fd: in_fd,
            out_fd: out_fd
        }
    }

    pub fn is_unsupported_term() -> bool {
        match std::env::var("TERM") {
            Ok(term) => {
                UNSUPPORTED_TERM.iter().find(|x| &&term == x).is_some()
            }
            Err(_) => false
        }
    }

    pub fn is_a_tty(&self) -> bool {
        unsafe { libc::isatty(self.out_fd) != 0 }
    }

    pub fn acquire_raw_mode(&self) -> Result<RawMode, Error> {
        if !self.is_a_tty() {
            return Err(Error::from(nix::Error::from_errno(Errno::ENOTTY)));
        }
        RawMode::acquire(self.out_fd).map_err(Error::from)
    }

    pub fn read_byte(&mut self) -> Result<Option<u8>, nix::Error> {
        let mut input: [u8; 1] = [0; 1];
        let n = try!(read(self.in_fd, &mut input));
        if n == 0 {
            return Ok(None);
        }
        Ok(Some(input[0]))
    }

}
<|endoftext|>


<commit_before>use std::io::Read;
use std::sync::{Once, ONCE_INIT};

use failure::Error;
use serde_json::{Map, Value};

/// Maximum number of bytes that can be allocated when decoding
/// Avro-encoded values. This is a protection against ill-formed
/// data, whose length field might be interpreted as enourmous.
/// See max_allocation_bytes to change this limit.
pub static mut MAX_ALLOCATION_BYTES: usize = 512 * 1024 * 1024;
static MAX_ALLOCATION_BYTES_ONCE: Once = ONCE_INIT;

/// Describes errors happened trying to allocate too many bytes
#[derive(Fail, Debug)]
#[fail(display = "Allocation error: {}", _0)]
pub struct AllocationError(String);

impl AllocationError {
    pub fn new<S>(msg: S) -> AllocationError
    where
        S: Into<String>,
    {
        AllocationError(msg.into())
    }
}

/// Describes errors happened while decoding Avro data.
#[derive(Fail, Debug)]
#[fail(display = "Decoding error: {}", _0)]
pub struct DecodeError(String);

impl DecodeError {
    pub fn new<S>(msg: S) -> DecodeError
    where
        S: Into<String>,
    {
        DecodeError(msg.into())
    }
}

pub trait MapHelper {
    fn string(&self, key: &str) -> Option<String>;

    fn name(&self) -> Option<String> {
        self.string("name")
    }

    fn doc(&self) -> Option<String> {
        self.string("doc")
    }
}

impl MapHelper for Map<String, Value> {
    fn string(&self, key: &str) -> Option<String> {
        self.get(key)
            .and_then(|v| v.as_str())
            .map(|v| v.to_string())
    }
}

pub fn read_long<R: Read>(reader: &mut R) -> Result<i64, Error> {
    zag_i64(reader)
}

pub fn zig_i32(n: i32, buffer: &mut Vec<u8>) {
    encode_variable(i64::from((n << 1) ^ (n >> 31)), buffer)
}

pub fn zig_i64(n: i64, buffer: &mut Vec<u8>) {
    encode_variable((n << 1) ^ (n >> 31), buffer)
}

pub fn zag_i32<R: Read>(reader: &mut R) -> Result<i32, Error> {
    let i = zag_i64(reader)?;
    if i < i64::from(i32::min_value()) || i > i64::from(i32::max_value()) {
        Err(DecodeError::new("int out of range").into())
    } else {
        Ok(i as i32)
    }
}

pub fn zag_i64<R: Read>(reader: &mut R) -> Result<i64, Error> {
    let z = decode_variable(reader)?;
    Ok(if z & 0x1 == 0 {
        (z >> 1) as i64
    } else {
        !(z >> 1) as i64
    })
}

fn encode_variable(mut z: i64, buffer: &mut Vec<u8>) {
    loop {
        if z <= 0x7F {
            buffer.push((z & 0x7F) as u8);
            break
        } else {
            buffer.push((0x80 | (z & 0x7F)) as u8);
            z >>= 7;
        }
    }
}

fn decode_variable<R: Read>(reader: &mut R) -> Result<u64, Error> {
    let mut i = 0u64;
    let mut buf = [0u8; 1];

    let mut j = 0;
    loop {
        if j > 9 {
            // if j * 7 > 64
            return Err(DecodeError::new("Overflow when decoding integer value").into())
        }
        reader.read_exact(&mut buf[..])?;
        i |= (u64::from(buf[0] & 0x7F)) << (j * 7);
        if (buf[0] >> 7) == 0 {
            break
        } else {
            j += 1;
        }
    }

    Ok(i)
}

/// Set a new maximum number of bytes that can be allocated when decoding data.
/// Once called, the limit cannot be changed.
///
/// **NOTE** This function must be called before decoding **any** data. The
/// library leverages [`std::sync::Once`](https://doc.rust-lang.org/std/sync/struct.Once.html)
/// to set the limit either when calling this method, or when decoding for
/// the first time.
pub fn max_allocation_bytes(num_bytes: usize) -> usize {
    unsafe {
        MAX_ALLOCATION_BYTES_ONCE.call_once(|| {
            MAX_ALLOCATION_BYTES = num_bytes;
        });
        MAX_ALLOCATION_BYTES
    }
}

pub fn safe_len(len: usize) -> Result<usize, Error> {
    let max_bytes = max_allocation_bytes(512 * 1024 * 1024);

    if len <= max_bytes {
        Ok(len)
    } else {
        Err(AllocationError::new(format!(
            "Unable to allocate {} bytes (Maximum allowed: {})",
            len, max_bytes
        )).into())
    }
}

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

    #[test]
    fn test_zigzag() {
        let mut a = Vec::new();
        let mut b = Vec::new();
        zig_i32(42i32, &mut a);
        zig_i64(42i64, &mut b);
        assert_eq!(a, b);
    }

    #[test]
    fn test_overflow() {
        let causes_left_shift_overflow: &[u8] = &[0xe1, 0xe1, 0xe1, 0xe1, 0xe1];
        assert!(decode_variable(&mut &causes_left_shift_overflow[..]).is_err());
    }

    #[test]
    fn test_safe_len() {
        assert_eq!(42usize, safe_len(42usize).unwrap());
        assert!(safe_len(1024 * 1024 * 1024).is_err());
    }
}
<commit_msg>Shift 64-bit integers by sixty-three bits<commit_after>use std::io::Read;
use std::sync::{Once, ONCE_INIT};

use failure::Error;
use serde_json::{Map, Value};

/// Maximum number of bytes that can be allocated when decoding
/// Avro-encoded values. This is a protection against ill-formed
/// data, whose length field might be interpreted as enourmous.
/// See max_allocation_bytes to change this limit.
pub static mut MAX_ALLOCATION_BYTES: usize = 512 * 1024 * 1024;
static MAX_ALLOCATION_BYTES_ONCE: Once = ONCE_INIT;

/// Describes errors happened trying to allocate too many bytes
#[derive(Fail, Debug)]
#[fail(display = "Allocation error: {}", _0)]
pub struct AllocationError(String);

impl AllocationError {
    pub fn new<S>(msg: S) -> AllocationError
    where
        S: Into<String>,
    {
        AllocationError(msg.into())
    }
}

/// Describes errors happened while decoding Avro data.
#[derive(Fail, Debug)]
#[fail(display = "Decoding error: {}", _0)]
pub struct DecodeError(String);

impl DecodeError {
    pub fn new<S>(msg: S) -> DecodeError
    where
        S: Into<String>,
    {
        DecodeError(msg.into())
    }
}

pub trait MapHelper {
    fn string(&self, key: &str) -> Option<String>;

    fn name(&self) -> Option<String> {
        self.string("name")
    }

    fn doc(&self) -> Option<String> {
        self.string("doc")
    }
}

impl MapHelper for Map<String, Value> {
    fn string(&self, key: &str) -> Option<String> {
        self.get(key)
            .and_then(|v| v.as_str())
            .map(|v| v.to_string())
    }
}

pub fn read_long<R: Read>(reader: &mut R) -> Result<i64, Error> {
    zag_i64(reader)
}

pub fn zig_i32(n: i32, buffer: &mut Vec<u8>) {
    encode_variable(i64::from((n << 1) ^ (n >> 31)), buffer)
}

pub fn zig_i64(n: i64, buffer: &mut Vec<u8>) {
    encode_variable((n << 1) ^ (n >> 63), buffer)
}

pub fn zag_i32<R: Read>(reader: &mut R) -> Result<i32, Error> {
    let i = zag_i64(reader)?;
    if i < i64::from(i32::min_value()) || i > i64::from(i32::max_value()) {
        Err(DecodeError::new("int out of range").into())
    } else {
        Ok(i as i32)
    }
}

pub fn zag_i64<R: Read>(reader: &mut R) -> Result<i64, Error> {
    let z = decode_variable(reader)?;
    Ok(if z & 0x1 == 0 {
        (z >> 1) as i64
    } else {
        !(z >> 1) as i64
    })
}

fn encode_variable(mut z: i64, buffer: &mut Vec<u8>) {
    loop {
        if z <= 0x7F {
            buffer.push((z & 0x7F) as u8);
            break
        } else {
            buffer.push((0x80 | (z & 0x7F)) as u8);
            z >>= 7;
        }
    }
}

fn decode_variable<R: Read>(reader: &mut R) -> Result<u64, Error> {
    let mut i = 0u64;
    let mut buf = [0u8; 1];

    let mut j = 0;
    loop {
        if j > 9 {
            // if j * 7 > 64
            return Err(DecodeError::new("Overflow when decoding integer value").into())
        }
        reader.read_exact(&mut buf[..])?;
        i |= (u64::from(buf[0] & 0x7F)) << (j * 7);
        if (buf[0] >> 7) == 0 {
            break
        } else {
            j += 1;
        }
    }

    Ok(i)
}

/// Set a new maximum number of bytes that can be allocated when decoding data.
/// Once called, the limit cannot be changed.
///
/// **NOTE** This function must be called before decoding **any** data. The
/// library leverages [`std::sync::Once`](https://doc.rust-lang.org/std/sync/struct.Once.html)
/// to set the limit either when calling this method, or when decoding for
/// the first time.
pub fn max_allocation_bytes(num_bytes: usize) -> usize {
    unsafe {
        MAX_ALLOCATION_BYTES_ONCE.call_once(|| {
            MAX_ALLOCATION_BYTES = num_bytes;
        });
        MAX_ALLOCATION_BYTES
    }
}

pub fn safe_len(len: usize) -> Result<usize, Error> {
    let max_bytes = max_allocation_bytes(512 * 1024 * 1024);

    if len <= max_bytes {
        Ok(len)
    } else {
        Err(AllocationError::new(format!(
            "Unable to allocate {} bytes (Maximum allowed: {})",
            len, max_bytes
        )).into())
    }
}

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

    #[test]
    fn test_zigzag() {
        let mut a = Vec::new();
        let mut b = Vec::new();
        zig_i32(42i32, &mut a);
        zig_i64(42i64, &mut b);
        assert_eq!(a, b);
    }

    #[test]
    fn test_zig_i64() {
        let mut s = Vec::new();
        zig_i64(2147483647i64, &mut s);
        assert_eq!(s, [254, 255, 255, 255, 15]);

        s.clear();
        zig_i64(2147483648i64, &mut s);
        assert_eq!(s, [128, 128, 128, 128, 16]);

        s.clear();
        zig_i64(-2147483648i64, &mut s);
        assert_eq!(s, [255, 255, 255, 255, 15]);

        s.clear();
        zig_i64(-2147483649i64, &mut s);
        assert_eq!(s, [129, 128, 128, 128, 16]);
    }

    #[test]
    fn test_overflow() {
        let causes_left_shift_overflow: &[u8] = &[0xe1, 0xe1, 0xe1, 0xe1, 0xe1];
        assert!(decode_variable(&mut &causes_left_shift_overflow[..]).is_err());
    }

    #[test]
    fn test_safe_len() {
        assert_eq!(42usize, safe_len(42usize).unwrap());
        assert!(safe_len(1024 * 1024 * 1024).is_err());
    }
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use ncurses::*;
use view_data::ViewData;
use message::Message;
use channel::Channel;

pub struct View {
    messages: WINDOW,
    input: WINDOW,
    unread: WINDOW,
    view_data: Option<ViewData>
}

impl View {
    pub fn new() -> View {
        initscr();

        let mut max_y = 0;
        let mut max_x = 0;
        getmaxyx(stdscr, &mut max_y, &mut max_x);

        let messages = init_messages(max_y, max_x);
        let input = init_input(max_y, max_x);
        let unread = init_unread(max_y, max_y);

        View {
            messages: messages,
            input: input,
            unread: unread,
            view_data: None
        }
    }

    // Rather have ncurses call a callback
    // instead of a receiver to cut the dependency
    // but couldn't get it to work
    pub fn init(&mut self, on_input: Box<Fn(String, String)>, view_data_rx: mpsc::Receiver<ViewData>) {
        self.draw_prompt();
        let mut input = String::new();
        loop {
            match view_data_rx.try_recv() {
                Ok(view_data) => {
                    self.view_data = Some(view_data);
                    self.naive_redraw();
                    self.draw_prompt();
                }
                _ => ()
            }

            let ch = wgetch(self.input);
            if ch == ERR { continue };
            if ch != '\n' as i32 && ch != '\r' as i32 {
                // TODO This might send wrong keys
                unsafe { input.as_mut_vec().push(ch as u8); }
                mvwprintw(self.input, 1, (self.current_prompt().len() + 1) as i32, &input);
                wrefresh(self.input);
            } else {
                on_input(input, self.current_channel().id.clone());
                input = String::new();
                self.draw_prompt();
            }
        }
        // Doesn't work
        // mvwin(self.messages,1,1); // Padding for messages
    }

    pub fn print_message(&self, message: &Message) {
        let string = format!("{}", message) + "\n";
        wprintw(self.messages, &string);
    }

    fn current_channel(&self) -> &Channel {
       &self.view_data
           .as_ref()
           .expect("Trying to get current channel without viewdata")
           .channel
    }

    fn draw_prompt(&self) {
        wclear(self.input);
        let top = 0 as chtype;
        let bottom = ' ' as chtype;
        let empty = ' ' as chtype;
        wborder(self.input, empty,empty,top,bottom,empty,empty,empty,empty);
        let promptname: &str = &self.current_prompt();
        mvwprintw(self.input, 1, 1, promptname);
        wmove(self.input, 1, (promptname.len() + 1) as i32); // Move physical cursor to prompt start
        wrefresh(self.input);
    }

    fn draw_unread(&self) {
        wclear(self.unread);
        let channels = &self.view_data.as_ref().unwrap().unread_channels;
        attron(A_BOLD());
        wmove(self.unread, 0, 1);
        for channel in channels {
            wprintw(self.unread, &format!("#{}", channel.name));
            wmove(self.unread, 0, (channel.name.len() + 2) as i32);
        }
        attroff(A_BOLD());
        wrefresh(self.unread);
    }

    fn current_prompt(&self) -> String {
        if self.view_data.is_none() { return "> ".to_string() }

        let name = &self.current_channel().name;
        format!("#{} > ", name)
    }

    // Naively redraws the whole UI by clearing all data
    fn naive_redraw(&self) {
        if self.view_data.is_none() { return };

        self.draw_unread();

        wclear(self.messages);

        let view_data = self.view_data.as_ref().unwrap();
        for message in view_data.messages.iter() {
            self.print_message(message)
        }

        wrefresh(self.messages);
    }

}

impl Drop for View {
    fn drop(&mut self) {
        endwin();
    }
}

fn init_messages(max_y: i32, max_x: i32) -> WINDOW {
    let win = newwin(max_y - 3, max_x, 0, 0);
    let top = ' ' as chtype;
    let bottom = ' ' as chtype;
    let empty = ' ' as chtype;
    wborder(win, empty,empty,top,bottom,empty,empty,empty,empty);
    // box_(win, 0, 0);
    scrollok(win, true);
    wrefresh(win);
    win
}

fn init_input(max_y: i32, max_x: i32) -> WINDOW {
    let win = newwin(2, max_x, max_y - 2, 0);
    // nodelay(win, true);
    // box_(win, 0, 0);
    halfdelay(1); // Dirty fix for cpu cycles, needs better fix
    // cbreak()
    wrefresh(win);
    win
}

fn init_unread(max_y: i32, max_x: i32) -> WINDOW {
    let win = newwin(1, max_x, max_y - 3, 0);
    wrefresh(win);
    win
}

<commit_msg>Implement backspace<commit_after>use std::sync::mpsc;
use ncurses::*;
use view_data::ViewData;
use message::Message;
use channel::Channel;

const LINE_BREAK: i32 = '\n' as i32;
const BACKSPACE: i32 = 127; // ncurses KEY_BACKSPACE not working?

pub struct View {
    messages: WINDOW,
    input: WINDOW,
    unread: WINDOW,
    view_data: Option<ViewData>
}

impl View {
    pub fn new() -> View {
        initscr();

        let mut max_y = 0;
        let mut max_x = 0;
        getmaxyx(stdscr, &mut max_y, &mut max_x);

        let messages = init_messages(max_y, max_x);
        let input = init_input(max_y, max_x);
        let unread = init_unread(max_y, max_y);

        View {
            messages: messages,
            input: input,
            unread: unread,
            view_data: None
        }
    }

    // Rather have ncurses call a callback
    // instead of a receiver to cut the dependency
    // but couldn't get it to work
    pub fn init(&mut self, on_input: Box<Fn(String, String)>, view_data_rx: mpsc::Receiver<ViewData>) {
        self.draw_prompt();
        let mut buffer = String::new();
        loop {
            match view_data_rx.try_recv() {
                Ok(view_data) => {
                    self.view_data = Some(view_data);
                    self.naive_redraw();
                    self.draw_prompt();
                }
                _ => ()
            }

            match wgetch(self.input) {
                ERR => continue,

                LINE_BREAK  => {
                    on_input(buffer, self.current_channel().id.clone());
                    buffer = String::new();
                    wclear(self.input);
                    self.draw_prompt();
                },

                BACKSPACE => {
                    if buffer.len() > 0 {
                        buffer.pop();
                        // Just redraw the whole thing to avoid crap
                    }
                    // for some reason backspaces get rendered regardles
                    // of what this code does. So clear screen.
                    wclear(self.input);
                    self.draw_prompt();
                    mvwprintw(self.input, 1, (self.current_prompt().len() + 1) as i32, &buffer);
                    wrefresh(self.input);
                },

                ch @ _ => {
                    // TODO why unsafe?
                    unsafe { buffer.as_mut_vec().push(ch as u8); }
                    // Just redraw the whole thing to avoid crap
                    wclear(self.input);
                    self.draw_prompt();
                    mvwprintw(self.input, 1, (self.current_prompt().len() + 1) as i32, &buffer);
                    wrefresh(self.input);
                }
            }
        }
    }

    pub fn print_message(&self, message: &Message) {
        let string = format!("{}", message) + "\n";
        wprintw(self.messages, &string);
    }

    fn current_channel(&self) -> &Channel {
       &self.view_data
           .as_ref()
           .expect("Trying to get current channel without viewdata")
           .channel
    }

    fn draw_prompt(&self) {
        let top = 0 as chtype;
        let bottom = ' ' as chtype;
        let empty = ' ' as chtype;
        wborder(self.input, empty,empty,top,bottom,empty,empty,empty,empty);
        let promptname: &str = &self.current_prompt();
        mvwprintw(self.input, 1, 1, promptname);
        wmove(self.input, 1, (promptname.len() + 1) as i32); // Move physical cursor to prompt start
        wrefresh(self.input);
    }

    fn draw_unread(&self) {
        wclear(self.unread);
        let channels = &self.view_data.as_ref().unwrap().unread_channels;
        attron(A_BOLD());
        wmove(self.unread, 0, 1);
        for channel in channels {
            wprintw(self.unread, &format!("#{}", channel.name));
            wmove(self.unread, 0, (channel.name.len() + 2) as i32);
        }
        attroff(A_BOLD());
        wrefresh(self.unread);
    }

    fn current_prompt(&self) -> String {
        if self.view_data.is_none() { return "> ".to_string() }

        let name = &self.current_channel().name;
        format!("#{} > ", name)
    }

    // Naively redraws the whole UI by clearing all data
    fn naive_redraw(&self) {
        if self.view_data.is_none() { return };

        self.draw_unread();

        wclear(self.messages);

        let view_data = self.view_data.as_ref().unwrap();
        for message in view_data.messages.iter() {
            self.print_message(message)
        }

        wrefresh(self.messages);
    }

}

impl Drop for View {
    fn drop(&mut self) {
        endwin();
    }
}

fn init_messages(max_y: i32, max_x: i32) -> WINDOW {
    let win = newwin(max_y - 3, max_x, 0, 0);
    let top = ' ' as chtype;
    let bottom = ' ' as chtype;
    let empty = ' ' as chtype;
    wborder(win, empty,empty,top,bottom,empty,empty,empty,empty);
    // box_(win, 0, 0);
    scrollok(win, true);
    wrefresh(win);
    win
}

fn init_input(max_y: i32, max_x: i32) -> WINDOW {
    let win = newwin(2, max_x, max_y - 2, 0);
    // nodelay(win, true);
    // box_(win, 0, 0);
    halfdelay(1); // Dirty fix for cpu cycles, needs better fix
    // cbreak()
    wrefresh(win);
    win
}

fn init_unread(max_y: i32, max_x: i32) -> WINDOW {
    let win = newwin(1, max_x, max_y - 3, 0);
    wrefresh(win);
    win
}

<|endoftext|>


<commit_before>use grid;
use pancurses;
use pancurses::{Window, Attribute, Attributes, ColorPair, ToChtype};
use sprite;
use sprite::Sprite;
use geometry::*;
use util::pick;
use world::World;
use log::Log;

fn cell(sprite: Sprite) -> pancurses::chtype {
    let attributes = match *pick(sprite.color) {
        sprite::Color::Navy   => (Attributes::new() | ColorPair(1)),
        sprite::Color::Green  => (Attributes::new() | ColorPair(2)),
        sprite::Color::Teal   => (Attributes::new() | ColorPair(3)),
        sprite::Color::Maroon => (Attributes::new() | ColorPair(4)),
        sprite::Color::Purple => (Attributes::new() | ColorPair(5)),
        sprite::Color::Brown  => (Attributes::new() | ColorPair(6)),
        sprite::Color::Gray   => (Attributes::new() | ColorPair(0)),
        sprite::Color::Dark   => (Attribute::Bold   | ColorPair(7)),
        sprite::Color::Blue   => (Attribute::Bold   | ColorPair(1)),
        sprite::Color::Lime   => (Attribute::Bold   | ColorPair(2)),
        sprite::Color::Aqua   => (Attribute::Bold   | ColorPair(3)),
        sprite::Color::Red    => (Attribute::Bold   | ColorPair(4)),
        sprite::Color::Pink   => (Attribute::Bold   | ColorPair(5)),
        sprite::Color::Yellow => (Attribute::Bold   | ColorPair(6)),
        sprite::Color::White  => (Attribute::Bold   | ColorPair(0)),
    };

    sprite.character.to_chtype() | pancurses::chtype::from(attributes)
}

pub fn initialize() -> Window {
    let terminal = pancurses::initscr();
    pancurses::cbreak();
    pancurses::noecho();
    pancurses::curs_set(0);

    pancurses::start_color();
    pancurses::init_pair(0, pancurses::COLOR_WHITE,   pancurses::COLOR_BLACK);
    pancurses::init_pair(1, pancurses::COLOR_BLUE,    pancurses::COLOR_BLACK);
    pancurses::init_pair(2, pancurses::COLOR_GREEN,   pancurses::COLOR_BLACK);
    pancurses::init_pair(3, pancurses::COLOR_CYAN,    pancurses::COLOR_BLACK);
    pancurses::init_pair(4, pancurses::COLOR_RED,     pancurses::COLOR_BLACK);
    pancurses::init_pair(5, pancurses::COLOR_MAGENTA, pancurses::COLOR_BLACK);
    pancurses::init_pair(6, pancurses::COLOR_YELLOW,  pancurses::COLOR_BLACK);
    pancurses::init_pair(7, pancurses::COLOR_BLACK,   pancurses::COLOR_BLACK);

    // NOTE: only used for drawing border; remove if we end up not needing this
    pancurses::init_pair(8, pancurses::COLOR_BLUE, pancurses::COLOR_BLUE);

    terminal
}

// TODO: write an actual dang view

pub fn draw(term: &Window, world: &World) {
    term.erase(); // clear back-buffer

    // draw border (NOTE: remove color pair 8 if we remove this)
    // TODO: depend on terminal size; don't hardcode lengths/alignments
    let blue = '#'.to_chtype() | pancurses::chtype::from(ColorPair(8));
    for position in grid::RECTANGLE.grow(1) {
        let Point(col, row) = position;
        term.mvaddch(row + 4, col + 30, blue);
    }

    draw_board(term, &world);

    draw_messages(term, &world.log);

    term.refresh();
}

fn draw_board(term: &Window, world: &World) {
    // TODO: depend on terminal size; don't hardcode lengths/alignments
    let level = world.player.current_level(&world.dungeon);
    for row in 0..grid::HEIGHT as i32 {
        term.mv(row + 4, 30);
        for col in 0..grid::WIDTH as i32 {
            let position = Point(col, row);
            let sprite =
                if level.known_tiles.contains(&position) {
                    let visible = world.player.visible.contains(&position);
                    level.sprite_at(position, &world).darken(!visible)
                } else {
                    sprite::HIDDEN
                };
            term.addch(cell(sprite));
        }
    }
}

fn draw_messages(term: &Window, log: &Log) {
    // TODO: depend on terminal size; don't hardcode lengths/alignments;
    //       visually group messages by turn (underscore separators? color?);
    //       maybe group similar messages; add linewrapping if needed
    for (i, &(_, ref message)) in log.recent_messages().iter().take(6).enumerate() {
        term.mvaddstr(23 - i as i32, 0, &message);
    }
}
<commit_msg>inventory display<commit_after>use grid;
use pancurses;
use pancurses::{Window, Attribute, Attributes, ColorPair, ToChtype};
use sprite;
use sprite::{Sprite, Color};
use geometry::*;
use util::pick;
use world::World;
use log::Log;
use item::Inventory;

fn color(color: Color) -> Attributes {
    match color {
        Color::Navy   => (Attributes::new() | ColorPair(1)),
        Color::Green  => (Attributes::new() | ColorPair(2)),
        Color::Teal   => (Attributes::new() | ColorPair(3)),
        Color::Maroon => (Attributes::new() | ColorPair(4)),
        Color::Purple => (Attributes::new() | ColorPair(5)),
        Color::Brown  => (Attributes::new() | ColorPair(6)),
        Color::Gray   => (Attributes::new() | ColorPair(0)),
        Color::Dark   => (Attribute::Bold   | ColorPair(7)),
        Color::Blue   => (Attribute::Bold   | ColorPair(1)),
        Color::Lime   => (Attribute::Bold   | ColorPair(2)),
        Color::Aqua   => (Attribute::Bold   | ColorPair(3)),
        Color::Red    => (Attribute::Bold   | ColorPair(4)),
        Color::Pink   => (Attribute::Bold   | ColorPair(5)),
        Color::Yellow => (Attribute::Bold   | ColorPair(6)),
        Color::White  => (Attribute::Bold   | ColorPair(0)),
    }
}

fn cell(sprite: Sprite) -> pancurses::chtype {
    sprite.character.to_chtype() | pancurses::chtype::from(color(*pick(sprite.color)))
}

pub fn initialize() -> Window {
    let terminal = pancurses::initscr();
    pancurses::cbreak();
    pancurses::noecho();
    pancurses::curs_set(0);

    pancurses::start_color();
    pancurses::init_pair(0, pancurses::COLOR_WHITE,   pancurses::COLOR_BLACK);
    pancurses::init_pair(1, pancurses::COLOR_BLUE,    pancurses::COLOR_BLACK);
    pancurses::init_pair(2, pancurses::COLOR_GREEN,   pancurses::COLOR_BLACK);
    pancurses::init_pair(3, pancurses::COLOR_CYAN,    pancurses::COLOR_BLACK);
    pancurses::init_pair(4, pancurses::COLOR_RED,     pancurses::COLOR_BLACK);
    pancurses::init_pair(5, pancurses::COLOR_MAGENTA, pancurses::COLOR_BLACK);
    pancurses::init_pair(6, pancurses::COLOR_YELLOW,  pancurses::COLOR_BLACK);
    pancurses::init_pair(7, pancurses::COLOR_BLACK,   pancurses::COLOR_BLACK);

    // NOTE: only used for drawing border; remove if we end up not needing this
    pancurses::init_pair(8, pancurses::COLOR_BLUE, pancurses::COLOR_BLUE);

    terminal
}

// TODO: write an actual dang view

pub fn draw(term: &Window, world: &World) {
    term.erase(); // clear back-buffer

    // draw border (NOTE: remove color pair 8 if we remove this)
    // TODO: depend on terminal size; don't hardcode lengths/alignments
    let blue = '#'.to_chtype() | pancurses::chtype::from(ColorPair(8));
    for position in grid::RECTANGLE.grow(1) {
        let Point(col, row) = position;
        term.mvaddch(row + 4, col + 30, blue);
    }

    draw_inventory(term, &world.player.inventory);

    draw_board(term, &world);

    draw_messages(term, &world.log);

    term.refresh();
}

fn draw_board(term: &Window, world: &World) {
    // TODO: depend on terminal size; don't hardcode lengths/alignments
    term.attrset(Attributes::new());
    let level = world.player.current_level(&world.dungeon);
    for row in 0..grid::HEIGHT as i32 {
        term.mv(row + 4, 30);
        for col in 0..grid::WIDTH as i32 {
            let position = Point(col, row);
            let sprite =
                if level.known_tiles.contains(&position) {
                    let visible = world.player.visible.contains(&position);
                    level.sprite_at(position, &world).darken(!visible)
                } else {
                    sprite::HIDDEN
                };
            term.addch(cell(sprite));
        }
    }
}

fn draw_messages(term: &Window, log: &Log) {
    // TODO: depend on terminal size; don't hardcode lengths/alignments;
    //       visually group messages by turn (underscore separators? color?);
    //       maybe group similar messages; add linewrapping if needed
    term.attrset(Attributes::new());
    for (i, &(_, ref message)) in log.recent_messages().iter().take(6).enumerate() {
        term.mvaddstr(23 - i as i32, 0, &message);
    }
}

fn draw_inventory(term: &Window, inventory: &Inventory) {
    // TODO: depend on terminal size; don't hardcode lengths/alignments
    // TODO: abbreviate long inventory slot descriptions
    for (index, slot) in inventory.slots.iter().enumerate() {
        let index_color = if !slot.is_empty() {
            Color::Blue
        } else {
            Color::Dark
        };
        term.attrset(color(index_color));
        term.mvaddstr(index as i32 + 4, 0, &(index+1).to_string());

        let text_color = if slot.is_cursed() {
            if slot.is_equipped() {Color::Red} else {Color::Maroon}
        } else if !slot.is_empty() {
            if slot.is_equipped() {Color::White} else {Color::Gray}
        } else {
            Color::Dark
        };
        term.attrset(color(text_color));

        if slot.is_cursed() {
            term.addstr(" cursed");
        }

        if slot.is_enchanted() {
            term.addstr(" enchanted");
        }

        term.addch(' ');
        let name = match slot.get_item() {
            Some(item) => item.name(),
            None => "nothing"
        };
        term.addstr(name);

        if slot.is_equipped() {
            term.addstr("(equipped)");
        }
    }
}
<|endoftext|>


<commit_before>use std::ops;
use std::mem;
use std::ptr;
use std::ptr::Unique;
use std::marker;
use std::fmt;
use std::hash;
use std::hash::Hash;
use std::cmp::Ordering;

use super::space::U4;

/// On-stack allocation for dynamically-sized type.
///
/// # Examples
///
/// ```
/// use smallbox::StackBox;
///
/// let val: StackBox<PartialEq<usize>> = StackBox::new(5usize).unwrap();
///
/// assert!(*val == 5)
/// ```
pub struct StackBox<T: ?Sized, Space = U4> {
    ptr: Unique<T>,
    space: Space,
}

impl<T: ?Sized, Space> StackBox<T, Space> {
    /// Try to alloc on stack, and return Err<T>
    /// when val is too large (about 4 words)
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    /// use smallbox::StackBox;
    ///
    /// assert!(StackBox::<Any>::new(5usize).is_ok());
    /// assert!(StackBox::<Any>::new([5usize; 8]).is_err());
    /// ```
    pub fn new<U>(val: U) -> Result<StackBox<T, Space>, U>
        where U: marker::Unsize<T>
    {
        if mem::size_of::<U>() > mem::size_of::<Space>() {
            Err(val)
        } else {
            unsafe { Ok(Self::box_up(val)) }
        }
    }

    pub fn resize<ToSpace>(self) -> Result<StackBox<T, ToSpace>, Self> {
        if mem::size_of::<Space>() > mem::size_of::<ToSpace>() {
            Err(self)
        } else {
            unsafe {
                let ptr = self.ptr;
                let mut space = mem::uninitialized::<ToSpace>();
                ptr::copy_nonoverlapping(&self.space, &mut space as *mut _ as *mut Space, 1);
                mem::forget(self);
                Ok(StackBox { ptr, space })
            }
        }
    }

    unsafe fn box_up<U>(mut val: U) -> StackBox<T, Space>
        where U: marker::Unsize<T>
    {
        let ptr: Unique<T> = Unique::new(&mut val);

        let mut space = mem::uninitialized::<Space>();
        ptr::copy_nonoverlapping(&val, &mut space as *mut _ as *mut U, 1);
        mem::forget(val);

        StackBox { ptr, space }
    }

    unsafe fn as_ptr(&self) -> *const T {
        let mut ptr: *const T = self.ptr.as_ptr();
        *(&mut ptr as *mut _ as *mut usize) = &self.space as *const _ as usize;
        ptr
    }
}

impl<T: ?Sized, Space> ops::Deref for StackBox<T, Space> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.as_ptr() }
    }
}

impl<T: ?Sized, Space> ops::DerefMut for StackBox<T, Space> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *(self.as_ptr() as *mut T) }
    }
}

impl<T: ?Sized, Space> ops::Drop for StackBox<T, Space> {
    fn drop(&mut self) {
        unsafe { ptr::drop_in_place(&mut **self) }
    }
}

impl<T: fmt::Display + ?Sized, Space> fmt::Display for StackBox<T, Space> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

impl<T: fmt::Debug + ?Sized, Space> fmt::Debug for StackBox<T, Space> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}

impl<T: ?Sized, Space> fmt::Pointer for StackBox<T, Space> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // It's not possible to extract the inner Uniq directly from the Box,
        // instead we cast it to a *const which aliases the Unique
        let ptr: *const T = &**self;
        fmt::Pointer::fmt(&ptr, f)
    }
}

impl<T: ?Sized + PartialEq, Space> PartialEq for StackBox<T, Space> {
    #[inline]
    fn eq(&self, other: &StackBox<T, Space>) -> bool {
        PartialEq::eq(&**self, &**other)
    }
    #[inline]
    fn ne(&self, other: &StackBox<T, Space>) -> bool {
        PartialEq::ne(&**self, &**other)
    }
}

impl<T: ?Sized + PartialOrd, Space> PartialOrd for StackBox<T, Space> {
    #[inline]
    fn partial_cmp(&self, other: &StackBox<T, Space>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
    #[inline]
    fn lt(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::lt(&**self, &**other)
    }
    #[inline]
    fn le(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::le(&**self, &**other)
    }
    #[inline]
    fn ge(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::ge(&**self, &**other)
    }
    #[inline]
    fn gt(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::gt(&**self, &**other)
    }
}

impl<T: ?Sized + Ord, Space> Ord for StackBox<T, Space> {
    #[inline]
    fn cmp(&self, other: &StackBox<T, Space>) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

impl<T: ?Sized + Eq, Space> Eq for StackBox<T, Space> {}

impl<T: ?Sized + Hash, Space> Hash for StackBox<T, Space> {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        (**self).hash(state);
    }
}<commit_msg>refactor stackbox::new<commit_after>use std::ops;
use std::mem;
use std::ptr;
use std::ptr::Unique;
use std::marker;
use std::fmt;
use std::hash;
use std::hash::Hash;
use std::cmp::Ordering;

use super::space::U4;

/// On-stack allocation for dynamically-sized type.
///
/// # Examples
///
/// ```
/// use smallbox::StackBox;
///
/// let val: StackBox<PartialEq<usize>> = StackBox::new(5usize).unwrap();
///
/// assert!(*val == 5)
/// ```
pub struct StackBox<T: ?Sized, Space = U4> {
    ptr: Unique<T>,
    space: Space,
}

impl<T: ?Sized, Space> StackBox<T, Space> {
    /// Try to alloc on stack, and return Err<T>
    /// when val is too large (about 4 words)
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    /// use smallbox::StackBox;
    ///
    /// assert!(StackBox::<Any>::new(5usize).is_ok());
    /// assert!(StackBox::<Any>::new([5usize; 8]).is_err());
    /// ```
    pub fn new<U>(mut val: U) -> Result<StackBox<T, Space>, U>
        where U: marker::Unsize<T>
    {
        if mem::size_of::<U>() > mem::size_of::<Space>() {
            Err(val)
        } else {
            unsafe {
                let ptr: Unique<T> = Unique::new(&mut val);
                let mut space = mem::uninitialized::<Space>();
                ptr::copy_nonoverlapping(&val, &mut space as *mut _ as *mut U, 1);
                mem::forget(val);
                Ok(StackBox { ptr, space })
            }
        }
    }

    pub fn resize<ToSpace>(self) -> Result<StackBox<T, ToSpace>, Self> {
        if mem::size_of::<Space>() > mem::size_of::<ToSpace>() {
            Err(self)
        } else {
            unsafe {
                let ptr = self.ptr;
                let mut space = mem::uninitialized::<ToSpace>();
                ptr::copy_nonoverlapping(&self.space, &mut space as *mut _ as *mut Space, 1);
                mem::forget(self);
                Ok(StackBox { ptr, space })
            }
        }
    }

    unsafe fn as_ptr(&self) -> *const T {
        let mut ptr: *const T = self.ptr.as_ptr();
        *(&mut ptr as *mut _ as *mut usize) = &self.space as *const _ as usize;
        ptr
    }
}

impl<T: ?Sized, Space> ops::Deref for StackBox<T, Space> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.as_ptr() }
    }
}

impl<T: ?Sized, Space> ops::DerefMut for StackBox<T, Space> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *(self.as_ptr() as *mut T) }
    }
}

impl<T: ?Sized, Space> ops::Drop for StackBox<T, Space> {
    fn drop(&mut self) {
        unsafe { ptr::drop_in_place(&mut **self) }
    }
}

impl<T: fmt::Display + ?Sized, Space> fmt::Display for StackBox<T, Space> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

impl<T: fmt::Debug + ?Sized, Space> fmt::Debug for StackBox<T, Space> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}

impl<T: ?Sized, Space> fmt::Pointer for StackBox<T, Space> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // It's not possible to extract the inner Uniq directly from the Box,
        // instead we cast it to a *const which aliases the Unique
        let ptr: *const T = &**self;
        fmt::Pointer::fmt(&ptr, f)
    }
}

impl<T: ?Sized + PartialEq, Space> PartialEq for StackBox<T, Space> {
    #[inline]
    fn eq(&self, other: &StackBox<T, Space>) -> bool {
        PartialEq::eq(&**self, &**other)
    }
    #[inline]
    fn ne(&self, other: &StackBox<T, Space>) -> bool {
        PartialEq::ne(&**self, &**other)
    }
}

impl<T: ?Sized + PartialOrd, Space> PartialOrd for StackBox<T, Space> {
    #[inline]
    fn partial_cmp(&self, other: &StackBox<T, Space>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
    #[inline]
    fn lt(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::lt(&**self, &**other)
    }
    #[inline]
    fn le(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::le(&**self, &**other)
    }
    #[inline]
    fn ge(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::ge(&**self, &**other)
    }
    #[inline]
    fn gt(&self, other: &StackBox<T, Space>) -> bool {
        PartialOrd::gt(&**self, &**other)
    }
}

impl<T: ?Sized + Ord, Space> Ord for StackBox<T, Space> {
    #[inline]
    fn cmp(&self, other: &StackBox<T, Space>) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

impl<T: ?Sized + Eq, Space> Eq for StackBox<T, Space> {}

impl<T: ?Sized + Hash, Space> Hash for StackBox<T, Space> {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        (**self).hash(state);
    }
}<|endoftext|>


<commit_before>
mod list;

use ::volatile::Volatile;
use ::system_control;
use self::list::TaskQueue;
use ::alloc::boxed::Box;
use ::collections::Vec;

static mut init_task: TaskControl = TaskControl::uninitialized("init");

#[no_mangle]
pub static mut current_task: Option<Box<TaskControl>> = None;

// TODO: Wrap task_list in a mutex lock to provide safe access
static mut task_list: TaskQueue = TaskQueue::new();

/*
struct TaskHandle<'task> {
  task: &'task TaskControl,
}
*/

#[derive(Copy, Clone)]
pub enum Priority {
  Critical,
  Low,
}

#[derive(Copy, Clone)]
enum State {
  Ready,
  Running,
  Blocked,
  Suspended,
  Embryo,
}

pub fn new_task(code: fn(), stack_depth: u32, priority: Priority, name: &'static str) {
  let mut task = Box::new(TaskControl::new(stack_depth, name));
  task.initialize(code, priority);

  unsafe { task_list.enqueue(task); }
}

#[repr(C)]
pub struct TaskControl {
  stack: u32, /* stack pointer MUST be first field */
  stack_base: u32,
  stack_depth: u32,
  state: State,
  priority: Priority,
  name: &'static str,
  next: Option<Box<TaskControl>>,
}

impl TaskControl {
  fn new(depth: u32, name: &'static str) -> Self {
    let stack_mem: Vec<u8> = Vec::with_capacity(depth as usize);
    let stack = stack_mem.as_ptr() as u32;
    // Don't free the heap space
    ::core::mem::forget(stack_mem);
    TaskControl {
      stack: stack + depth,
      stack_base: stack,
      stack_depth: depth,
      state: State::Embryo,
      priority: Priority::Critical,
      name: name,
      next: None,
    }
  }

  const fn uninitialized(name: &'static str) -> Self {
    TaskControl {
      stack: 0,
      stack_base: 0,
      stack_depth: 0,
      state: State::Embryo,
      priority: Priority::Low,
      name: name,
      next: None,
    }
  }

  fn initialize(&mut self, code: fn(), priority: Priority) {
    const INITIAL_XPSR: u32 = 0x0100_0000;
    unsafe {
      let mut stack_mut = Volatile::new(self.stack as *const _);
      // Offset added to account for way MCU uses stack on entry/exit of interrupts
      stack_mut -= 4;
      stack_mut.store(INITIAL_XPSR); /* xPSR */
      stack_mut -= 4;
      stack_mut.store(code as u32); /* PC */
      stack_mut -= 4;
      stack_mut.store(exit_error as u32); /* LR */
      stack_mut -= 20; /* R12, R3, R2, R1 */
      // *stack_mut = params; /* R0 */
      stack_mut -= 32; /* R11..R4 */
      self.stack = stack_mut.as_ptr() as u32;
    }
    self.state = State::Ready;
    self.priority = priority;
  }

  fn is_stack_overflowed(&self) -> bool {
    self.stack <= self.stack_base
  }
}

/// Select a new task to run and switch its context, this function MUST only be called from the
/// PendSV handler, calling it from elsewhere could lead to undefined behavior. It must be exposed
/// publicly so that the compiler doesn't optimize it away when compiling for release.
#[no_mangle]
pub unsafe fn switch_context() {
  match current_task.take() {
    Some(running) => {
      if running.is_stack_overflowed() {
        ::arm::bkpt();
      }
      loop {
        if let Some(new_task) = task_list.dequeue() {
          task_list.enqueue(running);
          current_task = Some(new_task);
          break;
        }
        else {
          // Go to next priority queue
          // If all queues are empty, reschedule current task
          current_task = Some(running);
          break;
        }
      }
    },
    None => panic!("switch_context - current task doesn't exist!"),
  }
}

pub fn start_first_task() {
  unsafe {
    current_task = task_list.dequeue();
    asm!(
      concat!(
          "ldr r2, current_task_const_2\n", /* get location of current_task */
          "ldr r3, [r2]\n",
          "ldr r0, [r3]\n",

          "adds r0, #32\n", /* discard everything up to r0 */
          "msr psp, r0\n", /* this is the new top of stack to use for the task */

          "movs r0, #3\n", /* switch to the psp stack */
          "msr CONTROL, r0\n", /* we're using psp instead of msp now */

          "isb\n", /* instruction barrier */

          "pop {r0-r5}\n", /* pop the registers that are saved automatically */
          "mov lr, r5\n", /* lr is now in r5, so put it back where it belongs */
          "pop {r3}\n", /* pop return address (old pc) into r3 */
          "pop {r2}\n", /* pop and discard xPSR */
          "cpsie i\n", /* first task has its context, so interrupts can be enabled */
          "bx r3\n", /* start executing user code */

           ".align 4\n",
          "current_task_const_2: .word current_task\n")
        );
  }
}

fn exit_error() -> ! {
  unsafe {
    ::arm::bkpt();
    loop{}
  }
}

pub fn yield_task() {
  let scb = system_control::scb();
  scb.set_pend_sv();
  scb.clear_pend_sv();
}
<commit_msg>use the right value for stack switch<commit_after>
mod list;

use ::volatile::Volatile;
use ::system_control;
use self::list::TaskQueue;
use ::alloc::boxed::Box;
use ::collections::Vec;

static mut init_task: TaskControl = TaskControl::uninitialized("init");

#[no_mangle]
pub static mut current_task: Option<Box<TaskControl>> = None;

// TODO: Wrap task_list in a mutex lock to provide safe access
static mut task_list: TaskQueue = TaskQueue::new();

/*
struct TaskHandle<'task> {
  task: &'task TaskControl,
}
*/

#[derive(Copy, Clone)]
pub enum Priority {
  Critical,
  Low,
}

#[derive(Copy, Clone)]
enum State {
  Ready,
  Running,
  Blocked,
  Suspended,
  Embryo,
}

pub fn new_task(code: fn(), stack_depth: u32, priority: Priority, name: &'static str) {
  let mut task = Box::new(TaskControl::new(stack_depth, name));
  task.initialize(code, priority);

  unsafe { task_list.enqueue(task); }
}

#[repr(C)]
pub struct TaskControl {
  stack: u32, /* stack pointer MUST be first field */
  stack_base: u32,
  stack_depth: u32,
  state: State,
  priority: Priority,
  name: &'static str,
  next: Option<Box<TaskControl>>,
}

impl TaskControl {
  fn new(depth: u32, name: &'static str) -> Self {
    let stack_mem: Vec<u8> = Vec::with_capacity(depth as usize);
    let stack = stack_mem.as_ptr() as u32;
    // Don't free the heap space
    ::core::mem::forget(stack_mem);
    TaskControl {
      stack: stack + depth,
      stack_base: stack,
      stack_depth: depth,
      state: State::Embryo,
      priority: Priority::Critical,
      name: name,
      next: None,
    }
  }

  const fn uninitialized(name: &'static str) -> Self {
    TaskControl {
      stack: 0,
      stack_base: 0,
      stack_depth: 0,
      state: State::Embryo,
      priority: Priority::Low,
      name: name,
      next: None,
    }
  }

  fn initialize(&mut self, code: fn(), priority: Priority) {
    const INITIAL_XPSR: u32 = 0x0100_0000;
    unsafe {
      let mut stack_mut = Volatile::new(self.stack as *const _);
      // Offset added to account for way MCU uses stack on entry/exit of interrupts
      stack_mut -= 4;
      stack_mut.store(INITIAL_XPSR); /* xPSR */
      stack_mut -= 4;
      stack_mut.store(code as u32); /* PC */
      stack_mut -= 4;
      stack_mut.store(exit_error as u32); /* LR */
      stack_mut -= 20; /* R12, R3, R2, R1 */
      // *stack_mut = params; /* R0 */
      stack_mut -= 32; /* R11..R4 */
      self.stack = stack_mut.as_ptr() as u32;
    }
    self.state = State::Ready;
    self.priority = priority;
  }

  fn is_stack_overflowed(&self) -> bool {
    self.stack <= self.stack_base
  }
}

/// Select a new task to run and switch its context, this function MUST only be called from the
/// PendSV handler, calling it from elsewhere could lead to undefined behavior. It must be exposed
/// publicly so that the compiler doesn't optimize it away when compiling for release.
#[no_mangle]
pub unsafe fn switch_context() {
  match current_task.take() {
    Some(running) => {
      if running.is_stack_overflowed() {
        ::arm::bkpt();
      }
      loop {
        if let Some(new_task) = task_list.dequeue() {
          task_list.enqueue(running);
          current_task = Some(new_task);
          break;
        }
        else {
          // Go to next priority queue
          // If all queues are empty, reschedule current task
          current_task = Some(running);
          break;
        }
      }
    },
    None => panic!("switch_context - current task doesn't exist!"),
  }
}

pub fn start_first_task() {
  unsafe {
    current_task = task_list.dequeue();
    asm!(
      concat!(
          "ldr r2, current_task_const_2\n", /* get location of current_task */
          "ldr r3, [r2]\n",
          "ldr r0, [r3]\n",

          "adds r0, #32\n", /* discard everything up to r0 */
          "msr psp, r0\n", /* this is the new top of stack to use for the task */

          "movs r0, #2\n", /* switch to the psp stack */
          "msr CONTROL, r0\n", /* we're using psp instead of msp now */

          "isb\n", /* instruction barrier */

          "pop {r0-r5}\n", /* pop the registers that are saved automatically */
          "mov lr, r5\n", /* lr is now in r5, so put it back where it belongs */
          "pop {r3}\n", /* pop return address (old pc) into r3 */
          "pop {r2}\n", /* pop and discard xPSR */
          "cpsie i\n", /* first task has its context, so interrupts can be enabled */
          "bx r3\n", /* start executing user code */

           ".align 4\n",
          "current_task_const_2: .word current_task\n")
        );
  }
}

fn exit_error() -> ! {
  unsafe {
    ::arm::bkpt();
    loop{}
  }
}

pub fn yield_task() {
  let scb = system_control::scb();
  scb.set_pend_sv();
  scb.clear_pend_sv();
}
<|endoftext|>