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

/// Number of byes in each entry pair
pub static INDEX_ENTRY_BYTES: usize = 8;

/// An index is a file with pairs of relative offset to file position offset
/// of messages at the relative offset messages. The index is Memory Mapped.
pub struct Index {
    file: File,
    mmap: Mmap,
    mode: AccessMode,

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

/// Describes the access mode of the index
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AccessMode {
    /// Only reads are permitted.
    Read,
    /// This is the active index and can be read or written to.
    ReadWrite,
}

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

impl IndexEntry {
    #[inline]
    #[allow(dead_code)]
    pub fn relative_offset(&self) -> u32 {
        self.rel_offset
    }

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

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

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

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

impl Index {
    pub fn new<P>(log_dir: P, base_offset: u64, file_bytes: usize) -> io::Result<Index>
        where P: AsRef<Path>
    {
        // open the file, expecting to create it
        let index_path = {
            let mut path_buf = PathBuf::new();
            path_buf.push(&log_dir);
            path_buf.push(format!("{:020}", base_offset));
            path_buf.set_extension("index");
            path_buf
        };

        let index_file = try!(OpenOptions::new()
            .read(true)
            .write(true)
            .append(true)
            .create_new(true)
            .open(&index_path));

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

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

        Ok(Index {
            file: index_file,
            mmap: mmap,
            mode: AccessMode::ReadWrite,
            next_write_pos: 0,
            base_offset: base_offset,
        })
    }

    pub fn can_write(&self) -> bool {
        self.mode == AccessMode::ReadWrite && self.size() >= (self.next_write_pos + 8)
    }

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

    pub fn append(&mut self, abs_offset: u64, position: u32) -> Result<(), IndexWriteError> {
        assert!(abs_offset >= self.base_offset);

        if !self.can_write() {
            return Err(IndexWriteError::IndexFull);
        }

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

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

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

            self.next_write_pos += 8;

            Ok(())
        }
    }

    pub fn set_readonly(&mut self) -> io::Result<()> {
        if self.mode != AccessMode::Read {
            self.mode = AccessMode::Read;
            self.flush_sync()
        } else {
            Ok(())
        }
    }

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

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

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

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

    #[test]
    fn index() {
        let path = TestDir::new();
        let mut index = Index::new(&path, 9u64, 1000usize).unwrap();

        assert_eq!(1000, index.size());
        index.append(11u64, 0xffff).unwrap();
        index.append(12u64, 0xeeee).unwrap();
        index.flush_sync().unwrap();

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

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

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

    #[test]
    fn index_set_readonly() {
        let path = TestDir::new();
        let mut index = Index::new(&path, 10u64, 1000usize).unwrap();

        index.append(11u64, 0xffff).unwrap();
        index.append(12u64, 0xeeee).unwrap();

        // set_readonly it
        index.set_readonly().expect("Unable to set readonly");

        // append should fail with insertion error
        assert_eq!(index.append(13u64, 0xeeeeee),
                   Err(IndexWriteError::IndexFull));

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

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

}
<commit_msg>Adding index open option<commit_after>use byteorder::{BigEndian, ByteOrder};
use memmap::{Mmap, Protection};
use std::path::{Path, PathBuf};
use std::io::{self, Write};
use std::fs::{OpenOptions, File};
use std::{u64, usize};

/// Number of byes in each entry pair
pub static INDEX_ENTRY_BYTES: usize = 8;
pub static INDEX_FILE_NAME_LEN: usize = 20;
pub static INDEX_FILE_NAME_EXTENSION: &'static str = "index";

/// An index is a file with pairs of relative offset to file position offset
/// of messages at the relative offset messages. The index is Memory Mapped.
pub struct Index {
    file: File,
    mmap: Mmap,
    mode: AccessMode,

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

/// Describes the access mode of the index
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AccessMode {
    /// Only reads are permitted.
    Read,
    /// This is the active index and can be read or written to.
    ReadWrite,
}

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

impl IndexEntry {
    #[inline]
    #[allow(dead_code)]
    pub fn relative_offset(&self) -> u32 {
        self.rel_offset
    }

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

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

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

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

impl Index {
    pub fn new<P>(log_dir: P, base_offset: u64, file_bytes: usize) -> io::Result<Index>
        where P: AsRef<Path>
    {
        // open the file, expecting to create it
        let index_path = {
            let mut path_buf = PathBuf::new();
            path_buf.push(&log_dir);
            path_buf.push(format!("{:020}", base_offset));
            path_buf.set_extension(INDEX_FILE_NAME_EXTENSION);
            path_buf
        };

        let index_file = try!(OpenOptions::new()
            .read(true)
            .write(true)
            .append(true)
            .create_new(true)
            .open(&index_path));

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

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

        Ok(Index {
            file: index_file,
            mmap: mmap,
            mode: AccessMode::ReadWrite,
            next_write_pos: 0,
            base_offset: base_offset,
        })
    }

    #[allow(dead_code)]
    pub fn open<P>(index_path: P) -> io::Result<Index>
        where P: AsRef<Path>
    {
        let index_file = try!(OpenOptions::new()
            .read(true)
            .write(false)
            .append(false)
            .open(&index_path));

        let filename = index_path.as_ref().file_name().unwrap().to_str().unwrap();
        let base_offset = match u64::from_str_radix(&filename[0..INDEX_FILE_NAME_LEN], 10) {
            Ok(v) => v,
            Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidData, "Index file name does not parse as u64")),
        };

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

        Ok(Index {
            file: index_file,
            mmap: mmap,
            mode: AccessMode::Read,
            next_write_pos: usize::max_value(),
            base_offset: base_offset,
        })
    }

    fn can_write(&self) -> bool {
        self.mode == AccessMode::ReadWrite && self.size() >= (self.next_write_pos + 8)
    }

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

    pub fn append(&mut self, abs_offset: u64, position: u32) -> Result<(), IndexWriteError> {
        assert!(abs_offset >= self.base_offset);

        if !self.can_write() {
            return Err(IndexWriteError::IndexFull);
        }

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

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

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

            self.next_write_pos += 8;

            Ok(())
        }
    }

    pub fn set_readonly(&mut self) -> io::Result<()> {
        if self.mode != AccessMode::Read {
            self.mode = AccessMode::Read;
            self.flush_sync()
        } else {
            Ok(())
        }
    }

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

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

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

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

    #[test]
    pub fn index() {
        let path = TestDir::new();
        let mut index = Index::new(&path, 9u64, 1000usize).unwrap();

        assert_eq!(1000, index.size());
        index.append(11u64, 0xffff).unwrap();
        index.append(12u64, 0xeeee).unwrap();
        index.flush_sync().unwrap();

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

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

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

    #[test]
    pub fn index_set_readonly() {
        let path = TestDir::new();
        let mut index = Index::new(&path, 10u64, 1000usize).unwrap();

        index.append(11u64, 0xffff).unwrap();
        index.append(12u64, 0xeeee).unwrap();

        // set_readonly it
        index.set_readonly().expect("Unable to set readonly");

        // append should fail with insertion error
        assert_eq!(index.append(13u64, 0xeeeeee),
                   Err(IndexWriteError::IndexFull));

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

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

    #[test]
    pub fn open_index() {
        let dir = TestDir::new();
        // issue some writes
        {
            let mut index = Index::new(&dir, 10u64, 1000usize).unwrap();
            index.append(10, 0).unwrap();
            index.append(11, 10).unwrap();
            index.append(12, 20).unwrap();
            index.append(13, 30).unwrap();
            index.append(14, 40).unwrap();
            index.set_readonly().unwrap();
        }

        // now open it
        {
            let mut index_path = PathBuf::new();
            index_path.push(&dir);
            index_path.push("00000000000000000010.index");

            let meta = fs::metadata(&index_path).unwrap();
            assert!(meta.is_file());

            let index = Index::open(&index_path).unwrap();

            for i in 0..5usize {
                let e = index.read_entry(i).unwrap();
                assert!(e.is_some());
                assert_eq!(e.unwrap().relative_offset(), i as u32);
                assert_eq!(e.unwrap().offset(), (i+10) as u64);
                assert_eq!(e.unwrap().file_position(), (i*10) as u32);
            }
        }

    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![allow(non_upper_case_globals)]

use core_foundation::array::CFArrayRef;
use core_foundation::base::{CFRelease, CFRetain, CFType, CFTypeID, CFTypeRef, TCFType};
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::set::CFSetRef;
use core_foundation::string::{CFString, CFStringRef};
use core_foundation::url::{CFURL, CFURLRef};
use core_graphics::base::CGFloat;

use libc::c_void;
use std::mem;

/*
* CTFontTraits.h
*/
// actually, these are extern enums
pub type CTFontFormat = u32;
pub const kCTFontFormatUnrecognized: CTFontFormat = 0;
pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1;
pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2;
pub const kCTFontFormatTrueType: CTFontFormat = 3;
pub const kCTFontFormatPostScript: CTFontFormat = 4;
pub const kCTFontFormatBitmap: CTFontFormat = 5;

pub const kCTFontClassMaskShift: u32 = 28;

pub type CTFontSymbolicTraits = u32;
pub const kCTFontItalicTrait: CTFontSymbolicTraits = (1 << 0);
pub const kCTFontBoldTrait: CTFontSymbolicTraits = (1 << 1);
pub const kCTFontExpandedTrait: CTFontSymbolicTraits = (1 << 5);
pub const kCTFontCondensedTrait: CTFontSymbolicTraits = (1 << 6);
pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = (1 << 10);
pub const kCTFontVerticalTrait: CTFontSymbolicTraits = (1 << 11);
pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = (1 << 12);
pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = (1 << 13);
pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = (15 << kCTFontClassMaskShift );

pub trait SymbolicTraitAccessors {
    fn is_italic(&self) -> bool;
    fn is_bold(&self) -> bool;
    fn is_expanded(&self) -> bool;
    fn is_condensed(&self) -> bool;
    fn is_monospace(&self) -> bool;
}

impl SymbolicTraitAccessors for CTFontSymbolicTraits {
    fn is_italic(&self) -> bool { (*self & kCTFontItalicTrait) != 0 }
    fn is_bold(&self) -> bool { (*self & kCTFontBoldTrait) != 0 }
    fn is_expanded(&self) -> bool { (*self & kCTFontExpandedTrait) != 0 }
    fn is_condensed(&self) -> bool { (*self & kCTFontCondensedTrait) != 0 }
    fn is_monospace(&self) -> bool { (*self & kCTFontMonoSpaceTrait) != 0 }
}

pub type CTFontStylisticClass = u32;
pub const kCTFontUnknownClass: CTFontStylisticClass = (0 << kCTFontClassMaskShift );
pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = (1 << kCTFontClassMaskShift );
pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = (2 << kCTFontClassMaskShift );
pub const kCTFontModernSerifsClass: CTFontStylisticClass = (3 << kCTFontClassMaskShift );
pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = (4 << kCTFontClassMaskShift );
pub const kCTFontSlabSerifsClass: CTFontStylisticClass = (5 << kCTFontClassMaskShift );
pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = (7 << kCTFontClassMaskShift );
pub const kCTFontSansSerifClass: CTFontStylisticClass = (8 << kCTFontClassMaskShift );
pub const kCTFontOrnamentalsClass: CTFontStylisticClass = (9 << kCTFontClassMaskShift );
pub const kCTFontScriptsClass: CTFontStylisticClass = (10 << kCTFontClassMaskShift );
pub const kCTFontSymbolicClass: CTFontStylisticClass = (12 << kCTFontClassMaskShift );

pub trait StylisticClassAccessors {
    fn is_serif(&self) -> bool;
    fn is_sans_serif(&self) -> bool;
    fn is_script(&self) -> bool;
    fn is_fantasy(&self) -> bool;
    fn is_symbols(&self) -> bool;
}

impl StylisticClassAccessors for CTFontStylisticClass {
    fn is_serif(&self) -> bool {
        let any_serif_class = kCTFontOldStyleSerifsClass
            | kCTFontTransitionalSerifsClass
            | kCTFontModernSerifsClass
            | kCTFontClarendonSerifsClass
            | kCTFontSlabSerifsClass
            | kCTFontFreeformSerifsClass;

        return (*self & any_serif_class) != 0;
    }

    fn is_sans_serif(&self) -> bool {
        return (*self & kCTFontSansSerifClass) != 0;
    }

    fn is_script(&self) -> bool {
        return (*self & kCTFontScriptsClass) != 0;
    }

    fn is_fantasy(&self) -> bool {
        return (*self & kCTFontOrnamentalsClass) != 0;
    }

    fn is_symbols(&self) -> bool {
        return (*self & kCTFontSymbolicClass) != 0;
    }
}

pub type CTFontAttributes = CFDictionary;

pub type CTFontTraits = CFDictionary;

pub trait TraitAccessors {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits;
    fn normalized_weight(&self) -> f64;
    fn normalized_width(&self) -> f64;
    fn normalized_slant(&self) -> f64;
}

trait TraitAccessorPrivate {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber;
}

impl TraitAccessorPrivate for CTFontTraits {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber {
        let cftype = self.get_CFType(mem::transmute(key));
        assert!(cftype.instance_of::<CFNumberRef,CFNumber>());
        TCFType::wrap_under_get_rule(mem::transmute(cftype.as_CFTypeRef()))
    }

}

impl TraitAccessors for CTFontTraits {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSymbolicTrait);
            number.to_i64().unwrap() as u32
        }
    }

    fn normalized_weight(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWeightTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_width(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWidthTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_slant(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSlantTrait);
            number.to_f64().unwrap()
        }
    }
}

/*
* CTFontDescriptor.h
*/
pub type CTFontOrientation = u32;
pub const kCTFontDefaultOrientation: CTFontOrientation = 0;
pub const kCTFontHorizontalOrientation: CTFontOrientation = 1;
pub const kCTFontVerticalOrientation: CTFontOrientation = 2;

pub type CTFontPriority = u32;
pub const kCTFontPrioritySystem: CTFontPriority = 10000;
pub const kCTFontPriorityNetwork: CTFontPriority = 20000;
pub const kCTFontPriorityComputer: CTFontPriority = 30000;
pub const kCTFontPriorityUser: CTFontPriority = 40000;
pub const kCTFontPriorityDynamic: CTFontPriority = 50000;
pub const kCTFontPriorityProcess: CTFontPriority = 60000;

#[repr(C)]
pub struct __CTFontDescriptor(c_void);

pub type CTFontDescriptorRef = *const __CTFontDescriptor;

#[derive(Debug)]
pub struct CTFontDescriptor {
    obj: CTFontDescriptorRef,
}

impl Drop for CTFontDescriptor {
    fn drop(&mut self) {
        unsafe {
            CFRelease(self.as_CFTypeRef())
        }
    }
}

impl TCFType<CTFontDescriptorRef> for CTFontDescriptor {
    #[inline]
    fn as_concrete_TypeRef(&self) -> CTFontDescriptorRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CTFontDescriptorRef) -> CTFontDescriptor {
        let reference: CTFontDescriptorRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CTFontDescriptorRef) -> CTFontDescriptor {
        CTFontDescriptor {
            obj: obj,
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CTFontDescriptorGetTypeID()
        }
    }
}

impl CTFontDescriptor {
    fn get_string_attribute(&self, attribute: CFStringRef) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.obj, attribute);
            if value.is_null() {
                return None
            }

            let value: CFType = TCFType::wrap_under_get_rule(value);
            assert!(value.instance_of::<CFStringRef,CFString>());
            let s: CFString = TCFType::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(s.to_string())
        }
    }

}

impl CTFontDescriptor {
    pub fn family_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontDisplayNameAttribute);
            value.expect("A font2 must have a non-null font family name.")
        }
    }

    pub fn font_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontNameAttribute);
            value.expect("A font must have a non-null name.")
        }
    }

    pub fn style_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontStyleNameAttribute);
            value.expect("A font must have a non-null style name.")
        }
    }

    pub fn display_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontDisplayNameAttribute);
            value.expect("A font must have a non-null display name.")
        }
    }

    pub fn font_path(&self) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.obj, kCTFontURLAttribute);
            if value.is_null() {
                return None;
            }

            let value: CFType = TCFType::wrap_under_get_rule(value);
            assert!(value.instance_of::<CFURLRef,CFURL>());
            let url: CFURL = TCFType::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(format!("{:?}", url))
        }
    }
}

pub fn new_from_attributes(attributes: &CFDictionary) -> CTFontDescriptor {
    unsafe {
        let result: CTFontDescriptorRef =
            CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef());
        TCFType::wrap_under_create_rule(result)
    }
}

pub fn debug_descriptor(desc: &CTFontDescriptor) {
    println!("family: {}", desc.family_name());
    println!("name: {}", desc.font_name());
    println!("style: {}", desc.style_name());
    println!("display: {}", desc.display_name());
    println!("path: {:?}", desc.font_path());
    desc.show();
}

extern {
    /*
     * CTFontTraits.h
     */

    // font trait constants
    pub static kCTFontSymbolicTrait: CFStringRef;
    pub static kCTFontWeightTrait: CFStringRef;
    pub static kCTFontWidthTrait: CFStringRef;
    pub static kCTFontSlantTrait: CFStringRef;

    /*
     * CTFontDescriptor.h
     */

    // font attribute constants. Note that the name-related attributes
    // here are somewhat flaky. Servo creates CTFont instances and
    // then uses CTFontCopyName to get more fine-grained names.
    pub static kCTFontURLAttribute:                  CFStringRef; // value: CFURLRef
    pub static kCTFontNameAttribute:                 CFStringRef; // value: CFStringRef
    pub static kCTFontDisplayNameAttribute:          CFStringRef; // value: CFStringRef
    pub static kCTFontFamilyNameAttribute:           CFStringRef; // value: CFStringRef
    pub static kCTFontStyleNameAttribute:            CFStringRef; // value: CFStringRef
    pub static kCTFontTraitsAttribute:               CFStringRef;
    pub static kCTFontVariationAttribute:            CFStringRef;
    pub static kCTFontSizeAttribute:                 CFStringRef;
    pub static kCTFontMatrixAttribute:               CFStringRef;
    pub static kCTFontCascadeListAttribute:          CFStringRef;
    pub static kCTFontCharacterSetAttribute:         CFStringRef;
    pub static kCTFontLanguagesAttribute:            CFStringRef;
    pub static kCTFontBaselineAdjustAttribute:       CFStringRef;
    pub static kCTFontMacintoshEncodingsAttribute:   CFStringRef;
    pub static kCTFontFeaturesAttribute:             CFStringRef;
    pub static kCTFontFeatureSettingsAttribute:      CFStringRef;
    pub static kCTFontFixedAdvanceAttribute:         CFStringRef;
    pub static kCTFontOrientationAttribute:          CFStringRef;
    pub static kCTFontFormatAttribute:               CFStringRef;
    pub static kCTFontRegistrationScopeAttribute:    CFStringRef;
    pub static kCTFontPriorityAttribute:             CFStringRef;
    pub static kCTFontEnabledAttribute:              CFStringRef;

    pub fn CTFontDescriptorCopyAttribute(descriptor: CTFontDescriptorRef,
                                         attribute: CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef;
    pub fn CTFontDescriptorCopyLocalizedAttribute(descriptor: CTFontDescriptorRef,
                                                  attribute: CFStringRef,
                                                  language: *mut CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCreateCopyWithAttributes(original: CTFontDescriptorRef,
                                                    attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithFeature(original: CTFontDescriptorRef,
                                                 featureTypeIdentifier: CFNumberRef,
                                                 featureSelectorIdentifier: CFNumberRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithVariation(original: CTFontDescriptorRef,
                                                   variationIdentifier: CFNumberRef,
                                                   variationValue: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateMatchingFontDescriptor(descriptor: CTFontDescriptorRef,
                                                        mandatoryAttributes: CFSetRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithNameAndSize(name: CFStringRef, size: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorGetTypeID() -> CFTypeID;
}

extern {
    pub fn CTFontDescriptorCreateMatchingFontDescriptors(descriptor: CTFontDescriptorRef,
                                                         mandatoryAttributes: CFSetRef) -> CFArrayRef;
}
<commit_msg>Auto merge of #76 - poiru:fix-family-name, r=jdm<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![allow(non_upper_case_globals)]

use core_foundation::array::CFArrayRef;
use core_foundation::base::{CFRelease, CFRetain, CFType, CFTypeID, CFTypeRef, TCFType};
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::set::CFSetRef;
use core_foundation::string::{CFString, CFStringRef};
use core_foundation::url::{CFURL, CFURLRef};
use core_graphics::base::CGFloat;

use libc::c_void;
use std::mem;

/*
* CTFontTraits.h
*/
// actually, these are extern enums
pub type CTFontFormat = u32;
pub const kCTFontFormatUnrecognized: CTFontFormat = 0;
pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1;
pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2;
pub const kCTFontFormatTrueType: CTFontFormat = 3;
pub const kCTFontFormatPostScript: CTFontFormat = 4;
pub const kCTFontFormatBitmap: CTFontFormat = 5;

pub const kCTFontClassMaskShift: u32 = 28;

pub type CTFontSymbolicTraits = u32;
pub const kCTFontItalicTrait: CTFontSymbolicTraits = (1 << 0);
pub const kCTFontBoldTrait: CTFontSymbolicTraits = (1 << 1);
pub const kCTFontExpandedTrait: CTFontSymbolicTraits = (1 << 5);
pub const kCTFontCondensedTrait: CTFontSymbolicTraits = (1 << 6);
pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = (1 << 10);
pub const kCTFontVerticalTrait: CTFontSymbolicTraits = (1 << 11);
pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = (1 << 12);
pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = (1 << 13);
pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = (15 << kCTFontClassMaskShift );

pub trait SymbolicTraitAccessors {
    fn is_italic(&self) -> bool;
    fn is_bold(&self) -> bool;
    fn is_expanded(&self) -> bool;
    fn is_condensed(&self) -> bool;
    fn is_monospace(&self) -> bool;
}

impl SymbolicTraitAccessors for CTFontSymbolicTraits {
    fn is_italic(&self) -> bool { (*self & kCTFontItalicTrait) != 0 }
    fn is_bold(&self) -> bool { (*self & kCTFontBoldTrait) != 0 }
    fn is_expanded(&self) -> bool { (*self & kCTFontExpandedTrait) != 0 }
    fn is_condensed(&self) -> bool { (*self & kCTFontCondensedTrait) != 0 }
    fn is_monospace(&self) -> bool { (*self & kCTFontMonoSpaceTrait) != 0 }
}

pub type CTFontStylisticClass = u32;
pub const kCTFontUnknownClass: CTFontStylisticClass = (0 << kCTFontClassMaskShift );
pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = (1 << kCTFontClassMaskShift );
pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = (2 << kCTFontClassMaskShift );
pub const kCTFontModernSerifsClass: CTFontStylisticClass = (3 << kCTFontClassMaskShift );
pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = (4 << kCTFontClassMaskShift );
pub const kCTFontSlabSerifsClass: CTFontStylisticClass = (5 << kCTFontClassMaskShift );
pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = (7 << kCTFontClassMaskShift );
pub const kCTFontSansSerifClass: CTFontStylisticClass = (8 << kCTFontClassMaskShift );
pub const kCTFontOrnamentalsClass: CTFontStylisticClass = (9 << kCTFontClassMaskShift );
pub const kCTFontScriptsClass: CTFontStylisticClass = (10 << kCTFontClassMaskShift );
pub const kCTFontSymbolicClass: CTFontStylisticClass = (12 << kCTFontClassMaskShift );

pub trait StylisticClassAccessors {
    fn is_serif(&self) -> bool;
    fn is_sans_serif(&self) -> bool;
    fn is_script(&self) -> bool;
    fn is_fantasy(&self) -> bool;
    fn is_symbols(&self) -> bool;
}

impl StylisticClassAccessors for CTFontStylisticClass {
    fn is_serif(&self) -> bool {
        let any_serif_class = kCTFontOldStyleSerifsClass
            | kCTFontTransitionalSerifsClass
            | kCTFontModernSerifsClass
            | kCTFontClarendonSerifsClass
            | kCTFontSlabSerifsClass
            | kCTFontFreeformSerifsClass;

        return (*self & any_serif_class) != 0;
    }

    fn is_sans_serif(&self) -> bool {
        return (*self & kCTFontSansSerifClass) != 0;
    }

    fn is_script(&self) -> bool {
        return (*self & kCTFontScriptsClass) != 0;
    }

    fn is_fantasy(&self) -> bool {
        return (*self & kCTFontOrnamentalsClass) != 0;
    }

    fn is_symbols(&self) -> bool {
        return (*self & kCTFontSymbolicClass) != 0;
    }
}

pub type CTFontAttributes = CFDictionary;

pub type CTFontTraits = CFDictionary;

pub trait TraitAccessors {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits;
    fn normalized_weight(&self) -> f64;
    fn normalized_width(&self) -> f64;
    fn normalized_slant(&self) -> f64;
}

trait TraitAccessorPrivate {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber;
}

impl TraitAccessorPrivate for CTFontTraits {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber {
        let cftype = self.get_CFType(mem::transmute(key));
        assert!(cftype.instance_of::<CFNumberRef,CFNumber>());
        TCFType::wrap_under_get_rule(mem::transmute(cftype.as_CFTypeRef()))
    }

}

impl TraitAccessors for CTFontTraits {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSymbolicTrait);
            number.to_i64().unwrap() as u32
        }
    }

    fn normalized_weight(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWeightTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_width(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWidthTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_slant(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSlantTrait);
            number.to_f64().unwrap()
        }
    }
}

/*
* CTFontDescriptor.h
*/
pub type CTFontOrientation = u32;
pub const kCTFontDefaultOrientation: CTFontOrientation = 0;
pub const kCTFontHorizontalOrientation: CTFontOrientation = 1;
pub const kCTFontVerticalOrientation: CTFontOrientation = 2;

pub type CTFontPriority = u32;
pub const kCTFontPrioritySystem: CTFontPriority = 10000;
pub const kCTFontPriorityNetwork: CTFontPriority = 20000;
pub const kCTFontPriorityComputer: CTFontPriority = 30000;
pub const kCTFontPriorityUser: CTFontPriority = 40000;
pub const kCTFontPriorityDynamic: CTFontPriority = 50000;
pub const kCTFontPriorityProcess: CTFontPriority = 60000;

#[repr(C)]
pub struct __CTFontDescriptor(c_void);

pub type CTFontDescriptorRef = *const __CTFontDescriptor;

#[derive(Debug)]
pub struct CTFontDescriptor {
    obj: CTFontDescriptorRef,
}

impl Drop for CTFontDescriptor {
    fn drop(&mut self) {
        unsafe {
            CFRelease(self.as_CFTypeRef())
        }
    }
}

impl TCFType<CTFontDescriptorRef> for CTFontDescriptor {
    #[inline]
    fn as_concrete_TypeRef(&self) -> CTFontDescriptorRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CTFontDescriptorRef) -> CTFontDescriptor {
        let reference: CTFontDescriptorRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CTFontDescriptorRef) -> CTFontDescriptor {
        CTFontDescriptor {
            obj: obj,
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CTFontDescriptorGetTypeID()
        }
    }
}

impl CTFontDescriptor {
    fn get_string_attribute(&self, attribute: CFStringRef) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.obj, attribute);
            if value.is_null() {
                return None
            }

            let value: CFType = TCFType::wrap_under_get_rule(value);
            assert!(value.instance_of::<CFStringRef,CFString>());
            let s: CFString = TCFType::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(s.to_string())
        }
    }

}

impl CTFontDescriptor {
    pub fn family_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontFamilyNameAttribute);
            value.expect("A font must have a non-null family name.")
        }
    }

    pub fn font_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontNameAttribute);
            value.expect("A font must have a non-null name.")
        }
    }

    pub fn style_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontStyleNameAttribute);
            value.expect("A font must have a non-null style name.")
        }
    }

    pub fn display_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontDisplayNameAttribute);
            value.expect("A font must have a non-null display name.")
        }
    }

    pub fn font_path(&self) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.obj, kCTFontURLAttribute);
            if value.is_null() {
                return None;
            }

            let value: CFType = TCFType::wrap_under_get_rule(value);
            assert!(value.instance_of::<CFURLRef,CFURL>());
            let url: CFURL = TCFType::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(format!("{:?}", url))
        }
    }
}

pub fn new_from_attributes(attributes: &CFDictionary) -> CTFontDescriptor {
    unsafe {
        let result: CTFontDescriptorRef =
            CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef());
        TCFType::wrap_under_create_rule(result)
    }
}

pub fn debug_descriptor(desc: &CTFontDescriptor) {
    println!("family: {}", desc.family_name());
    println!("name: {}", desc.font_name());
    println!("style: {}", desc.style_name());
    println!("display: {}", desc.display_name());
    println!("path: {:?}", desc.font_path());
    desc.show();
}

extern {
    /*
     * CTFontTraits.h
     */

    // font trait constants
    pub static kCTFontSymbolicTrait: CFStringRef;
    pub static kCTFontWeightTrait: CFStringRef;
    pub static kCTFontWidthTrait: CFStringRef;
    pub static kCTFontSlantTrait: CFStringRef;

    /*
     * CTFontDescriptor.h
     */

    // font attribute constants. Note that the name-related attributes
    // here are somewhat flaky. Servo creates CTFont instances and
    // then uses CTFontCopyName to get more fine-grained names.
    pub static kCTFontURLAttribute:                  CFStringRef; // value: CFURLRef
    pub static kCTFontNameAttribute:                 CFStringRef; // value: CFStringRef
    pub static kCTFontDisplayNameAttribute:          CFStringRef; // value: CFStringRef
    pub static kCTFontFamilyNameAttribute:           CFStringRef; // value: CFStringRef
    pub static kCTFontStyleNameAttribute:            CFStringRef; // value: CFStringRef
    pub static kCTFontTraitsAttribute:               CFStringRef;
    pub static kCTFontVariationAttribute:            CFStringRef;
    pub static kCTFontSizeAttribute:                 CFStringRef;
    pub static kCTFontMatrixAttribute:               CFStringRef;
    pub static kCTFontCascadeListAttribute:          CFStringRef;
    pub static kCTFontCharacterSetAttribute:         CFStringRef;
    pub static kCTFontLanguagesAttribute:            CFStringRef;
    pub static kCTFontBaselineAdjustAttribute:       CFStringRef;
    pub static kCTFontMacintoshEncodingsAttribute:   CFStringRef;
    pub static kCTFontFeaturesAttribute:             CFStringRef;
    pub static kCTFontFeatureSettingsAttribute:      CFStringRef;
    pub static kCTFontFixedAdvanceAttribute:         CFStringRef;
    pub static kCTFontOrientationAttribute:          CFStringRef;
    pub static kCTFontFormatAttribute:               CFStringRef;
    pub static kCTFontRegistrationScopeAttribute:    CFStringRef;
    pub static kCTFontPriorityAttribute:             CFStringRef;
    pub static kCTFontEnabledAttribute:              CFStringRef;

    pub fn CTFontDescriptorCopyAttribute(descriptor: CTFontDescriptorRef,
                                         attribute: CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef;
    pub fn CTFontDescriptorCopyLocalizedAttribute(descriptor: CTFontDescriptorRef,
                                                  attribute: CFStringRef,
                                                  language: *mut CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCreateCopyWithAttributes(original: CTFontDescriptorRef,
                                                    attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithFeature(original: CTFontDescriptorRef,
                                                 featureTypeIdentifier: CFNumberRef,
                                                 featureSelectorIdentifier: CFNumberRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithVariation(original: CTFontDescriptorRef,
                                                   variationIdentifier: CFNumberRef,
                                                   variationValue: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateMatchingFontDescriptor(descriptor: CTFontDescriptorRef,
                                                        mandatoryAttributes: CFSetRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithNameAndSize(name: CFStringRef, size: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorGetTypeID() -> CFTypeID;
}

extern {
    pub fn CTFontDescriptorCreateMatchingFontDescriptors(descriptor: CTFontDescriptorRef,
                                                         mandatoryAttributes: CFSetRef) -> CFArrayRef;
}
<|endoftext|>


<commit_before>extern crate glutin;

use std::collections::HashSet;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MouseState {
    pub at: (i32, i32), // could make this optional for off screen? might be a stupid idea.

    pub down: HashSet<glutin::MouseButton>,
    pub pushed: HashSet<glutin::MouseButton>,
    pub released: HashSet<glutin::MouseButton>,

    pub mouse_wheel_delta: i32, // we multiply the float delta by 100 and round it
}

impl MouseState {
    pub fn left_pushed(&self) -> bool {
        self.pushed.contains(&glutin::MouseButton::Left)
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyState {
    pub down: HashSet<glutin::VirtualKeyCode>,
    pub pushed: HashSet<glutin::VirtualKeyCode>,
    pub released: HashSet<glutin::VirtualKeyCode>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InputState {
    pub mouse:MouseState,
    pub keys:KeyState,
    pub close:bool,
}

pub fn is_close_event(event: &glutin::Event) -> bool {
    match event {
        &glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) | &glutin::Event::Closed => true,
        _ => false,
    }
}

pub fn produce(input:&InputState, events: &Vec<glutin::Event>) -> InputState {
    let mut next_input = input.clone();

    next_input.keys.pushed.clear();
    next_input.keys.released.clear();
    next_input.mouse.pushed.clear();
    next_input.mouse.released.clear();

    next_input.mouse.mouse_wheel_delta = 0;

    for event in events {
        if is_close_event(&event) {
            next_input.close = true;
        }
        match event {
            &glutin::Event::KeyboardInput(element_state, _, Some(key_code)) =>
                match element_state {
                    glutin::ElementState::Pressed => {
                        let was_down = next_input.keys.down.contains(&key_code);
                        next_input.keys.down.insert(key_code);
                        if !was_down {
                            next_input.keys.pushed.insert(key_code);
                        }
                    },
                    glutin::ElementState::Released => {
                        let was_down = next_input.keys.down.contains(&key_code);
                        next_input.keys.down.remove(&key_code);
                        if !was_down {
                            next_input.keys.released.insert(key_code);
                        }
                    }
                },
            &glutin::Event::MouseWheel(glutin::MouseScrollDelta::LineDelta(_, mouse_scroll_delta), _) => {
                next_input.mouse.mouse_wheel_delta += (mouse_scroll_delta * 100.0) as i32;
            },
            &glutin::Event::MouseInput(element_state, mouse_button) =>
                match element_state {
                    glutin::ElementState::Pressed => {
                        let was_down = next_input.mouse.down.contains(&mouse_button);
                        next_input.mouse.down.insert(mouse_button);
                        if !was_down {
                            next_input.mouse.pushed.insert(mouse_button);
                        }
                    },
                    glutin::ElementState::Released => {
                        let was_down = next_input.mouse.down.contains(&mouse_button);
                        next_input.mouse.down.remove(&mouse_button);
                        if was_down {
                            next_input.mouse.released.insert(mouse_button);
                        }
                    },
                },
            &glutin::Event::MouseMoved(x, y) => next_input.mouse.at = (x, y),
            _ => (),
        };
    }

    next_input
}

impl InputState {
    pub fn default() -> InputState {
        InputState {
            mouse: MouseState {
                at: (0, 0),
                down: HashSet::new(),
                pushed: HashSet::new(),
                released: HashSet::new(),
                mouse_wheel_delta: 0,
            },
            keys: KeyState {
                down: HashSet::new(),
                pushed: HashSet::new(),
                released: HashSet::new(),
            },
            close: false,
        }
    }
}
<commit_msg>Mouse convenience methods<commit_after>extern crate glutin;

use std::collections::HashSet;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MouseState {
    pub at: (i32, i32), // could make this optional for off screen? might be a stupid idea.

    pub down: HashSet<glutin::MouseButton>,
    pub pushed: HashSet<glutin::MouseButton>,
    pub released: HashSet<glutin::MouseButton>,

    pub mouse_wheel_delta: i32, // we multiply the float delta by 100 and round it
}

impl MouseState {
    pub fn left_pushed(&self) -> bool {
        self.pushed.contains(&glutin::MouseButton::Left)
    }

    pub fn left_released(&self) -> bool {
        self.released.contains(&glutin::MouseButton::Left)
    }

    pub fn right_pushed(&self) -> bool {
        self.pushed.contains(&glutin::MouseButton::Right)
    }

    pub fn right_released(&self) -> bool {
        self.released.contains(&glutin::MouseButton::Right)
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyState {
    pub down: HashSet<glutin::VirtualKeyCode>,
    pub pushed: HashSet<glutin::VirtualKeyCode>,
    pub released: HashSet<glutin::VirtualKeyCode>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InputState {
    pub mouse:MouseState,
    pub keys:KeyState,
    pub close:bool,
}

pub fn is_close_event(event: &glutin::Event) -> bool {
    match event {
        &glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) | &glutin::Event::Closed => true,
        _ => false,
    }
}

pub fn produce(input:&InputState, events: &Vec<glutin::Event>) -> InputState {
    let mut next_input = input.clone();

    next_input.keys.pushed.clear();
    next_input.keys.released.clear();
    next_input.mouse.pushed.clear();
    next_input.mouse.released.clear();

    next_input.mouse.mouse_wheel_delta = 0;

    for event in events {
        if is_close_event(&event) {
            next_input.close = true;
        }
        match event {
            &glutin::Event::KeyboardInput(element_state, _, Some(key_code)) =>
                match element_state {
                    glutin::ElementState::Pressed => {
                        let was_down = next_input.keys.down.contains(&key_code);
                        next_input.keys.down.insert(key_code);
                        if !was_down {
                            next_input.keys.pushed.insert(key_code);
                        }
                    },
                    glutin::ElementState::Released => {
                        let was_down = next_input.keys.down.contains(&key_code);
                        next_input.keys.down.remove(&key_code);
                        if !was_down {
                            next_input.keys.released.insert(key_code);
                        }
                    }
                },
            &glutin::Event::MouseWheel(glutin::MouseScrollDelta::LineDelta(_, mouse_scroll_delta), _) => {
                next_input.mouse.mouse_wheel_delta += (mouse_scroll_delta * 100.0) as i32;
            },
            &glutin::Event::MouseInput(element_state, mouse_button) =>
                match element_state {
                    glutin::ElementState::Pressed => {
                        let was_down = next_input.mouse.down.contains(&mouse_button);
                        next_input.mouse.down.insert(mouse_button);
                        if !was_down {
                            next_input.mouse.pushed.insert(mouse_button);
                        }
                    },
                    glutin::ElementState::Released => {
                        let was_down = next_input.mouse.down.contains(&mouse_button);
                        next_input.mouse.down.remove(&mouse_button);
                        if was_down {
                            next_input.mouse.released.insert(mouse_button);
                        }
                    },
                },
            &glutin::Event::MouseMoved(x, y) => next_input.mouse.at = (x, y),
            _ => (),
        };
    }

    next_input
}

impl InputState {
    pub fn default() -> InputState {
        InputState {
            mouse: MouseState {
                at: (0, 0),
                down: HashSet::new(),
                pushed: HashSet::new(),
                released: HashSet::new(),
                mouse_wheel_delta: 0,
            },
            keys: KeyState {
                down: HashSet::new(),
                pushed: HashSet::new(),
                released: HashSet::new(),
            },
            close: false,
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

fn run_command_or_fail<P, S>(dir: &str, cmd: P, args: &[S])
where
    P: AsRef<Path>,
    S: Borrow<str> + AsRef<OsStr>,
{
    let cmd = cmd.as_ref();
    let cmd = if cmd.components().count() > 1 && cmd.is_relative() {
        // If `cmd` is a relative path (and not a bare command that should be
        // looked up in PATH), absolutize it relative to `dir`, as otherwise the
        // behavior of std::process::Command is undefined.
        // https://github.com/rust-lang/rust/issues/37868
        PathBuf::from(dir)
            .join(cmd)
            .canonicalize()
            .expect("canonicalization failed")
    } else {
        PathBuf::from(cmd)
    };
    eprintln!(
        "Running command: \"{} {}\" in dir: {}",
        cmd.display(),
        args.join(" "),
        dir
    );
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => return,
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    let librdkafka_version = env!("CARGO_PKG_VERSION")
        .split('-')
        .next()
        .expect("Crate version is not valid");

    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        let openssl_root = env::var("DEP_OPENSSL_ROOT").expect("DEP_OPENSSL_ROOT is not set");
        env::set_var("CFLAGS", format!("-I{}/include", openssl_root));
        env::set_var("LDFLAGS", format!("-L{}/lib", openssl_root));
    }

    if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
        eprintln!("librdkafka will be linked dynamically");
        let pkg_probe = pkg_config::Config::new()
            .cargo_metadata(true)
            .atleast_version(librdkafka_version)
            .probe("rdkafka");

        match pkg_probe {
            Ok(library) => {
                eprintln!("librdkafka found on the system:");
                eprintln!("  Name: {:?}", library.libs);
                eprintln!("  Path: {:?}", library.link_paths);
                eprintln!("  Version: {}", library.version);
            }
            Err(_) => {
                eprintln!(
                    "librdkafka {} cannot be found on the system",
                    librdkafka_version
                );
                eprintln!("Dynamic linking failed. Exiting.");
                process::exit(1);
            }
        }
    } else {
        if !Path::new("librdkafka/LICENSE").exists() {
            eprintln!("Setting up submodules");
            run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
        }
        eprintln!("Building and linking librdkafka statically");
        build_librdkafka();
    }
}

#[cfg(not(feature = "cmake_build"))]
fn build_librdkafka() {
    let mut configure_flags: Vec<String> = Vec::new();
    let mut cflags = Vec::new();
    let mut ldflags = Vec::new();

    if env::var("CARGO_FEATURE_SASL").is_ok() {
        configure_flags.push("--enable-sasl".into());
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        configure_flags.push("--disable-sasl".into());
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl".into());
    } else {
        configure_flags.push("--disable-ssl".into());
    }
    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        let openssl_root = env::var("DEP_OPENSSL_ROOT").expect("DEP_OPENSSL_ROOT is not set");
        cflags.push(format!("-I{}/include", openssl_root));
        ldflags.push(format!("-L{}/lib", openssl_root));
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        configure_flags.push("--enable-zstd".into());
    } else {
        configure_flags.push("--disable-zstd".into());
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        configure_flags.push("--enable-lz4".into());
    } else {
        configure_flags.push("--disable-lz4".into());
    }

    if !cflags.is_empty() {
        configure_flags.push(format!("--CFLAGS={}", cflags.join(" ")));
    }
    if !ldflags.is_empty() {
        configure_flags.push(format!("--LDFLAGS={}", ldflags.join(" ")));
    }

    println!("Configuring librdkafka");
    run_command_or_fail("librdkafka", "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    make_librdkafka();

    println!(
        "cargo:rustc-link-search=native={}/librdkafka/src",
        env::current_dir()
            .expect("Can't find current dir")
            .display()
    );
    println!("cargo:rustc-link-lib=static=rdkafka");
}

#[cfg(not(feature = "cmake_build"))]
fn make_librdkafka() {
    run_command_or_fail(
        "librdkafka",
        if cfg!(target_os = "freebsd") { "gmake" } else { "make" },
        &["-j", &num_cpus::get().to_string(), "libs"],
    );
}

#[cfg(feature = "cmake_build")]
fn build_librdkafka() {
    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let mut config = cmake::Config::new("librdkafka");
    config
        .define("RDKAFKA_BUILD_STATIC", "1")
        .build_target("rdkafka");
    if env::var("CARGO_FEATURE_SSL").is_ok() {
        config.define("WITH_SSL", "1");
    } else {
        config.define("WITH_SSL", "0");
    }
    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        config.register_dep("openssl");
    }
    if env::var("CARGO_FEATURE_SASL").is_ok() {
        config.define("WITH_SASL", "1");
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        config.define("WITH_SASL", "0");
    }
    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        config.define("WITH_ZSTD", "1");
        config.register_dep("zstd");
    } else {
        config.define("WITH_ZSTD", "0");
    }
    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        config.define("ENABLE_LZ4_EXT", "1");
    } else {
        config.define("ENABLE_LZ4_EXT", "0");
    }
    if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") {
        config.define("CMAKE_SYSTEM_NAME", system_name);
    }
    // The CMake build will incorrectly use config.h from the non-CMake build,
    // if it exists, so remove it if it does.
    match std::fs::remove_file("librdkafka/config.h") {
        Ok(()) => (),
        Err(err) => match err.kind() {
            std::io::ErrorKind::NotFound => (),
            _ => panic!("Unable to remove config.h from non-CMake build: {}", err),
        }
    }
    let dst = config.build();
    #[cfg(target_env = "msvc")]
    {
        let profile = match &env::var("PROFILE").expect("Cannot determine build profile")[..] {
            "release" | "bench" => "Release",
            _ => "Debug"
        };
        println!("cargo:rustc-link-search=native={}/build/src/{}", dst.display(), profile);
    }
    #[cfg(not(target_env = "msvc"))]
    println!("cargo:rustc-link-search=native={}/build/src", dst.display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<commit_msg>Reformat rdkafka-sys build script<commit_after>use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

fn run_command_or_fail<P, S>(dir: &str, cmd: P, args: &[S])
where
    P: AsRef<Path>,
    S: Borrow<str> + AsRef<OsStr>,
{
    let cmd = cmd.as_ref();
    let cmd = if cmd.components().count() > 1 && cmd.is_relative() {
        // If `cmd` is a relative path (and not a bare command that should be
        // looked up in PATH), absolutize it relative to `dir`, as otherwise the
        // behavior of std::process::Command is undefined.
        // https://github.com/rust-lang/rust/issues/37868
        PathBuf::from(dir)
            .join(cmd)
            .canonicalize()
            .expect("canonicalization failed")
    } else {
        PathBuf::from(cmd)
    };
    eprintln!(
        "Running command: \"{} {}\" in dir: {}",
        cmd.display(),
        args.join(" "),
        dir
    );
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => return,
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    let librdkafka_version = env!("CARGO_PKG_VERSION")
        .split('-')
        .next()
        .expect("Crate version is not valid");

    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        let openssl_root = env::var("DEP_OPENSSL_ROOT").expect("DEP_OPENSSL_ROOT is not set");
        env::set_var("CFLAGS", format!("-I{}/include", openssl_root));
        env::set_var("LDFLAGS", format!("-L{}/lib", openssl_root));
    }

    if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
        eprintln!("librdkafka will be linked dynamically");
        let pkg_probe = pkg_config::Config::new()
            .cargo_metadata(true)
            .atleast_version(librdkafka_version)
            .probe("rdkafka");

        match pkg_probe {
            Ok(library) => {
                eprintln!("librdkafka found on the system:");
                eprintln!("  Name: {:?}", library.libs);
                eprintln!("  Path: {:?}", library.link_paths);
                eprintln!("  Version: {}", library.version);
            }
            Err(_) => {
                eprintln!(
                    "librdkafka {} cannot be found on the system",
                    librdkafka_version
                );
                eprintln!("Dynamic linking failed. Exiting.");
                process::exit(1);
            }
        }
    } else {
        if !Path::new("librdkafka/LICENSE").exists() {
            eprintln!("Setting up submodules");
            run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
        }
        eprintln!("Building and linking librdkafka statically");
        build_librdkafka();
    }
}

#[cfg(not(feature = "cmake_build"))]
fn build_librdkafka() {
    let mut configure_flags: Vec<String> = Vec::new();
    let mut cflags = Vec::new();
    let mut ldflags = Vec::new();

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl".into());
    } else {
        configure_flags.push("--disable-ssl".into());
    }
    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        let openssl_root = env::var("DEP_OPENSSL_ROOT").expect("DEP_OPENSSL_ROOT is not set");
        cflags.push(format!("-I{}/include", openssl_root));
        ldflags.push(format!("-L{}/lib", openssl_root));
    }

    if env::var("CARGO_FEATURE_SASL").is_ok() {
        configure_flags.push("--enable-sasl".into());
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        configure_flags.push("--disable-sasl".into());
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        configure_flags.push("--enable-zstd".into());
    } else {
        configure_flags.push("--disable-zstd".into());
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        configure_flags.push("--enable-lz4".into());
    } else {
        configure_flags.push("--disable-lz4".into());
    }

    if !cflags.is_empty() {
        configure_flags.push(format!("--CFLAGS={}", cflags.join(" ")));
    }
    if !ldflags.is_empty() {
        configure_flags.push(format!("--LDFLAGS={}", ldflags.join(" ")));
    }

    println!("Configuring librdkafka");
    run_command_or_fail("librdkafka", "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    run_command_or_fail(
        "librdkafka",
        if cfg!(target_os = "freebsd") { "gmake" } else { "make" },
        &["-j", &num_cpus::get().to_string(), "libs"],
    );

    println!(
        "cargo:rustc-link-search=native={}/librdkafka/src",
        env::current_dir()
            .expect("Can't find current dir")
            .display()
    );
    println!("cargo:rustc-link-lib=static=rdkafka");
}

#[cfg(feature = "cmake_build")]
fn build_librdkafka() {
    env::set_var("NUM_JOBS", num_cpus::get().to_string());

    let mut config = cmake::Config::new("librdkafka");

    config
        .define("RDKAFKA_BUILD_STATIC", "1")
        .build_target("rdkafka");

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        config.define("WITH_SSL", "1");
    } else {
        config.define("WITH_SSL", "0");
    }
    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        config.register_dep("openssl");
    }

    if env::var("CARGO_FEATURE_SASL").is_ok() {
        config.define("WITH_SASL", "1");
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        config.define("WITH_SASL", "0");
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        config.define("WITH_ZSTD", "1");
        config.register_dep("zstd");
    } else {
        config.define("WITH_ZSTD", "0");
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        config.define("ENABLE_LZ4_EXT", "1");
    } else {
        config.define("ENABLE_LZ4_EXT", "0");
    }

    if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") {
        config.define("CMAKE_SYSTEM_NAME", system_name);
    }

    // The CMake build will incorrectly use config.h from the non-CMake build,
    // if it exists, so remove it if it does.
    match std::fs::remove_file("librdkafka/config.h") {
        Ok(()) => (),
        Err(err) => match err.kind() {
            std::io::ErrorKind::NotFound => (),
            _ => panic!("Unable to remove config.h from non-CMake build: {}", err),
        }
    }

    println!("Configuring and compiling librdkafka");
    let dst = config.build();

    if cfg!(target_env = "msvc") {
        let profile = match &env::var("PROFILE").expect("Cannot determine build profile")[..] {
            "release" | "bench" => "Release",
            _ => "Debug"
        };
        println!("cargo:rustc-link-search=native={}/build/src/{}", dst.display(), profile);
    } else {
        println!("cargo:rustc-link-search=native={}/build/src", dst.display());
    }
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::{mem};

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

#[derive(Debug)]
struct Node {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

impl Node {
    pub fn new(weight: size_t, initial_count: c_int, capacity: usize) -> Node {
        let mut n = Node {
            weight: weight,
            leaf_counts: Vec::with_capacity(capacity),
        };
        n.leaf_counts.push(initial_count);
        n
    }
}

#[derive(Debug)]
struct Leaf {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for Leaf {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for Leaf { }
impl Ord for Leaf {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for Leaf {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: Node,
    lookahead2: Node,
    next_leaf_index: size_t,
}

/// Calculates the bitlengths for the Huffman tree, based on the counts of each
/// symbol.
pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<c_uint> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(Leaf { weight: freq, index: i });
        }
    }

    // Short circuit some special cases
    if leaves.is_empty() {
        // There are no non-zero frequencies.
        return vec![0; frequencies.len()];
    }
    if leaves.len() == 1 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        return result;
    }
    if leaves.len() == 2 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        result[leaves[1].index] = 1;
        return result;
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let max_num_leaves = 2 * leaves.len() - 2;

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: Node::new(leaves[0].weight, 1, max_num_leaves),
            lookahead2: Node::new(leaves[1].weight, 2, max_num_leaves),
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = max_num_leaves - 2;
    for _ in 0..num_boundary_pm_runs {
        boundary_pm_toplevel(&mut lists[..], &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn lowest_list(lists: &mut [List], leaves: &[Leaf]) {
    // We're in the lowest list, just add another leaf to the lookaheads
    // There will always be more leaves to be added on level 0 so this is safe.
    let mut current_list = lists.get_mut(0).unwrap();
    let ref next_leaf = leaves[current_list.next_leaf_index];
    current_list.lookahead2.weight = next_leaf.weight;

    current_list.lookahead2.leaf_counts[0] = current_list.lookahead1.leaf_counts.last().unwrap() + 1;
    current_list.next_leaf_index += 1;
}

fn next_leaf(lists: &mut [List], leaves: &[Leaf], current_list_index: usize) {
    let mut current_list = lists.get_mut(current_list_index).unwrap();

    // The next leaf goes next; counting itself makes the leaf_count increase by one.
    current_list.lookahead2.weight = leaves[current_list.next_leaf_index].weight;
    current_list.lookahead2.leaf_counts.clear();
    current_list.lookahead2.leaf_counts.extend(current_list.lookahead1.leaf_counts.iter());
    let last_index = current_list.lookahead2.leaf_counts.len() - 1;
    current_list.lookahead2.leaf_counts[last_index] += 1;
    current_list.next_leaf_index += 1;
}

fn next_tree(weight_sum: size_t, lists: &mut [List], leaves: &[Leaf], current_list_index: usize) {
    let num_leaf_counts = lists[current_list_index - 1].lookahead2.leaf_counts.len();
    let previous_list_leaf_counts = lists[current_list_index - 1].lookahead2.leaf_counts.as_ptr();
    {
        let ref mut current_list = lists[current_list_index];

        // Make a tree from the lookaheads from the previous list; that goes next.
        // This is not a leaf node, so the leaf count stays the same.
        current_list.lookahead2.weight = weight_sum;
        current_list.lookahead2.leaf_counts.clear();
        for i in 0..num_leaf_counts {
            current_list.lookahead2.leaf_counts.push(unsafe {
                *previous_list_leaf_counts.offset(i as isize)
            });
        }
        current_list.lookahead2.leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());
    }

    // The previous list needs two new lookahead nodes.
    boundary_pm(lists, leaves, current_list_index - 1);
    boundary_pm(lists, leaves, current_list_index - 1);
}

fn boundary_pm_toplevel(lists: &mut [List], leaves: &[Leaf]) {
    let last_index = lists.len() - 1;
    boundary_pm(lists, leaves, last_index);
}

fn boundary_pm(lists: &mut [List], leaves: &[Leaf], current_list_index: usize) {
    let next_leaf_index = lists[current_list_index].next_leaf_index;

    if current_list_index == 0 && next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        return;
    }

    mem::swap(&mut lists[current_list_index].lookahead1, &mut lists[current_list_index].lookahead2);

    if current_list_index == 0 {
        lowest_list(lists, leaves);
    } else {
        // We're at a list other than the lowest list.
        let weight_sum = {
            let previous_list = lists.get(current_list_index - 1).unwrap();
            previous_list.lookahead1.weight + previous_list.lookahead2.weight
        };

        if next_leaf_index < leaves.len() && weight_sum > leaves[next_leaf_index].weight {
            next_leaf(lists, leaves, current_list_index);
        } else {
            next_tree(weight_sum, lists, leaves, current_list_index);
        }
    }
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn no_frequencies() {
        let input = [0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_one_frequency() {
        let input = [0, 10, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 1, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_two_frequencies() {
        let input = [0, 0, 0, 0, 252, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<commit_msg>Make iter_muts more idiomatic, thanks clippy!!!<commit_after>use std::cmp::Ordering;
use std::{mem};

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

#[derive(Debug)]
struct Node {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

impl Node {
    pub fn new(weight: size_t, initial_count: c_int, capacity: usize) -> Node {
        let mut n = Node {
            weight: weight,
            leaf_counts: Vec::with_capacity(capacity),
        };
        n.leaf_counts.push(initial_count);
        n
    }
}

#[derive(Debug)]
struct Leaf {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for Leaf {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for Leaf { }
impl Ord for Leaf {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for Leaf {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: Node,
    lookahead2: Node,
    next_leaf_index: size_t,
}

/// Calculates the bitlengths for the Huffman tree, based on the counts of each
/// symbol.
pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<c_uint> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(Leaf { weight: freq, index: i });
        }
    }

    // Short circuit some special cases
    if leaves.is_empty() {
        // There are no non-zero frequencies.
        return vec![0; frequencies.len()];
    }
    if leaves.len() == 1 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        return result;
    }
    if leaves.len() == 2 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        result[leaves[1].index] = 1;
        return result;
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in &mut leaves {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in &mut leaves {
        leaf.weight >>= 9;
    }

    let max_num_leaves = 2 * leaves.len() - 2;

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: Node::new(leaves[0].weight, 1, max_num_leaves),
            lookahead2: Node::new(leaves[1].weight, 2, max_num_leaves),
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = max_num_leaves - 2;
    for _ in 0..num_boundary_pm_runs {
        boundary_pm_toplevel(&mut lists[..], &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn lowest_list(lists: &mut [List], leaves: &[Leaf]) {
    // We're in the lowest list, just add another leaf to the lookaheads
    // There will always be more leaves to be added on level 0 so this is safe.
    let mut current_list = lists.get_mut(0).unwrap();
    let ref next_leaf = leaves[current_list.next_leaf_index];
    current_list.lookahead2.weight = next_leaf.weight;

    current_list.lookahead2.leaf_counts[0] = current_list.lookahead1.leaf_counts.last().unwrap() + 1;
    current_list.next_leaf_index += 1;
}

fn next_leaf(lists: &mut [List], leaves: &[Leaf], current_list_index: usize) {
    let mut current_list = lists.get_mut(current_list_index).unwrap();

    // The next leaf goes next; counting itself makes the leaf_count increase by one.
    current_list.lookahead2.weight = leaves[current_list.next_leaf_index].weight;
    current_list.lookahead2.leaf_counts.clear();
    current_list.lookahead2.leaf_counts.extend(current_list.lookahead1.leaf_counts.iter());
    let last_index = current_list.lookahead2.leaf_counts.len() - 1;
    current_list.lookahead2.leaf_counts[last_index] += 1;
    current_list.next_leaf_index += 1;
}

fn next_tree(weight_sum: size_t, lists: &mut [List], leaves: &[Leaf], current_list_index: usize) {
    let num_leaf_counts = lists[current_list_index - 1].lookahead2.leaf_counts.len();
    let previous_list_leaf_counts = lists[current_list_index - 1].lookahead2.leaf_counts.as_ptr();
    {
        let ref mut current_list = lists[current_list_index];

        // Make a tree from the lookaheads from the previous list; that goes next.
        // This is not a leaf node, so the leaf count stays the same.
        current_list.lookahead2.weight = weight_sum;
        current_list.lookahead2.leaf_counts.clear();
        for i in 0..num_leaf_counts {
            current_list.lookahead2.leaf_counts.push(unsafe {
                *previous_list_leaf_counts.offset(i as isize)
            });
        }
        current_list.lookahead2.leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());
    }

    // The previous list needs two new lookahead nodes.
    boundary_pm(lists, leaves, current_list_index - 1);
    boundary_pm(lists, leaves, current_list_index - 1);
}

fn boundary_pm_toplevel(lists: &mut [List], leaves: &[Leaf]) {
    let last_index = lists.len() - 1;
    boundary_pm(lists, leaves, last_index);
}

fn boundary_pm(lists: &mut [List], leaves: &[Leaf], current_list_index: usize) {
    let next_leaf_index = lists[current_list_index].next_leaf_index;

    if current_list_index == 0 && next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        return;
    }

    mem::swap(&mut lists[current_list_index].lookahead1, &mut lists[current_list_index].lookahead2);

    if current_list_index == 0 {
        lowest_list(lists, leaves);
    } else {
        // We're at a list other than the lowest list.
        let weight_sum = {
            let previous_list = lists.get(current_list_index - 1).unwrap();
            previous_list.lookahead1.weight + previous_list.lookahead2.weight
        };

        if next_leaf_index < leaves.len() && weight_sum > leaves[next_leaf_index].weight {
            next_leaf(lists, leaves, current_list_index);
        } else {
            next_tree(weight_sum, lists, leaves, current_list_index);
        }
    }
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn no_frequencies() {
        let input = [0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_one_frequency() {
        let input = [0, 10, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 1, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_two_frequencies() {
        let input = [0, 0, 0, 0, 252, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<|endoftext|>


<commit_before>use self::Token::*;
use self::Element::*;
use self::ComparisonOperator::*;
use regex::Regex;

static MARKUP     : Regex = regex!("\\{%.*?%\\}|\\{\\{.*?\\}\\}");
static TAG        : Regex = regex!("\\{%(.*?)%\\}");
static OUTPUT     : Regex = regex!("\\{\\{(.*?)\\}\\}");
static WHITESPACE : Regex = regex!(r"\s+");

static IDENTIFIER            : Regex = regex!(r"[a-zA-Z_][\w-]*\??");
static SINGLE_STRING_LITERAL : Regex = regex!(r"'[^']*'");
static DOUBLE_STRING_LITERAL : Regex = regex!("\"[^\"]*\"");
static NUMBER_LITERAL        : Regex = regex!(r"-?\d+(\.\d+)?");
static DOTDOT                : Regex = regex!(r"\.\.");

#[deriving(Clone, Show, PartialEq)]
pub enum ComparisonOperator{
    Equals, NotEquals,
    LessThan, GreaterThan,
    LessThanEquals, GreaterThanEquals,
    Contains
}

#[deriving(Clone, Show, PartialEq)]
pub enum Token {
    Pipe,
    Dot,
    Colon,
    Comma,
    OpenSquare,
    CloseSquare,
    OpenRound,
    CloseRound,
    Question,
    Dash,

    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f32),
    DotDot,
    Comparison(ComparisonOperator)
}

#[deriving(Clone, Show, PartialEq)]
pub enum Element{
    Output(Vec<Token>, String),
    Tag(Vec<Token>, String),
    Raw(String)
}

fn split_blocks(text: &str) -> Vec<&str>{
    let mut tokens = vec![];
    let mut current = 0;
    for (begin, end) in MARKUP.find_iter(text) {
        match text.slice(current, begin){
            "" => {}
            t => tokens.push(t)
        };
        tokens.push(text.slice(begin, end));
        current = end;
    }
    match text.slice(current, text.len()){
        "" => {}
        t => tokens.push(t)
    };
    tokens
}

pub fn tokenize(text: &str) -> Vec<Element> {
    split_blocks(text).iter().map(|block| {
        if TAG.is_match(*block) {
            let caps = TAG.captures(*block).unwrap();
            Tag(granularize(caps.at(1)), block.to_string())
        }else if OUTPUT.is_match(*block) {
            let caps = OUTPUT.captures(*block).unwrap();
            Output(granularize(caps.at(1)), block.to_string())
        }else{
            Raw(block.to_string())
        }
    }).collect()
}

fn granularize(block: &str) -> Vec<Token>{
    WHITESPACE.split(block).map(|el|{
        match el {
            "|" => Pipe,
            "." => Dot,
            ":" => Colon,
            "," => Comma,
            "[" => OpenSquare,
            "]" => CloseSquare,
            "(" => OpenRound,
            ")" => CloseRound,
            "?" => Question,
            "-" => Dash,

            "=="       => Comparison(Equals),
            "!="       => Comparison(NotEquals),
            "<="       => Comparison(LessThanEquals),
            ">="       => Comparison(GreaterThanEquals),
            "<"        => Comparison(LessThan),
            ">"        => Comparison(GreaterThan),
            "contains" => Comparison(Contains),

            x if DOTDOT.is_match(x) => DotDot,
            x if SINGLE_STRING_LITERAL.is_match(x) => StringLiteral(x[1..x.len() -1].to_string()),
            x if DOUBLE_STRING_LITERAL.is_match(x) => StringLiteral(x[1..x.len() -1].to_string()),
            x if NUMBER_LITERAL.is_match(x) => NumberLiteral(from_str::<f32>(x).unwrap()),
            x if IDENTIFIER.is_match(x) => Identifier(x.to_string()),
            x => panic!("{} is not a valid identifier", x)
        }
    }).collect()
}

#[test]
fn test_split_blocks() {
    assert_eq!(split_blocks("asdlkjfn\n{{askdljfbalkjsdbf}} asdjlfb"),
                vec!["asdlkjfn\n", "{{askdljfbalkjsdbf}}", " asdjlfb"]);
    assert_eq!(split_blocks("asdlkjfn\n{%askdljfbalkjsdbf%} asdjlfb"),
                vec!["asdlkjfn\n", "{%askdljfbalkjsdbf%}", " asdjlfb"]);
}

#[test]
fn test_tokenize() {
    assert_eq!(tokenize("wat\n{{hello 'world'}} test"), vec![
               Raw("wat\n".to_string()), Output(vec![Identifier("hello".to_string()), StringLiteral("world".to_string())], "{{hello 'world'}}".to_string()), Raw(" test".to_string())
               ]);
}

#[test]
fn test_granularize() {
    assert_eq!(granularize("test me"), vec![Identifier("test".to_string()), Identifier("me".to_string())]);
    assert_eq!(granularize("test == me"), vec![Identifier("test".to_string()), Comparison(Equals), Identifier("me".to_string())]);
    assert_eq!(granularize("'test' == \"me\""), vec![StringLiteral("test".to_string()), Comparison(Equals), StringLiteral("me".to_string())]);
}

<commit_msg>Fix ignoring whitespace inside tags<commit_after>use self::Token::*;
use self::Element::*;
use self::ComparisonOperator::*;
use regex::Regex;

static MARKUP     : Regex = regex!("\\{%.*?%\\}|\\{\\{.*?\\}\\}");
static TAG        : Regex = regex!("\\{%(.*?)%\\}");
static OUTPUT     : Regex = regex!("\\{\\{(.*?)\\}\\}");
static WHITESPACE : Regex = regex!(r"\s+");

static IDENTIFIER            : Regex = regex!(r"[a-zA-Z_][\w-]*\??");
static SINGLE_STRING_LITERAL : Regex = regex!(r"'[^']*'");
static DOUBLE_STRING_LITERAL : Regex = regex!("\"[^\"]*\"");
static NUMBER_LITERAL        : Regex = regex!(r"-?\d+(\.\d+)?");
static DOTDOT                : Regex = regex!(r"\.\.");

#[deriving(Clone, Show, PartialEq)]
pub enum ComparisonOperator{
    Equals, NotEquals,
    LessThan, GreaterThan,
    LessThanEquals, GreaterThanEquals,
    Contains
}

#[deriving(Clone, Show, PartialEq)]
pub enum Token {
    Pipe,
    Dot,
    Colon,
    Comma,
    OpenSquare,
    CloseSquare,
    OpenRound,
    CloseRound,
    Question,
    Dash,

    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f32),
    DotDot,
    Comparison(ComparisonOperator)
}

#[deriving(Clone, Show, PartialEq)]
pub enum Element{
    Output(Vec<Token>, String),
    Tag(Vec<Token>, String),
    Raw(String)
}

fn split_blocks(text: &str) -> Vec<&str>{
    let mut tokens = vec![];
    let mut current = 0;
    for (begin, end) in MARKUP.find_iter(text) {
        match text.slice(current, begin){
            "" => {}
            t => tokens.push(t)
        };
        tokens.push(text.slice(begin, end));
        current = end;
    }
    match text.slice(current, text.len()){
        "" => {}
        t => tokens.push(t)
    };
    tokens
}

pub fn tokenize(text: &str) -> Vec<Element> {
    split_blocks(text).iter().map(|block| {
        if TAG.is_match(*block) {
            let caps = TAG.captures(*block).unwrap();
            Tag(granularize(caps.at(1)), block.to_string())
        }else if OUTPUT.is_match(*block) {
            let caps = OUTPUT.captures(*block).unwrap();
            Output(granularize(caps.at(1)), block.to_string())
        }else{
            Raw(block.to_string())
        }
    }).collect()
}

fn granularize(block: &str) -> Vec<Token>{
    WHITESPACE.split(block).filter_map(|el|{
        if el == ""{
            return None;
        }
        Some(match el {
            "|" => Pipe,
            "." => Dot,
            ":" => Colon,
            "," => Comma,
            "[" => OpenSquare,
            "]" => CloseSquare,
            "(" => OpenRound,
            ")" => CloseRound,
            "?" => Question,
            "-" => Dash,

            "=="       => Comparison(Equals),
            "!="       => Comparison(NotEquals),
            "<="       => Comparison(LessThanEquals),
            ">="       => Comparison(GreaterThanEquals),
            "<"        => Comparison(LessThan),
            ">"        => Comparison(GreaterThan),
            "contains" => Comparison(Contains),

            x if DOTDOT.is_match(x) => DotDot,
            x if SINGLE_STRING_LITERAL.is_match(x) => StringLiteral(x[1..x.len() -1].to_string()),
            x if DOUBLE_STRING_LITERAL.is_match(x) => StringLiteral(x[1..x.len() -1].to_string()),
            x if NUMBER_LITERAL.is_match(x) => NumberLiteral(from_str::<f32>(x).unwrap()),
            x if IDENTIFIER.is_match(x) => Identifier(x.to_string()),
            x => panic!("{} is not a valid identifier", x)
        })
    }).collect()
}

#[test]
fn test_split_blocks() {
    assert_eq!(split_blocks("asdlkjfn\n{{askdljfbalkjsdbf}} asdjlfb"),
                vec!["asdlkjfn\n", "{{askdljfbalkjsdbf}}", " asdjlfb"]);
    assert_eq!(split_blocks("asdlkjfn\n{%askdljfbalkjsdbf%} asdjlfb"),
                vec!["asdlkjfn\n", "{%askdljfbalkjsdbf%}", " asdjlfb"]);
}

#[test]
fn test_tokenize() {
    assert_eq!(tokenize("{{hello 'world'}}"), vec![
               Output(vec![Identifier("hello".to_string()), StringLiteral("world".to_string())], "{{hello 'world'}}".to_string())
               ]);
    assert_eq!(tokenize("{{ hello 'world' }}"), vec![
               Output(vec![Identifier("hello".to_string()), StringLiteral("world".to_string())], "{{ hello 'world' }}".to_string())
               ]);
    assert_eq!(tokenize("{{   hello   'world'    }}"), vec![
               Output(vec![Identifier("hello".to_string()), StringLiteral("world".to_string())], "{{   hello   'world'    }}".to_string())
               ]);
    assert_eq!(tokenize("wat\n{{hello 'world'}} test"), vec![
               Raw("wat\n".to_string()), Output(vec![Identifier("hello".to_string()), StringLiteral("world".to_string())], "{{hello 'world'}}".to_string()), Raw(" test".to_string())
               ]);
}

#[test]
fn test_granularize() {
    assert_eq!(granularize("test me"), vec![Identifier("test".to_string()), Identifier("me".to_string())]);
    assert_eq!(granularize("test == me"), vec![Identifier("test".to_string()), Comparison(Equals), Identifier("me".to_string())]);
    assert_eq!(granularize("'test' == \"me\""), vec![StringLiteral("test".to_string()), Comparison(Equals), StringLiteral("me".to_string())]);
}

<|endoftext|>


<commit_before>extern crate hyper;

use std::io::Read;
use std::sync::Mutex;

use self::hyper::net::Fresh;
use self::hyper::client::Client;
use self::hyper::header::Connection;
use self::hyper::header::Host;
use self::hyper::server::Handler;
use self::hyper::server::Request;
use self::hyper::server::Response;

use http::app_manager::AppManager;

pub struct Dispatcher {
    pub app_manager: Mutex<AppManager>,
}

impl Dispatcher {
    pub fn new(app_manager: AppManager) -> Dispatcher {
        Dispatcher {
            app_manager: Mutex::new(app_manager),
        }
    }

    fn ensure_app_running(&self, request: &Request) -> u16 {
        let host = request.headers.get::<Host>().unwrap().hostname.to_string();
        let app_name = host.split(".").collect::<Vec<_>>().first().unwrap().to_string();

        let mut app_manager = self.app_manager.lock().unwrap();

        app_manager.ensure_app_running(&app_name)
    }
}

impl Handler for Dispatcher {
    fn handle(&self, request: Request, response: Response<Fresh>) {
        let port = self.ensure_app_running(&request);

        let url = format!("http://localhost:{}", port);

        let client = Client::new();

        let mut app_response = client.get(&url)
            .header(Connection::close())
            .send().unwrap();

        let mut body = String::new();
        app_response.read_to_string(&mut body).unwrap();

        response.send(&body.into_bytes()).unwrap();
    }
}
<commit_msg>refactor: Remove unecessary heap allocations<commit_after>extern crate hyper;

use std::io::Read;
use std::sync::Mutex;

use self::hyper::net::Fresh;
use self::hyper::client::Client;
use self::hyper::header::Connection;
use self::hyper::header::Host;
use self::hyper::server::Handler;
use self::hyper::server::Request;
use self::hyper::server::Response;

use http::app_manager::AppManager;

pub struct Dispatcher {
    pub app_manager: Mutex<AppManager>,
}

impl Dispatcher {
    pub fn new(app_manager: AppManager) -> Dispatcher {
        Dispatcher {
            app_manager: Mutex::new(app_manager),
        }
    }

    fn ensure_app_running(&self, request: &Request) -> u16 {
        let host = &request.headers.get::<Host>().unwrap().hostname;
        let host_parts = host.split(".").collect::<Vec<_>>();
        let app_name = host_parts.first().unwrap();

        let mut app_manager = self.app_manager.lock().unwrap();

        app_manager.ensure_app_running(&app_name)
    }
}

impl Handler for Dispatcher {
    fn handle(&self, request: Request, response: Response<Fresh>) {
        let port = self.ensure_app_running(&request);

        let url = format!("http://localhost:{}", port);

        let client = Client::new();

        let mut app_response = client.get(&url)
            .header(Connection::close())
            .send().unwrap();

        let mut body = String::new();
        app_response.read_to_string(&mut body).unwrap();

        response.send(&body.into_bytes()).unwrap();
    }
}
<|endoftext|>