<commit_before>#![crate_id = "decodeopts"]
#![crate_type = "lib"];

extern crate getopts;
extern crate serialize;
use getopts::Matches;
use std::from_str::FromStr;
use std::str::StrSlice;

pub struct Decoder {
  priv matches: Matches,
  priv cur: ~str,
  priv current_type: ~str
}

impl Decoder {
  pub fn new(matches: Matches) -> Decoder {
    Decoder {
      matches: matches,
      cur: ~"",
      current_type: ~""
    }
  }

  fn err(&self, msg: &str) -> ! {
    fail!("Options decoding error: {}", msg);
  }

  fn missing_field(&self, field: &str) -> ! {
    self.err(format!("missing option '{}'", field));
  }

  fn expected(&self, expected: &str, field: &str) -> ! {
    self.err(format!("expected {expct} but found {fld}: {val}",
                   expct=expected, fld=field, val=self.matches.opt_str(self.cur).unwrap()))
  }

}

impl<T:FromStr> Decoder {
  fn get_field<T:FromStr>(&self, field: &str) -> Option<T> {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => FromStr::from_str(s)
    }
  }
}

impl serialize::Decoder for Decoder {

  fn read_nil(&mut self) {

  }

  fn read_u64(&mut self)  -> u64  { self.read_f64() as u64 }
  fn read_u32(&mut self)  -> u32  { self.read_f64() as u32 }
  fn read_u16(&mut self)  -> u16  { self.read_f64() as u16 }
  fn read_u8 (&mut self)  -> u8   { self.read_f64() as u8 }
  fn read_uint(&mut self) -> uint { self.read_f64() as uint }

  fn read_i64(&mut self) -> i64 { self.read_f64() as i64 }
  fn read_i32(&mut self) -> i32 { self.read_f64() as i32 }
  fn read_i16(&mut self) -> i16 { self.read_f64() as i16 }
  fn read_i8 (&mut self) -> i8  { self.read_f64() as i8 }
  fn read_int(&mut self) -> int { self.read_f64() as int }

  fn read_f32(&mut self) -> f32 { self.read_f64() as f32 }
  fn read_f64(&mut self) -> f64 {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => match FromStr::from_str(s) {
        None     => self.expected("f64", self.cur),
        Some(nb) => nb
      }
    }
  }

  fn read_bool(&mut self) -> bool {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => match FromStr::from_str(s) {
        None     => self.expected("boolean", self.cur),
        Some(b) => b
      }
    }
  }

  fn read_char(&mut self) -> char {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => if s.char_len() == 1 { s.char_at(0) } else { self.expected("char", self.cur) }
    }
  }

  fn read_str(&mut self) -> ~str {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => s
    }
  }

  fn read_enum<T>(&mut self, name: &str, f: |&mut Decoder| -> T) -> T {
    println!("reading enum: {}", name);
    self.current_type = name.to_owned();
    f(self)
  }

  fn read_enum_variant<T>(&mut self, names: &[&str], f: |&mut Decoder, uint| -> T) -> T {
    println!("reading enum variant({}): {}", self.cur, names);
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => match names.iter().position(|&e| e == s) {
        None    => self.expected(self.current_type + " enum", self.cur),
        Some(i) => f(self, i)
      }
    }
    //f(self, 0)
  }

  fn read_enum_variant_arg<T>(&mut self, a_idx: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading enum variant_arg: {}", a_idx);
    f(self)
  }

  fn read_enum_struct_variant<T>(&mut self, names: &[&str], f: |&mut Decoder, uint| -> T) -> T {
    println!("reading enum struct variant: {}", names);
    f(self, 0)
  }

  fn read_enum_struct_variant_field<T>(&mut self, f_name: &str, f_idx: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading enum struct variant field: {}, {}", f_name, f_idx);
    f(self)
  }

  fn read_struct<T>(&mut self, s_name: &str, len: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading struct: {} | len = {}", s_name, len);
    self.cur = s_name.to_owned();
    f(self)
  }

  fn read_struct_field<T>(&mut self, f_name: &str, f_idx: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading struct field: {} | idx = {}", f_name, f_idx);
    self.cur = f_name.to_owned();
    let data = f(self);
    //println!("got struct field data: {}", data);
    data
  }

  fn read_tuple<T>(&mut self, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0)
  }

  fn read_tuple_arg<T>(&mut self, a_idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self)
  }

  fn read_tuple_struct<T>(&mut self, s_name: &str, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0)
  }

  fn read_tuple_struct_arg<T>(&mut self, a_idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self)
  }

  fn read_option<T>(&mut self, f: |&mut Decoder, bool| -> T) -> T {
    f(self, true)
  }

  fn read_seq<T>(&mut self, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0)
  }

  fn read_seq_elt<T>(&mut self, idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self)
  }

  fn read_map<T>(&mut self, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0)
  }

  fn read_map_elt_key<T>(&mut self, idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self)
  }

  fn read_map_elt_val<T>(&mut self, idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self)
  }

}
<commit_msg>mark some methods as unimplemented<commit_after>#![crate_id = "decodeopts"]
#![crate_type = "lib"];

extern crate getopts;
extern crate serialize;
use getopts::Matches;
use std::from_str::FromStr;
use std::str::StrSlice;

pub struct Decoder {
  priv matches: Matches,
  priv cur: ~str,
  priv current_type: ~str
}

impl Decoder {
  pub fn new(matches: Matches) -> Decoder {
    Decoder {
      matches: matches,
      cur: ~"",
      current_type: ~""
    }
  }

  fn err(&self, msg: &str) -> ! {
    fail!("Options decoding error: {}", msg);
  }

  fn unimplemented(&self) -> ! {
    self.err("this mehod is not implemented yet");
  }

  fn missing_field(&self, field: &str) -> ! {
    self.err(format!("missing option '{}'", field));
  }

  fn expected(&self, expected: &str, field: &str) -> ! {
    self.err(format!("expected {expct} but found {fld}: {val}",
                   expct=expected, fld=field, val=self.matches.opt_str(self.cur).unwrap()))
  }

}

impl<T:FromStr> Decoder {
  fn get_field<T:FromStr>(&self, field: &str) -> Option<T> {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => FromStr::from_str(s)
    }
  }
}

impl serialize::Decoder for Decoder {

  fn read_nil(&mut self) {

  }

  fn read_u64(&mut self)  -> u64  { self.read_f64() as u64 }
  fn read_u32(&mut self)  -> u32  { self.read_f64() as u32 }
  fn read_u16(&mut self)  -> u16  { self.read_f64() as u16 }
  fn read_u8 (&mut self)  -> u8   { self.read_f64() as u8 }
  fn read_uint(&mut self) -> uint { self.read_f64() as uint }

  fn read_i64(&mut self) -> i64 { self.read_f64() as i64 }
  fn read_i32(&mut self) -> i32 { self.read_f64() as i32 }
  fn read_i16(&mut self) -> i16 { self.read_f64() as i16 }
  fn read_i8 (&mut self) -> i8  { self.read_f64() as i8 }
  fn read_int(&mut self) -> int { self.read_f64() as int }

  fn read_f32(&mut self) -> f32 { self.read_f64() as f32 }
  fn read_f64(&mut self) -> f64 {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => match FromStr::from_str(s) {
        None     => self.expected("f64", self.cur),
        Some(nb) => nb
      }
    }
  }

  fn read_bool(&mut self) -> bool {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => match FromStr::from_str(s) {
        None     => self.expected("boolean", self.cur),
        Some(b) => b
      }
    }
  }

  fn read_char(&mut self) -> char {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => if s.char_len() == 1 { s.char_at(0) } else { self.expected("char", self.cur) }
    }
  }

  fn read_str(&mut self) -> ~str {
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => s
    }
  }

  fn read_enum<T>(&mut self, name: &str, f: |&mut Decoder| -> T) -> T {
    println!("reading enum: {}", name);
    self.current_type = name.to_owned();
    f(self)
  }

  fn read_enum_variant<T>(&mut self, names: &[&str], f: |&mut Decoder, uint| -> T) -> T {
    println!("reading enum variant({}): {}", self.cur, names);
    match self.matches.opt_str(self.cur) {
      None    => self.missing_field(self.cur),
      Some(s) => match names.iter().position(|&e| e == s) {
        None    => self.expected(self.current_type + " enum", self.cur),
        Some(i) => f(self, i)
      }
    }
    //f(self, 0)
  }

  fn read_enum_variant_arg<T>(&mut self, a_idx: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading enum variant_arg: {}", a_idx);
    f(self);
    self.unimplemented()
  }

  fn read_enum_struct_variant<T>(&mut self, names: &[&str], f: |&mut Decoder, uint| -> T) -> T {
    println!("reading enum struct variant: {}", names);
    f(self, 0);
    self.unimplemented()
  }

  fn read_enum_struct_variant_field<T>(&mut self, f_name: &str, f_idx: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading enum struct variant field: {}, {}", f_name, f_idx);
    f(self);
    self.unimplemented()
  }

  fn read_struct<T>(&mut self, s_name: &str, len: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading struct: {} | len = {}", s_name, len);
    self.cur = s_name.to_owned();
    f(self)
  }

  fn read_struct_field<T>(&mut self, f_name: &str, f_idx: uint, f: |&mut Decoder| -> T) -> T {
    println!("reading struct field: {} | idx = {}", f_name, f_idx);
    self.cur = f_name.to_owned();
    let data = f(self);
    //println!("got struct field data: {}", data);
    data
  }

  fn read_option<T>(&mut self, f: |&mut Decoder, bool| -> T) -> T {
    f(self, true);
    self.unimplemented()
  }

  fn read_tuple<T>(&mut self, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0);
    self.unimplemented()
  }

  fn read_tuple_arg<T>(&mut self, a_idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self);
    self.unimplemented()
  }

  fn read_tuple_struct<T>(&mut self, s_name: &str, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0);
    self.unimplemented()
  }

  fn read_tuple_struct_arg<T>(&mut self, a_idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self);
    self.unimplemented()
  }

  fn read_seq<T>(&mut self, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0);
    self.unimplemented()
  }

  fn read_seq_elt<T>(&mut self, idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self);
    self.unimplemented()
  }

  fn read_map<T>(&mut self, f: |&mut Decoder, uint| -> T) -> T {
    f(self, 0);
    self.unimplemented()
  }

  fn read_map_elt_key<T>(&mut self, idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self);
    self.unimplemented()
  }

  fn read_map_elt_val<T>(&mut self, idx: uint, f: |&mut Decoder| -> T) -> T {
    f(self);
    self.unimplemented()
  }

}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;

// What does this do? You'll never guess!
type HeapId<'a> = PhantomData<std::cell::Cell<&'a mut ()>>;

// The heap is (for now) just a big Vec of Pairs
pub struct Heap<'a> {
    id: HeapId<'a>,
    storage: Vec<PairStorage<'a>>,
    freelist: *mut PairStorage<'a>,
    pins: RefCell<HashMap<*mut PairStorage<'a>, usize>>
}

pub const HEAP_SIZE: usize = 10000;

pub fn with_heap<F, O>(f: F) -> O
    where F: for<'a> FnOnce(&mut Heap<'a>) -> O
{
    f(&mut Heap::new())
}

unsafe trait Mark {
    unsafe fn mark(ptr: *mut Self);
}

impl<'a> Heap<'a> {
    fn new() -> Heap<'a> {
        // Allocate with the full capacity so that allocated Pairs never move
        let mut h = Heap {
            id: PhantomData,
            storage: Vec::with_capacity(HEAP_SIZE),
            freelist: std::ptr::null_mut(),
            pins: RefCell::new(HashMap::new())
        };
        for i in 0 .. HEAP_SIZE {
            h.storage.push(PairStorage {
                marked: false,
                head: ValueStorage::Null,
                tail: ValueStorage::Null
            });
            let p = &mut h.storage[i] as *mut PairStorage<'a>;
            unsafe {
                h.add_to_free_list(p);
            }
        }
        h
    }

    fn pin(&self, p: *mut PairStorage<'a>) {
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    fn unpin(&self, p: *mut PairStorage<'a>) {
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    unsafe fn add_to_free_list(&mut self, p: *mut PairStorage<'a>) {
        let listp: *mut *mut PairStorage<'a> = std::mem::transmute(p);
        *listp = self.freelist;
        assert_eq!(*listp, self.freelist);
        self.freelist = p;
    }

    pub fn try_alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Option<PairRoot<'a>> {
        if self.freelist.is_null() {
            unsafe {
                self.gc();
            }
            if self.freelist.is_null() {
                return None;
            }
        }

        let p = self.freelist;
        unsafe {
            let listp: *mut *mut PairStorage<'a> = std::mem::transmute(p);
            self.freelist = *listp;

            let (h, t) = pair;
            *p = PairStorage {
                marked: false,
                head: value_to_heap(h),
                tail: value_to_heap(t)
            };
            Some(PairRoot::new(self, p))
        }
    }

    pub fn alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> PairRoot<'a> {
        self.try_alloc(pair).expect("out of memory (gc did not collect anything)")
    }

    pub fn alloc_null(&mut self) -> PairRoot<'a> {
        self.alloc((Value::Null, Value::Null))
    }

    unsafe fn gc(&mut self) {
        // clear mark bits
        for p in &mut self.storage {
            p.marked = false;
        }

        // mark phase
        for (&p, _) in self.pins.borrow().iter() {
            Mark::mark(p);
        }

        // sweep phase
        self.freelist = std::ptr::null_mut();
        for i in 0 .. HEAP_SIZE {
            let p = &mut self.storage[i] as *mut PairStorage<'a>;
            if !(*p).marked {
                self.add_to_free_list(p);
            }
        }
    }

    #[cfg(test)]
    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

// === Pair, the reference type

// PairStorage is the only type that is actually allocated inside the heap (private)
struct PairStorage<'a> {
    marked: bool,
    head: ValueStorage<'a>,
    tail: ValueStorage<'a>
}

unsafe impl<'a> Mark for PairStorage<'a> {
    unsafe fn mark(ptr: *mut PairStorage<'a>) {
        if !ptr.is_null() && !(*ptr).marked {
            (*ptr).marked = true;
            Mark::mark(&mut (*ptr).head as *mut ValueStorage<'a>);
            Mark::mark(&mut (*ptr).tail as *mut ValueStorage<'a>);
        }
    }
}

// Handle to a PairStorage that lives in the heap.
#[allow(raw_pointer_derive)]
#[derive(Debug, PartialEq)]
pub struct PairRoot<'a> {
    ptr: *mut PairStorage<'a>,
    heap: *const Heap<'a>,
    heap_id: HeapId<'a>
}

impl<'a> Drop for PairRoot<'a> {
    fn drop(&mut self) {
        unsafe {
            (*self.heap).unpin(self.ptr);
        }
    }
}

impl<'a> Clone for PairRoot<'a> {
    fn clone(&self) -> PairRoot<'a> {
        let &PairRoot { ptr, heap, heap_id } = self;
        unsafe {
            (*heap).pin(ptr);
        }
        PairRoot {
            ptr: ptr,
            heap: heap,
            heap_id: heap_id
        }
    }
}

impl<'a> PairRoot<'a> {
    unsafe fn new(heap: &Heap<'a>, p: *mut PairStorage<'a>) -> PairRoot<'a> {
        heap.pin(p);
        PairRoot {
            ptr: p,
            heap: heap as *const Heap<'a>,
            heap_id: heap.id
        }
    }

    pub fn head(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { value_from_heap(&*self.heap, &(*ptr).head) }
    }

    pub fn tail(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { value_from_heap(&*self.heap, &(*ptr).tail) }
    }

    pub fn set_head(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).head = value_to_heap(v); }
    }

    pub fn set_tail(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).tail = value_to_heap(v); }
    }

    #[cfg(test)]
    pub fn address(&self) -> usize {
        unsafe { std::mem::transmute(self.ptr) }
    }
}

// === Values (a heap-inline enum)

// Values inside the heap (GC heap-to-heap cross-references) (private)
enum ValueStorage<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),
    Pair(*mut PairStorage<'a>, HeapId<'a>)
}

unsafe impl<'a> Mark for ValueStorage<'a> {
    unsafe fn mark(ptr: *mut ValueStorage<'a>) {
        match *ptr {
            ValueStorage::Pair(p, _) => Mark::mark(p),
            _ => {}
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Value<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),  // <-- equality is by value
    Pair(PairRoot<'a>)  // <-- equality is by pointer
}

// === Getting data into and out of the heap

fn value_to_heap<'a>(v: Value<'a>) -> ValueStorage<'a> {
    match v {
        Value::Null => ValueStorage::Null,
        Value::Int(n) => ValueStorage::Int(n),
        Value::Str(rcstr) => ValueStorage::Str(rcstr),
        Value::Pair(PairRoot{ptr, heap_id, ..}) => ValueStorage::Pair(ptr, heap_id)
    }
}

unsafe fn value_from_heap<'a>(heap: &Heap<'a>, v: &ValueStorage<'a>) -> Value<'a> {
    match v {
        &ValueStorage::Null => Value::Null,
        &ValueStorage::Int(n) => Value::Int(n),
        &ValueStorage::Str(ref rcstr) => Value::Str(rcstr.clone()),
        &ValueStorage::Pair(ptr, _) => Value::Pair(PairRoot::new(heap, ptr))
    }
}

#[cfg(test)]
mod test;
<commit_msg>Introduce `trait HeapInline`, implemented by `Value`.<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;

// What does this do? You'll never guess!
type HeapId<'a> = PhantomData<std::cell::Cell<&'a mut ()>>;

// The heap is (for now) just a big Vec of Pairs
pub struct Heap<'a> {
    id: HeapId<'a>,
    storage: Vec<PairStorage<'a>>,
    freelist: *mut PairStorage<'a>,
    pins: RefCell<HashMap<*mut PairStorage<'a>, usize>>
}

pub const HEAP_SIZE: usize = 10000;

pub fn with_heap<F, O>(f: F) -> O
    where F: for<'a> FnOnce(&mut Heap<'a>) -> O
{
    f(&mut Heap::new())
}

unsafe trait Mark {
    unsafe fn mark(ptr: *mut Self);
}

unsafe trait HeapInline {
    type Heap;
    type Storage;
    unsafe fn from_heap(heap: &Self::Heap, ptr: &Self::Storage) -> Self;
    fn to_heap(self) -> Self::Storage;
}

impl<'a> Heap<'a> {
    fn new() -> Heap<'a> {
        // Allocate with the full capacity so that allocated Pairs never move
        let mut h = Heap {
            id: PhantomData,
            storage: Vec::with_capacity(HEAP_SIZE),
            freelist: std::ptr::null_mut(),
            pins: RefCell::new(HashMap::new())
        };
        for i in 0 .. HEAP_SIZE {
            h.storage.push(PairStorage {
                marked: false,
                head: ValueStorage::Null,
                tail: ValueStorage::Null
            });
            let p = &mut h.storage[i] as *mut PairStorage<'a>;
            unsafe {
                h.add_to_free_list(p);
            }
        }
        h
    }

    fn pin(&self, p: *mut PairStorage<'a>) {
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    fn unpin(&self, p: *mut PairStorage<'a>) {
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    unsafe fn add_to_free_list(&mut self, p: *mut PairStorage<'a>) {
        let listp: *mut *mut PairStorage<'a> = std::mem::transmute(p);
        *listp = self.freelist;
        assert_eq!(*listp, self.freelist);
        self.freelist = p;
    }

    pub fn try_alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Option<PairRoot<'a>> {
        if self.freelist.is_null() {
            unsafe {
                self.gc();
            }
            if self.freelist.is_null() {
                return None;
            }
        }

        let p = self.freelist;
        unsafe {
            let listp: *mut *mut PairStorage<'a> = std::mem::transmute(p);
            self.freelist = *listp;

            let (h, t) = pair;
            *p = PairStorage {
                marked: false,
                head: h.to_heap(),
                tail: t.to_heap()
            };
            Some(PairRoot::new(self, p))
        }
    }

    pub fn alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> PairRoot<'a> {
        self.try_alloc(pair).expect("out of memory (gc did not collect anything)")
    }

    pub fn alloc_null(&mut self) -> PairRoot<'a> {
        self.alloc((Value::Null, Value::Null))
    }

    unsafe fn gc(&mut self) {
        // clear mark bits
        for p in &mut self.storage {
            p.marked = false;
        }

        // mark phase
        for (&p, _) in self.pins.borrow().iter() {
            Mark::mark(p);
        }

        // sweep phase
        self.freelist = std::ptr::null_mut();
        for i in 0 .. HEAP_SIZE {
            let p = &mut self.storage[i] as *mut PairStorage<'a>;
            if !(*p).marked {
                self.add_to_free_list(p);
            }
        }
    }

    #[cfg(test)]
    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

// === Pair, the reference type

// PairStorage is the only type that is actually allocated inside the heap (private)
struct PairStorage<'a> {
    marked: bool,
    head: ValueStorage<'a>,
    tail: ValueStorage<'a>
}

unsafe impl<'a> Mark for PairStorage<'a> {
    unsafe fn mark(ptr: *mut PairStorage<'a>) {
        if !ptr.is_null() && !(*ptr).marked {
            (*ptr).marked = true;
            Mark::mark(&mut (*ptr).head as *mut ValueStorage<'a>);
            Mark::mark(&mut (*ptr).tail as *mut ValueStorage<'a>);
        }
    }
}

// Handle to a PairStorage that lives in the heap.
#[allow(raw_pointer_derive)]
#[derive(Debug, PartialEq)]
pub struct PairRoot<'a> {
    ptr: *mut PairStorage<'a>,
    heap: *const Heap<'a>,
    heap_id: HeapId<'a>
}

impl<'a> Drop for PairRoot<'a> {
    fn drop(&mut self) {
        unsafe {
            (*self.heap).unpin(self.ptr);
        }
    }
}

impl<'a> Clone for PairRoot<'a> {
    fn clone(&self) -> PairRoot<'a> {
        let &PairRoot { ptr, heap, heap_id } = self;
        unsafe {
            (*heap).pin(ptr);
        }
        PairRoot {
            ptr: ptr,
            heap: heap,
            heap_id: heap_id
        }
    }
}

impl<'a> PairRoot<'a> {
    unsafe fn new(heap: &Heap<'a>, p: *mut PairStorage<'a>) -> PairRoot<'a> {
        heap.pin(p);
        PairRoot {
            ptr: p,
            heap: heap as *const Heap<'a>,
            heap_id: heap.id
        }
    }

    pub fn head(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { Value::from_heap(&*self.heap, &(*ptr).head) }
    }

    pub fn tail(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { Value::from_heap(&*self.heap, &(*ptr).tail) }
    }

    pub fn set_head(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).head = v.to_heap(); }
    }

    pub fn set_tail(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).tail = v.to_heap(); }
    }

    #[cfg(test)]
    pub fn address(&self) -> usize {
        unsafe { std::mem::transmute(self.ptr) }
    }
}

// === Values (a heap-inline enum)

// Values inside the heap (GC heap-to-heap cross-references) (private)
enum ValueStorage<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),
    Pair(*mut PairStorage<'a>, HeapId<'a>)
}

unsafe impl<'a> Mark for ValueStorage<'a> {
    unsafe fn mark(ptr: *mut ValueStorage<'a>) {
        match *ptr {
            ValueStorage::Pair(p, _) => Mark::mark(p),
            _ => {}
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Value<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),  // <-- equality is by value
    Pair(PairRoot<'a>)  // <-- equality is by pointer
}

unsafe impl<'a> HeapInline for Value<'a> {
    type Heap = Heap<'a>;
    type Storage = ValueStorage<'a>;

    fn to_heap(self) -> ValueStorage<'a> {
        match self {
            Value::Null => ValueStorage::Null,
            Value::Int(n) => ValueStorage::Int(n),
            Value::Str(rcstr) => ValueStorage::Str(rcstr),
            Value::Pair(PairRoot{ptr, heap_id, ..}) => ValueStorage::Pair(ptr, heap_id)
        }
    }

    unsafe fn from_heap(heap: &Heap<'a>, v: &ValueStorage<'a>) -> Value<'a> {
        match v {
            &ValueStorage::Null => Value::Null,
            &ValueStorage::Int(n) => Value::Int(n),
            &ValueStorage::Str(ref rcstr) => Value::Str(rcstr.clone()),
            &ValueStorage::Pair(ptr, _) => Value::Pair(PairRoot::new(heap, ptr))
        }
    }
}

#[cfg(test)]
mod test;
<|endoftext|>


<commit_before>#![feature(os, core, old_io, old_path, env)]

//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

use std::error::Error;
use std::old_io as io;
use std::old_io::fs::PathExtensions;
use std::os;
use std::env;

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<F>(get_env_var: &F) -> Path where
    F: Fn(&str) -> Option<String>
{
    getenv_path(get_env_var, "XDG_DATA_HOME")
        .unwrap_or(env::home_dir()
                       .unwrap()
                       .join(".local/share"))
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> Path {
    get_data_home_from_env(&os::getenv)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<F>(get_env_var: &F) -> Vec<Path> where
    F: Fn(&str) -> Option<String>
{
    let default_paths = "/usr/local/share:/usr/share".to_string();
    let paths = match (*get_env_var)("XDG_DATA_DIRS") {
        Some(paths) => {
            if paths.len() > 0 {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    env::split_paths(&paths).collect()
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<Path> {
    get_data_dirs_from_env(&os::getenv)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<F>(get_env_var: &F) -> Path where
    F: Fn(&str) -> Option<String>
{
    getenv_path(get_env_var, "XDG_CONFIG_HOME")
        .unwrap_or(env::home_dir()
                       .unwrap()
                       .join(".config"))
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> Path {
    get_config_home_from_env(&os::getenv)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<F>(get_env_var: &F) -> Vec<Path> where
    F: Fn(&str) -> Option<String>
{
    let default_paths = "/etc/xdg".to_string();
    let paths = match (*get_env_var)("XDG_CONFIG_DIRS") {
        Some(paths) => {
            if paths.len() > 0 {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    env::split_paths(&paths).collect()
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<Path> {
    get_config_dirs_from_env(&os::getenv)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<F>(get_env_var: &F) -> Path where
    F: Fn(&str) -> Option<String>
{
    getenv_path(get_env_var, "XDG_CACHE_HOME")
        .unwrap_or(env::home_dir()
                       .unwrap()
                       .join(".cache"))
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> Path {
    get_cache_home_from_env(&os::getenv)
}

/// Get $XDG_RUNTIME_DIR if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_PATH``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<F>(get_env_var: &F) -> Option<Path> where
    F: Fn(&str) -> Option<String>
{
    getenv_path(get_env_var, "XDG_RUNTIME_DIR")
}

pub fn get_runtime_dir() -> Option<Path> {
    getenv_path(&os::getenv, "XDG_RUNTIME_DIR")
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` meets the requirements of the specification
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications, a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
pub fn test_runtime_dir(path: &Path) -> Result<(), String> {
    match path.stat() {
        Ok(stat) => {
            if stat.perm.intersects(io::GROUP_RWX | io::OTHER_RWX) {
                Err("Incorrect permissions".to_string())
            } else {
                Ok(())
            }
        },

        Err(error) => Err(error.description().to_string())
    }
}

/// Get an environment variable's value as a Path.
fn getenv_path<F>(get_env_var: &F, env_var: &str) -> Option<Path> where
    F: Fn(&str) -> Option<String>
{
    let path = (*get_env_var)(env_var);
    match path {
        Some(path) => {
            let path = Path::new(path);
            if path.is_absolute() {
                Some(path)
            } else {
                None
            }
        },

        None => None
    }
}

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

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "".to_string());

        let f = |&: var: &str| { custom_env.get(var).map(|x| x.clone()) };
        assert!(super::get_data_home_from_env(&f)
                == env::home_dir().unwrap().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![Path::new("/usr/local/share"), Path::new("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == env::home_dir().unwrap().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![Path::new("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == env::home_dir().unwrap().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "".to_string());
        custom_env.insert("XDG_DATA_DIRS", "".to_string());
        custom_env.insert("XDG_CONFIG_HOME", "".to_string());
        custom_env.insert("XDG_CONFIG_DIRS", "".to_string());
        custom_env.insert("XDG_CACHE_HOME", "".to_string());

        let f = |&: var: &str| { custom_env.get(var).map(|x| x.clone()) };
        assert!(super::get_data_home_from_env(&f)
                == env::home_dir().unwrap().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![Path::new("/usr/local/share"), Path::new("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == env::home_dir().unwrap().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![Path::new("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == env::home_dir().unwrap().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = os::make_absolute(&Path::new(".")).unwrap();
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", format!("{}/user/data", cwd.display()));
        custom_env.insert("XDG_DATA_DIRS", format!("{}/share/data:{}/local/data", cwd.display(), cwd.display()));
        custom_env.insert("XDG_CONFIG_HOME", format!("{}/user/config", cwd.display()));
        custom_env.insert("XDG_CONFIG_DIRS", format!("{}/config:{}/local/config", cwd.display(), cwd.display()));
        custom_env.insert("XDG_CACHE_HOME", format!("{}/user/cache", cwd.display()));

        let f = |&: var: &str| { custom_env.get(var).map(|x| x.clone()) };
        assert!(super::get_data_home_from_env(&f)
                == custom_env.get("XDG_DATA_HOME").map(Path::new).unwrap());
        assert!(super::get_data_dirs_from_env(&f)
                == (env::split_paths(&*custom_env["XDG_DATA_DIRS"]).collect::<Vec<Path>>()));
        assert!(super::get_config_home_from_env(&f)
                == custom_env.get("XDG_CONFIG_HOME").map(Path::new).unwrap());
        assert!(super::get_config_dirs_from_env(&f)
                == env::split_paths(&*custom_env["XDG_CONFIG_DIRS"]).collect::<Vec<Path>>());
        assert!(super::get_cache_home_from_env(&f)
                == custom_env.get("XDG_CACHE_HOME").map(Path::new).unwrap());
    }
}
<commit_msg>Convert to new Path api<commit_after>#![cfg_attr(unix, feature(old_io, core, old_path))]
#![feature(os, env, std_misc, path)]

//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

use std::path::PathBuf;
use std::env;
use std::ffi::{self, AsOsStr, OsString};

fn home_dir() -> PathBuf {
    PathBuf::new(env::home_dir().unwrap().as_os_str())
}

fn split_paths<P: ?Sized>(paths: &P) -> Vec<PathBuf>
    where P: AsOsStr
{
    env::split_paths(paths).map(|x| PathBuf::new(x.as_os_str())).collect()
}

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_DATA_HOME")
        .unwrap_or(home_dir().join(".local/share"))
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> PathBuf {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let default_paths = "/usr/local/share:/usr/share".as_os_str().to_os_string();
    let paths = match (*get_env_var)("XDG_DATA_DIRS") {
        Some(paths) => {
            if paths != ffi::OsString::from_str("") {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    split_paths(&paths)
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_CONFIG_HOME")
        .unwrap_or(home_dir().join(".config"))
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> PathBuf {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let default_paths = "/etc/xdg".as_os_str().to_os_string();
    let paths = match (*get_env_var)("XDG_CONFIG_DIRS") {
        Some(paths) => {
            if paths != OsString::from_str("") {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    split_paths(&paths)
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_CACHE_HOME")
        .unwrap_or(home_dir().join(".cache"))
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> PathBuf {
    get_cache_home_from_env(&env::var_os)
}

/// Get $XDG_RUNTIME_DIR if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_PATH``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<F>(get_env_var: &F) -> Option<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_RUNTIME_DIR")
}

pub fn get_runtime_dir() -> Option<PathBuf> {
    getenv_path(&env::var_os, "XDG_RUNTIME_DIR")
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` meets the requirements of the specification
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications, a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(unix)]
pub fn test_runtime_dir(path: &PathBuf) -> Result<(), String> {
    use std::old_path;
    use std::old_io;
    use std::os::unix::prelude::OsStrExt;
    use std::old_io::fs::PathExtensions;
    use std::error::Error;
    // FIXME: https://github.com/rust-lang/rfcs/issues/905
    match old_path::Path::new(path.as_os_str().as_bytes()).stat() {
        Ok(stat) => {
            if stat.perm.intersects(old_io::GROUP_RWX | old_io::OTHER_RWX) {
                Err("Incorrect permissions".to_string())
            } else {
                Ok(())
            }
        },

        Err(error) => Err(error.description().to_string())
    }
}

#[cfg(not(unix))]
pub fn test_runtime_dir(_path: &PathBuf) -> Result<(), String> {
    Ok(())
}

/// Get an environment variable's value as a PathBuf.
fn getenv_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let path = (*get_env_var)(env_var);
    match path {
        Some(path) => {
            let path = PathBuf::new(&path);
            if path.is_absolute() {
                Some(path)
            } else {
                None
            }
        },

        None => None
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::env;
    use std::path::PathBuf;
    use std::ffi::AsOsStr;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = |&: var: &str| { custom_env.get(var).map(|x| x.as_os_str().to_os_string()) };
        assert!(super::get_data_home_from_env(&f)
                == super::home_dir().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![PathBuf::new("/usr/local/share"), PathBuf::new("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == super::home_dir().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![PathBuf::new("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == super::home_dir().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = |&: var: &str| { custom_env.get(var).map(|x| x.as_os_str().to_os_string()) };
        assert!(super::get_data_home_from_env(&f)
                == super::home_dir().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![PathBuf::new("/usr/local/share"), PathBuf::new("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == super::home_dir().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![PathBuf::new("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == super::home_dir().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::new(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").as_os_str().to_os_string());
        custom_env.insert("XDG_DATA_DIRS", env::join_paths(
                vec![cwd.join("share/data"),cwd.join("local/data")].into_iter()).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").as_os_str().to_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", env::join_paths(
                vec![cwd.join("config"), cwd.join("local/config")].into_iter()).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").as_os_str().to_os_string());

        let f = |&: var: &str| { custom_env.get(var).map(|x| x.clone()) };
        assert!(super::get_data_home_from_env(&f)
                == custom_env.get("XDG_DATA_HOME").map(PathBuf::new).unwrap());
        assert!(super::get_data_dirs_from_env(&f)
                == (super::split_paths(&*custom_env["XDG_DATA_DIRS"])));
        assert!(super::get_config_home_from_env(&f)
                == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::new).unwrap());
        assert!(super::get_config_dirs_from_env(&f)
                == super::split_paths(&*custom_env["XDG_CONFIG_DIRS"]));
        assert!(super::get_cache_home_from_env(&f)
                == custom_env.get("XDG_CACHE_HOME").map(PathBuf::new).unwrap());
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

pub fn puzzle(input: &str) -> usize {
    0
}

#[derive(PartialEq, Debug)]
pub struct Bot {
    chip1: Option<usize>,
    chip2: Option<usize>,
    low: Option<usize>,
    high: Option<usize>,
}

impl Bot {
    pub fn new() -> Bot {
        Bot { chip1: None, chip2: None, low: None, high: None }
    }

    pub fn has_two_chips(&self) -> bool {
        match (self.chip1, self.chip2) {
            (Some(_), Some(_)) => true,
            _ => false,
        }
    }

    pub fn receive_chip(&mut self, chip: usize) -> Vec<(usize, usize)> {
        if self.chip1.is_none() {
            self.chip1 = Some(chip);
            vec![]
        } else if self.chip2.is_none() {
            self.chip2 = Some(chip);
            if let (Some(lo), Some(hi)) = (self.low, self.high) {
                let return_commands = if self.chip1 < self.chip2 {
                    vec![
                        (lo, self.chip1.unwrap()),
                        (hi, self.chip2.unwrap()),
                    ]
                } else {
                    vec![
                        (lo, self.chip2.unwrap()),
                        (hi, self.chip1.unwrap()),
                    ]
                };
                self.chip1 = None;
                self.chip2 = None;
                self.low = None;
                self.high = None;
                return_commands
            } else {
                vec![]
            }
        } else {
            panic!("Cannot hold more than two chips");
        }
    }

    pub fn receive_command(&mut self, low_bot: usize, high_bot: usize) {
        self.low = Some(low_bot);
        self.high = Some(high_bot);
    }
}

pub struct BotRouter {
    bots: HashMap<usize, Bot>,
}

impl BotRouter {
    pub fn exec_command(&mut self, input: &str) {
        let pieces: Vec<&str> = input.split_whitespace().collect();

        if pieces[0] == "value" {
            self.value_to_bot(
                pieces[5].parse().expect("can't parse bot key"),
                pieces[1].parse().expect("can't parse chip value")
            );
        } else if pieces[0] == "bot" {
            self.command_to_bot(
                pieces[1].parse().expect("can't parse bot key"),
                pieces[6].parse().expect("can't parse low bot key"),
                pieces[11].parse().expect("can't parse high bot key"),
            );
        }
    }

    pub fn value_to_bot(&mut self, bot_key: usize, value: usize) {
        let return_commands = self.bots
                                  .entry(bot_key)
                                  .or_insert(Bot::new())
                                  .receive_chip(value);
        for &(bot_key, chip_value) in return_commands.iter() {
            self.value_to_bot(bot_key, chip_value);
        }
    }

    pub fn command_to_bot(&mut self, bot_key: usize,
                          low_bot_key: usize, high_bot_key: usize) {
        let bot = self.bots.entry(bot_key).or_insert(Bot::new());
        bot.receive_command(low_bot_key, high_bot_key);
    }
}

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

    #[test]
    fn new_bot_does_not_have_two_chips() {
        let bot = Bot::new();
        assert!( ! bot.has_two_chips() );
    }

    #[test]
    fn bot_gets_a_chip_still_not_two() {
        let mut bot = Bot::new();
        bot.receive_chip(1);
        assert!( ! bot.has_two_chips() );
    }

    #[test]
    fn bot_gets_two_chips() {
        let mut bot = Bot::new();
        bot.receive_chip(1);
        bot.receive_chip(4);
        assert!(bot.has_two_chips());
    }

    #[test]
    fn router_can_give_values() {
        let mut br = BotRouter { bots: HashMap::new() };
        br.exec_command("value 5 goes to bot 2");
        assert_eq!(
            br.bots.get(&2),
            Some(&Bot { chip1: Some(5), chip2: None, low: None, high: None })
        );
    }

    #[test]
    fn router_can_give_commands_that_get_stored() {
        let mut br = BotRouter { bots: HashMap::new() };
        br.exec_command("bot 2 gives low to bot 1 and high to bot 0");
        assert_eq!(
            br.bots.get(&2),
            Some(&Bot {
                chip1: None,
                chip2: None,
                low: Some(1),
                high: Some(0),
            })
        );
    }

    #[test]
    fn router_adds_two_values_so_commands_get_executed() {
        let mut br = BotRouter { bots: HashMap::new() };
        br.exec_command("value 5 goes to bot 2");
        br.exec_command("bot 2 gives low to bot 1 and high to bot 0");
        br.exec_command("value 2 goes to bot 2");

        assert_eq!(
            br.bots.get(&2),
            Some(&Bot {
                chip1: None,
                chip2: None,
                low: None,
                high: None,
            })
        );

        assert_eq!(
            br.bots.get(&1),
            Some(&Bot {
                chip1: Some(2),
                chip2: None,
                low: None,
                high: None,
            })
        );

        assert_eq!(
            br.bots.get(&0),
            Some(&Bot {
                chip1: Some(5),
                chip2: None,
                low: None,
                high: None,
            })
        );
    }

    // #[test]
    // fn test_sample() {
    //     let mut br = BotRouter { bots: HashMap::new() };
    //     br.exec_command("value 5 goes to bot 2");
    //     br.exec_command("bot 2 gives low to bot 1 and high to bot 0");
    //     br.exec_command("value 3 goes to bot 1");
    //     br.exec_command("bot 1 gives low to output 1 and high to bot 0");
    //     br.exec_command("bot 0 gives low to output 2 and high to output 0");
    //     br.exec_command("value 2 goes to bot 2");
    // }
}
<commit_msg>Be able to have output bins too<commit_after>use std::collections::HashMap;

pub fn puzzle(input: &str) -> usize {
    0
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum BotOrOutput {
    Bot,
    Output,
}

#[derive(PartialEq, Debug)]
pub struct Bot {
    chip1: Option<usize>,
    chip2: Option<usize>,
    low: Option<usize>,
    low_bot_or_output: Option<BotOrOutput>,
    high: Option<usize>,
    high_bot_or_output: Option<BotOrOutput>,
}

impl Bot {
    pub fn new() -> Bot {
        Bot {
            chip1: None, chip2: None,
            low: None, high: None,
            low_bot_or_output: None,
            high_bot_or_output: None,
        }
    }

    pub fn has_two_chips(&self) -> bool {
        match (self.chip1, self.chip2) {
            (Some(_), Some(_)) => true,
            _ => false,
        }
    }

    pub fn receive_chip(&mut self, chip: usize)
           -> Vec<(usize, BotOrOutput, usize)> {
        if self.chip1.is_none() {
            self.chip1 = Some(chip);
            vec![]
        } else if self.chip2.is_none() {
            self.chip2 = Some(chip);
            if let (Some(lo), Some(hi)) = (self.low, self.high) {
                let return_commands = if self.chip1 < self.chip2 {
                    vec![
                        (
                            lo,
                            self.low_bot_or_output.unwrap(),
                            self.chip1.unwrap()
                        ),
                        (
                            hi,
                            self.high_bot_or_output.unwrap(),
                            self.chip2.unwrap()
                        ),
                    ]
                } else {
                    vec![
                        (
                            lo,
                            self.low_bot_or_output.unwrap(),
                            self.chip2.unwrap()
                        ),
                        (
                            hi,
                            self.high_bot_or_output.unwrap(),
                            self.chip1.unwrap()
                        ),
                    ]
                };
                self.chip1 = None;
                self.chip2 = None;
                self.low = None;
                self.high = None;
                self.low_bot_or_output = None;
                self.high_bot_or_output = None;
                return_commands
            } else {
                vec![]
            }
        } else {
            panic!("Cannot hold more than two chips");
        }
    }

    pub fn receive_command(&mut self, low_bot_or_output: &str, low: usize,
                                      high_bot_or_output: &str, high: usize) {
        self.low = Some(low);
        if low_bot_or_output == "bot" {
            self.low_bot_or_output = Some(BotOrOutput::Bot);
        } else {
            self.low_bot_or_output = Some(BotOrOutput::Output);
        }
        self.high = Some(high);
        if high_bot_or_output == "bot" {
            self.high_bot_or_output = Some(BotOrOutput::Bot);
        } else {
            self.high_bot_or_output = Some(BotOrOutput::Output);
        }
    }
}

pub struct BotRouter {
    bots: HashMap<usize, Bot>,
    output_bins: HashMap<usize, Vec<usize>>,
}

impl BotRouter {
    pub fn exec_command(&mut self, input: &str) {
        let pieces: Vec<&str> = input.split_whitespace().collect();

        if pieces[0] == "value" {
            self.value_to_bot(
                pieces[5].parse().expect("can't parse bot key"),
                pieces[1].parse().expect("can't parse chip value")
            );
        } else if pieces[0] == "bot" {
            self.command_to_bot(
                pieces[1].parse().expect("can't parse bot key"),
                pieces[5],
                pieces[6].parse().expect("can't parse low key"),
                pieces[10],
                pieces[11].parse().expect("can't parse high key"),
            );
        }
    }

    pub fn value_to_bot(&mut self, bot_key: usize, value: usize) {
        let return_commands = self.bots
                                  .entry(bot_key)
                                  .or_insert(Bot::new())
                                  .receive_chip(value);
        for &(key, bot_or_output, chip_value) in return_commands.iter() {
            match bot_or_output {
                BotOrOutput::Bot => self.value_to_bot(key, chip_value),
                BotOrOutput::Output => self.value_to_output(key, chip_value),
            }
        }
    }

    pub fn value_to_output(&mut self, output_key: usize, value: usize) {
        self.output_bins.entry(output_key).or_insert(Vec::new()).push(value);
    }

    pub fn command_to_bot(&mut self, bot_key: usize,
                          low_bot_or_output: &str, low_key: usize,
                          high_bot_or_output: &str, high_key: usize) {
        let bot = self.bots.entry(bot_key).or_insert(Bot::new());
        bot.receive_command(
            low_bot_or_output,
            low_key,
            high_bot_or_output,
            high_key
        );
    }
}

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

    #[test]
    fn new_bot_does_not_have_two_chips() {
        let bot = Bot::new();
        assert!( ! bot.has_two_chips() );
    }

    #[test]
    fn bot_gets_a_chip_still_not_two() {
        let mut bot = Bot::new();
        bot.receive_chip(1);
        assert!( ! bot.has_two_chips() );
    }

    #[test]
    fn bot_gets_two_chips() {
        let mut bot = Bot::new();
        bot.receive_chip(1);
        bot.receive_chip(4);
        assert!(bot.has_two_chips());
    }

    #[test]
    fn router_can_give_values() {
        let mut br = BotRouter {
            bots: HashMap::new(),
            output_bins: HashMap::new(),
        };
        br.exec_command("value 5 goes to bot 2");
        assert_eq!(
            br.bots.get(&2).unwrap().chip1,
            Some(5)
        );
    }

    #[test]
    fn router_can_give_commands_that_get_stored() {
        let mut br = BotRouter {
            bots: HashMap::new(),
            output_bins: HashMap::new(),
        };
        br.exec_command("bot 2 gives low to bot 1 and high to bot 0");
        assert_eq!(
            br.bots.get(&2).unwrap().low,
            Some(1)
        );
        assert_eq!(
            br.bots.get(&2).unwrap().high,
            Some(0)
        );
    }

    #[test]
    fn router_adds_two_values_so_commands_get_executed() {
        let mut br = BotRouter {
            bots: HashMap::new(),
            output_bins: HashMap::new(),
        };
        br.exec_command("value 5 goes to bot 2");
        br.exec_command("bot 2 gives low to bot 1 and high to bot 0");
        br.exec_command("value 2 goes to bot 2");

        assert_eq!(
            br.bots.get(&2).unwrap().chip1,
            None
        );
        assert_eq!(
            br.bots.get(&2).unwrap().chip2,
            None
        );

        assert_eq!(
            br.bots.get(&1).unwrap().chip1,
            Some(2)
        );
        assert_eq!(
            br.bots.get(&1).unwrap().chip2,
            None
        );

        assert_eq!(
            br.bots.get(&0).unwrap().chip1,
            Some(5)
        );
        assert_eq!(
            br.bots.get(&0).unwrap().chip2,
            None
        );
    }

    #[test]
    fn test_sample() {
        let mut br = BotRouter {
            bots: HashMap::new(),
            output_bins: HashMap::new(),
        };
        br.exec_command("value 5 goes to bot 2");
        br.exec_command("bot 2 gives low to bot 1 and high to bot 0");
        br.exec_command("value 3 goes to bot 1");
        br.exec_command("bot 1 gives low to output 1 and high to bot 0");
        br.exec_command("bot 0 gives low to output 2 and high to output 0");
        br.exec_command("value 2 goes to bot 2");

        assert_eq!(
            br.output_bins.get(&0),
            Some(&vec![5])
        );

        assert_eq!(
            br.output_bins.get(&1),
            Some(&vec![2])
        );

        assert_eq!(
            br.output_bins.get(&2),
            Some(&vec![3])
        )
    }
}
<|endoftext|>


<commit_before>extern crate glium;

use std::mem::zeroed;
use std::ops::*;
use glium::uniforms::*;

pub trait Vector {
    fn transform(self, matrix: &Matrix) -> Self;
}

impl Vector for (f32, f32) {
    fn transform(self, matrix: &Matrix) -> Self {
        let m = matrix.m;

        let x = self.0*m[0][0] + self.1*m[1][0];
        let y = self.0*m[0][1] + self.1*m[1][1];
        (x, y)
    }
}

#[derive(PartialEq, Clone, Debug)]
pub struct Matrix { m: [[f32; 4]; 4] }

impl Matrix {
    pub fn new() -> Self { unsafe { zeroed() } }

    pub fn identity() -> Self {
        Matrix {
            m: [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, 1.0, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [0.0, 0.0, 0.0, 1.0],
            ]
        }
    }

    pub fn rotation_x(rad: f32) -> Self {
        let (sin, cos) = rad.sin_cos();

        Matrix {
            m: [
                [1.0,  0.0, 0.0, 0.0],
                [0.0,  cos, sin, 0.0],
                [0.0, -sin, cos, 0.0],
                [0.0,  0.0, 0.0, 1.0],
            ]
        }
    }

    pub fn rotation_y(rad: f32) -> Self {
        let (sin, cos) = rad.sin_cos();

        Matrix {
            m: [
                [cos, 0.0, -sin, 0.0],
                [0.0, 1.0,  0.0, 0.0],
                [sin, 0.0,  cos, 0.0],
                [0.0, 0.0,  0.0, 1.0],
            ]
        }
    }

    pub fn rotation_z(rad: f32) -> Self {
        let (sin, cos) = rad.sin_cos();

        Matrix {
            m: [
                [ cos, sin, 0.0, 0.0],
                [-sin, cos, 0.0, 0.0],
                [ 0.0, 0.0, 1.0, 0.0],
                [ 0.0, 0.0, 0.0, 1.0],
            ]
        }
    }

    pub fn orthographic(view_width: f32, view_height: f32, near_z: f32, far_z: f32) -> Self {
        // assert(!XMScalarNearEqual(ViewWidth, 0.0f, 0.00001f));
        // assert(!XMScalarNearEqual(ViewHeight, 0.0f, 0.00001f));
        // assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));
        let f_range = 1.0/(near_z - far_z);
        Matrix {
            m: [
                [2.0/view_width, 0.0, 0.0, 0.0],
                [0.0, 2.0/view_height, 0.0, 0.0],
                [0.0, 0.0, f_range, 0.0],
                [0.0, 0.0, f_range*near_z, 1.0],
            ]
        }

    }

    pub fn orthographic_off_center(view_left: f32, view_right: f32, view_bottom: f32, view_top: f32, near_z: f32, far_z: f32) -> Self {
        // reciprocal width and height
        let r_width = 1.0/(view_right - view_left);
        let r_height = 1.0/(view_top - view_bottom);
        let range = 1.0/(near_z-far_z);

        Matrix {
            m: [
                [r_width + r_width, 0.0, 0.0, 0.0],
                [0.0, r_height + r_height, 0.0, 0.0],
                [0.0, 0.0, range, 0.0],
                [-(view_left + view_right)*r_width, -(view_top + view_bottom)*r_height, range*near_z, 1.0],
            ]
        }
    }

    pub fn perspective(width: f32, height: f32, near_z: f32, far_z: f32) -> Self {
        let two_near_z = near_z + near_z;
        let range = far_z/(near_z - far_z);

        Matrix {
            m: [
                [two_near_z/width, 0.0, 0.0, 0.0],
                [0.0, two_near_z/height, 0.0, 0.0],
                [0.0, 0.0, range, -1.0],
                [0.0, 0.0, range*near_z, 0.0],
            ]
        }
    }

    /// aspect: Width / Height
    pub fn perspective_fov(fov: f32, aspect: f32, near_z: f32, far_z: f32) -> Self {
        let (sin, cos) = (0.5 * fov).sin_cos();
        let f = cos/sin;
        let range = far_z/(near_z - far_z);

        Matrix {
            m: [
                [f/aspect, 0.0, 0.0, 0.0],
                [0.0, f, 0.0, 0.0],
                [0.0, 0.0, range, -1.0],
                [0.0, 0.0, range*near_z, 0.0],
            ]
        }
    }

    pub fn translation(ox: f32, oy: f32, oz: f32) -> Self {
        Matrix {
            m: [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, 1.0, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [ox,  oy,  oz,  1.0],
            ]
        }
    }

    pub fn transpose(self) -> Self {
        Matrix {
            m: [
                [self.m[0][0], self.m[1][0], self.m[2][0], self.m[3][0]],
                [self.m[0][1], self.m[1][1], self.m[2][1], self.m[3][1]],
                [self.m[0][2], self.m[1][2], self.m[2][2], self.m[3][2]],
                [self.m[0][3], self.m[1][3], self.m[2][3], self.m[3][3]],
            ]
        }
    }
}

impl Mul for Matrix {
    type Output = Matrix;
    fn mul(self, rhs: Matrix) -> Matrix { &self * &rhs }
}

impl<'a> Mul<Matrix> for &'a Matrix {
    type Output = Matrix;
    fn mul(self, rhs: Matrix) -> Matrix { self * &rhs }
}

impl<'a> Mul<&'a Matrix> for Matrix {
    type Output = Matrix;
    fn mul(self, rhs: &'a Matrix) -> Matrix { &self * rhs }
}

impl<'a, 'b> Mul<&'a Matrix> for &'b Matrix {
    type Output = Matrix;

    fn mul(self, rhs: &'a Matrix) -> Matrix {
        macro_rules! row {
            ($col:expr) => ({
                let x = self.m[$col][0];
                let y = self.m[$col][1];
                let z = self.m[$col][2];
                let w = self.m[$col][3];
                [
                    (rhs.m[0][0]*x)+(rhs.m[1][0]*y)+(rhs.m[2][0]*z)+(rhs.m[3][0]*w),
                    (rhs.m[0][1]*x)+(rhs.m[1][1]*y)+(rhs.m[2][1]*z)+(rhs.m[3][1]*w),
                    (rhs.m[0][2]*x)+(rhs.m[1][2]*y)+(rhs.m[2][2]*z)+(rhs.m[3][2]*w),
                    (rhs.m[0][3]*x)+(rhs.m[1][3]*y)+(rhs.m[2][3]*z)+(rhs.m[3][3]*w),
                ]
            })
        }

        Matrix { m: [ row!(0), row!(1), row!(2), row!(3) ] }
    }
}

impl AsUniformValue for Matrix {
    fn as_uniform_value(&self) -> UniformValue<'static> {
        UniformValue::Mat4(self.m)
    }

    fn matches(ty: &UniformType) -> bool {
        ty == &UniformType::FloatMat4
    }
}
<commit_msg>Matrix::new() returns zero filled matrix.<commit_after>extern crate glium;

use std::mem::zeroed;
use std::ops::*;
use glium::uniforms::*;

pub trait Vector {
    fn transform(self, matrix: &Matrix) -> Self;
}

impl Vector for (f32, f32) {
    fn transform(self, matrix: &Matrix) -> Self {
        let m = matrix.m;

        let x = self.0*m[0][0] + self.1*m[1][0];
        let y = self.0*m[0][1] + self.1*m[1][1];
        (x, y)
    }
}

#[derive(PartialEq, Clone, Debug)]
pub struct Matrix { m: [[f32; 4]; 4] }

impl Matrix {
    pub fn new() -> Self { unsafe { zeroed() } }

    pub fn identity() -> Self {
        Matrix {
            m: [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, 1.0, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [0.0, 0.0, 0.0, 1.0],
            ]
        }
    }

    pub fn rotation_x(rad: f32) -> Self {
        let (sin, cos) = rad.sin_cos();

        Matrix {
            m: [
                [1.0,  0.0, 0.0, 0.0],
                [0.0,  cos, sin, 0.0],
                [0.0, -sin, cos, 0.0],
                [0.0,  0.0, 0.0, 1.0],
            ]
        }
    }

    pub fn rotation_y(rad: f32) -> Self {
        let (sin, cos) = rad.sin_cos();

        Matrix {
            m: [
                [cos, 0.0, -sin, 0.0],
                [0.0, 1.0,  0.0, 0.0],
                [sin, 0.0,  cos, 0.0],
                [0.0, 0.0,  0.0, 1.0],
            ]
        }
    }

    pub fn rotation_z(rad: f32) -> Self {
        let (sin, cos) = rad.sin_cos();

        Matrix {
            m: [
                [ cos, sin, 0.0, 0.0],
                [-sin, cos, 0.0, 0.0],
                [ 0.0, 0.0, 1.0, 0.0],
                [ 0.0, 0.0, 0.0, 1.0],
            ]
        }
    }

    pub fn orthographic(view_width: f32, view_height: f32, near_z: f32, far_z: f32) -> Self {
        // assert(!XMScalarNearEqual(ViewWidth, 0.0f, 0.00001f));
        // assert(!XMScalarNearEqual(ViewHeight, 0.0f, 0.00001f));
        // assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));
        let f_range = 1.0/(near_z - far_z);
        Matrix {
            m: [
                [2.0/view_width, 0.0, 0.0, 0.0],
                [0.0, 2.0/view_height, 0.0, 0.0],
                [0.0, 0.0, f_range, 0.0],
                [0.0, 0.0, f_range*near_z, 1.0],
            ]
        }

    }

    pub fn orthographic_off_center(view_left: f32, view_right: f32, view_bottom: f32, view_top: f32, near_z: f32, far_z: f32) -> Self {
        // reciprocal width and height
        let r_width = 1.0/(view_right - view_left);
        let r_height = 1.0/(view_top - view_bottom);
        let range = 1.0/(near_z-far_z);

        Matrix {
            m: [
                [r_width + r_width, 0.0, 0.0, 0.0],
                [0.0, r_height + r_height, 0.0, 0.0],
                [0.0, 0.0, range, 0.0],
                [-(view_left + view_right)*r_width, -(view_top + view_bottom)*r_height, range*near_z, 1.0],
            ]
        }
    }

    pub fn perspective(width: f32, height: f32, near_z: f32, far_z: f32) -> Self {
        let two_near_z = near_z + near_z;
        let range = far_z/(near_z - far_z);

        Matrix {
            m: [
                [two_near_z/width, 0.0, 0.0, 0.0],
                [0.0, two_near_z/height, 0.0, 0.0],
                [0.0, 0.0, range, -1.0],
                [0.0, 0.0, range*near_z, 0.0],
            ]
        }
    }

    /// aspect: Width / Height
    pub fn perspective_fov(fov: f32, aspect: f32, near_z: f32, far_z: f32) -> Self {
        let (sin, cos) = (0.5 * fov).sin_cos();
        let f = cos/sin;
        let range = far_z/(near_z - far_z);

        Matrix {
            m: [
                [f/aspect, 0.0, 0.0, 0.0],
                [0.0, f, 0.0, 0.0],
                [0.0, 0.0, range, -1.0],
                [0.0, 0.0, range*near_z, 0.0],
            ]
        }
    }

    pub fn translation(ox: f32, oy: f32, oz: f32) -> Self {
        Matrix {
            m: [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, 1.0, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [ox,  oy,  oz,  1.0],
            ]
        }
    }

    pub fn transpose(self) -> Self {
        Matrix {
            m: [
                [self.m[0][0], self.m[1][0], self.m[2][0], self.m[3][0]],
                [self.m[0][1], self.m[1][1], self.m[2][1], self.m[3][1]],
                [self.m[0][2], self.m[1][2], self.m[2][2], self.m[3][2]],
                [self.m[0][3], self.m[1][3], self.m[2][3], self.m[3][3]],
            ]
        }
    }
}

#[test]
fn new_matrix_is_zeroed() {
    let m = Matrix::new();

    assert_eq!(m.m[0][0], 0.0);
    assert_eq!(m.m[0][1], 0.0);
    assert_eq!(m.m[0][2], 0.0);
    assert_eq!(m.m[0][3], 0.0);
    assert_eq!(m.m[1][0], 0.0);
    assert_eq!(m.m[1][1], 0.0);
    assert_eq!(m.m[1][2], 0.0);
    assert_eq!(m.m[1][3], 0.0);
    assert_eq!(m.m[2][0], 0.0);
    assert_eq!(m.m[2][1], 0.0);
    assert_eq!(m.m[2][2], 0.0);
    assert_eq!(m.m[2][3], 0.0);
    assert_eq!(m.m[3][0], 0.0);
    assert_eq!(m.m[3][1], 0.0);
    assert_eq!(m.m[3][2], 0.0);
    assert_eq!(m.m[3][3], 0.0);
}

impl Mul for Matrix {
    type Output = Matrix;
    fn mul(self, rhs: Matrix) -> Matrix { &self * &rhs }
}

impl<'a> Mul<Matrix> for &'a Matrix {
    type Output = Matrix;
    fn mul(self, rhs: Matrix) -> Matrix { self * &rhs }
}

impl<'a> Mul<&'a Matrix> for Matrix {
    type Output = Matrix;
    fn mul(self, rhs: &'a Matrix) -> Matrix { &self * rhs }
}

impl<'a, 'b> Mul<&'a Matrix> for &'b Matrix {
    type Output = Matrix;

    fn mul(self, rhs: &'a Matrix) -> Matrix {
        macro_rules! row {
            ($col:expr) => ({
                let x = self.m[$col][0];
                let y = self.m[$col][1];
                let z = self.m[$col][2];
                let w = self.m[$col][3];
                [
                    (rhs.m[0][0]*x)+(rhs.m[1][0]*y)+(rhs.m[2][0]*z)+(rhs.m[3][0]*w),
                    (rhs.m[0][1]*x)+(rhs.m[1][1]*y)+(rhs.m[2][1]*z)+(rhs.m[3][1]*w),
                    (rhs.m[0][2]*x)+(rhs.m[1][2]*y)+(rhs.m[2][2]*z)+(rhs.m[3][2]*w),
                    (rhs.m[0][3]*x)+(rhs.m[1][3]*y)+(rhs.m[2][3]*z)+(rhs.m[3][3]*w),
                ]
            })
        }

        Matrix { m: [ row!(0), row!(1), row!(2), row!(3) ] }
    }
}

impl AsUniformValue for Matrix {
    fn as_uniform_value(&self) -> UniformValue<'static> {
        UniformValue::Mat4(self.m)
    }

    fn matches(ty: &UniformType) -> bool {
        ty == &UniformType::FloatMat4
    }
}
<|endoftext|>


<commit_before>#![feature(const_fn)]

extern crate num;
extern crate glium;
extern crate rustc_serialize;

macro_rules! impl_uniform_block_basic {
    ($ty:ty, $uniform_ty:expr) => (
        impl ::glium::uniforms::UniformBlock for $ty {

            fn matches(layout: &::glium::program::BlockLayout, base_offset: usize)
                       -> Result<(), ::glium::uniforms::LayoutMismatchError>
            {
                if let &::glium::program::BlockLayout::BasicType { ty, offset_in_buffer } = layout {
                    if ty != $uniform_ty {
                        return Err(::glium::uniforms::LayoutMismatchError::TypeMismatch {
                            expected: ty,
                            obtained: $uniform_ty,
                        });
                    }

                    if offset_in_buffer != base_offset {
                        return Err(::glium::uniforms::LayoutMismatchError::OffsetMismatch {
                            expected: offset_in_buffer,
                            obtained: base_offset,
                        });
                    }

                    Ok(())

                } else {
                    Err(::glium::uniforms::LayoutMismatchError::LayoutMismatch {
                        expected: layout.clone(),
                        obtained: ::glium::program::BlockLayout::BasicType {
                            ty: $uniform_ty,
                            offset_in_buffer: base_offset,
                        }
                    })
                }
            }

            #[inline]
            fn build_layout(base_offset: usize) -> ::glium::program::BlockLayout {
                ::glium::program::BlockLayout::BasicType {
                    ty: $uniform_ty,
                    offset_in_buffer: base_offset,
                }
            }
        }
    )
}

mod vector;
mod matrix;

pub use vector::{
    Vec2,
    Vec3,
    Vec4,
};

pub use matrix::{
    Mat4,
};

pub use vector::{
    Vector,
    Dot,
    Cross,
    Normalize,
    Length,
    Repeat,
};

pub use matrix::{
    Translate,
    Rotate,
    Transpose,
    Inverse,
    Scale,
    LookAt,
    Projection,
    Matrix,
};

pub use num::{
    One,
    Zero,
};

pub use std::f32::consts::PI;
pub const PI_2: f32 = PI * 2.0;

pub const fn radians(n: f32) -> f32 {
    n * (PI / 180.0)
}

pub const fn degrees(n: f32) -> f32 {
    n * (180.0 / PI)
}

<commit_msg>comment<commit_after>#![feature(const_fn)]

extern crate num;
extern crate glium;
extern crate rustc_serialize;

// From glium crate.
macro_rules! impl_uniform_block_basic {
    ($ty:ty, $uniform_ty:expr) => (
        impl ::glium::uniforms::UniformBlock for $ty {

            fn matches(layout: &::glium::program::BlockLayout, base_offset: usize)
                       -> Result<(), ::glium::uniforms::LayoutMismatchError>
            {
                if let &::glium::program::BlockLayout::BasicType { ty, offset_in_buffer } = layout {
                    if ty != $uniform_ty {
                        return Err(::glium::uniforms::LayoutMismatchError::TypeMismatch {
                            expected: ty,
                            obtained: $uniform_ty,
                        });
                    }

                    if offset_in_buffer != base_offset {
                        return Err(::glium::uniforms::LayoutMismatchError::OffsetMismatch {
                            expected: offset_in_buffer,
                            obtained: base_offset,
                        });
                    }

                    Ok(())

                } else {
                    Err(::glium::uniforms::LayoutMismatchError::LayoutMismatch {
                        expected: layout.clone(),
                        obtained: ::glium::program::BlockLayout::BasicType {
                            ty: $uniform_ty,
                            offset_in_buffer: base_offset,
                        }
                    })
                }
            }

            #[inline]
            fn build_layout(base_offset: usize) -> ::glium::program::BlockLayout {
                ::glium::program::BlockLayout::BasicType {
                    ty: $uniform_ty,
                    offset_in_buffer: base_offset,
                }
            }
        }
    )
}

mod vector;
mod matrix;

pub use vector::{
    Vec2,
    Vec3,
    Vec4,
};

pub use matrix::{
    Mat4,
};

pub use vector::{
    Vector,
    Dot,
    Cross,
    Normalize,
    Length,
    Repeat,
};

pub use matrix::{
    Translate,
    Rotate,
    Transpose,
    Inverse,
    Scale,
    LookAt,
    Projection,
    Matrix,
};

pub use num::{
    One,
    Zero,
};

pub use std::f32::consts::PI;
pub const PI_2: f32 = PI * 2.0;

pub const fn radians(n: f32) -> f32 {
    n * (PI / 180.0)
}

pub const fn degrees(n: f32) -> f32 {
    n * (180.0 / PI)
}

<|endoftext|>


<commit_before>//! [tui](https://github.com/fdehau/tui-rs) is a library used to build rich
//! terminal users interfaces and dashboards.
//!
//! ![](https://raw.githubusercontent.com/fdehau/tui-rs/master/docs/demo.gif)
//!
//! # Get started
//!
//! ## Creating a `Terminal`
//!
//! Every application using `tui` should start by instantiating a `Terminal`. It is
//! a light abstraction over available backends that provides basic functionalities
//! such as clearing the screen, hiding the cursor, etc. By default only the `termion`
//! backend is available.
//!
//! ```rust,no_run
//! extern crate tui;
//!
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//!
//! fn main() {
//!     let backend = RawBackend::new().unwrap();
//!     let mut terminal = Terminal::new(backend).unwrap();
//! }
//! ```
//!
//! If for some reason, you might want to use the `rustbox` backend instead, you
//! need the to replace your `tui` dependency specification by:
//!
//! ```toml
//! [dependencies.tui]
//! version = "0.2.0"
//! default-features = false
//! features = ['rustbox']
//! ```
//!
//! and then create the terminal in a similar way:
//!
//! ```rust,ignore
//! extern crate tui;
//!
//! use tui::Terminal;
//! use tui::backend::RustboxBackend;
//!
//! fn main() {
//!     let backend = RustboxBackend::new().unwrap();
//!     let mut terminal = Terminal::new(backend).unwrap();
//! }
//! ```
//!
//! ## Building a User Interface (UI)
//!
//! Every component of your interface will be implementing the `Widget` trait.
//! The library comes with a predefined set of widgets that should met most of
//! your use cases. You are also free to implement your owns.
//!
//! Each widget follows a builder pattern API providing a default configuration
//! along with methods to customize them. The widget is then registered using
//! its `render` method that take a `Terminal` instance and an area to draw
//! to.
//!
//! The following example renders a block of the size of the terminal:
//!
//! ```rust,no_run
//! extern crate tui;
//!
//! use std::io;
//!
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Group, Size, Direction};
//!
//! fn main() {
//!     let mut terminal = init().expect("Failed initialization");
//!     draw(&mut terminal).expect("Failed to draw");
//! }
//!
//! fn init() -> Result<Terminal<RawBackend>, io::Error> {
//!     let backend = RawBackend::new()?;
//!     Terminal::new(backend)
//! }
//!
//! fn draw(t: &mut Terminal<RawBackend>) -> Result<(), io::Error> {
//!
//!     let size = t.size()?;
//!
//!     Block::default()
//!         .title("Block")
//!         .borders(Borders::ALL)
//!         .render(t, &size);
//!
//!     t.draw()
//! }
//! ```
//!
//! ## Layout
//!
//! The library comes with a basic yet useful layout management object called
//! `Group`. As you may see below and in the examples, the library makes heavy
//! use of the builder pattern to provide full customization. And the `Group`
//! object is no exception:
//!
//! ```rust,no_run
//! extern crate tui;
//!
//! use std::io;
//!
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Group, Size, Direction};
//!
//! fn main() {
//!     let mut terminal = init().expect("Failed initialization");
//!     draw(&mut terminal).expect("Failed to draw");
//! }
//!
//! fn init() -> Result<Terminal<RawBackend>, io::Error> {
//!     let backend = RawBackend::new()?;
//!     Terminal::new(backend)
//! }
//!
//! fn draw(t: &mut Terminal<RawBackend>) -> Result<(), io::Error> {
//!
//!     let size = t.size()?;
//!
//!     Group::default()
//!         .direction(Direction::Vertical)
//!         .margin(1)
//!         .sizes(&[Size::Fixed(10), Size::Max(20), Size::Min(10)])
//!         .render(t, &size, |t, chunks| {
//!             Block::default()
//!                 .title("Block")
//!                 .borders(Borders::ALL)
//!                 .render(t, &chunks[0]);
//!             Block::default()
//!                 .title("Block 2")
//!                 .borders(Borders::ALL)
//!                 .render(t, &chunks[2]);
//!         });
//!
//!     t.draw()
//! }
//! ```
//!
//! This let you describe responsive terminal UI by nesting groups. You should note
//! that by default the computed layout tries to fill the available space
//! completely. So if for any reason you might need a blank space somewhere, try to
//! pass an additional size to the group and don't use the corresponding area inside
//! the render method.
//!
//! Once you have finished to describe the UI, you just need to call `draw`
//! on `Terminal` to actually flush to the terminal.

#[macro_use]
extern crate bitflags;
extern crate cassowary;
#[macro_use]
extern crate log;
extern crate unicode_segmentation;
extern crate unicode_width;

pub mod buffer;
pub mod symbols;
pub mod backend;
pub mod terminal;
pub mod widgets;
pub mod style;
pub mod layout;

pub use self::terminal::Terminal;
<commit_msg>[doc] Fix layout example in documentation<commit_after>//! [tui](https://github.com/fdehau/tui-rs) is a library used to build rich
//! terminal users interfaces and dashboards.
//!
//! ![](https://raw.githubusercontent.com/fdehau/tui-rs/master/docs/demo.gif)
//!
//! # Get started
//!
//! ## Creating a `Terminal`
//!
//! Every application using `tui` should start by instantiating a `Terminal`. It is
//! a light abstraction over available backends that provides basic functionalities
//! such as clearing the screen, hiding the cursor, etc. By default only the `termion`
//! backend is available.
//!
//! ```rust,no_run
//! extern crate tui;
//!
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//!
//! fn main() {
//!     let backend = RawBackend::new().unwrap();
//!     let mut terminal = Terminal::new(backend).unwrap();
//! }
//! ```
//!
//! If for some reason, you might want to use the `rustbox` backend instead, you
//! need the to replace your `tui` dependency specification by:
//!
//! ```toml
//! [dependencies.tui]
//! version = "0.2.0"
//! default-features = false
//! features = ['rustbox']
//! ```
//!
//! and then create the terminal in a similar way:
//!
//! ```rust,ignore
//! extern crate tui;
//!
//! use tui::Terminal;
//! use tui::backend::RustboxBackend;
//!
//! fn main() {
//!     let backend = RustboxBackend::new().unwrap();
//!     let mut terminal = Terminal::new(backend).unwrap();
//! }
//! ```
//!
//! ## Building a User Interface (UI)
//!
//! Every component of your interface will be implementing the `Widget` trait.
//! The library comes with a predefined set of widgets that should met most of
//! your use cases. You are also free to implement your owns.
//!
//! Each widget follows a builder pattern API providing a default configuration
//! along with methods to customize them. The widget is then registered using
//! its `render` method that take a `Terminal` instance and an area to draw
//! to.
//!
//! The following example renders a block of the size of the terminal:
//!
//! ```rust,no_run
//! extern crate tui;
//!
//! use std::io;
//!
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Group, Size, Direction};
//!
//! fn main() {
//!     let mut terminal = init().expect("Failed initialization");
//!     draw(&mut terminal).expect("Failed to draw");
//! }
//!
//! fn init() -> Result<Terminal<RawBackend>, io::Error> {
//!     let backend = RawBackend::new()?;
//!     Terminal::new(backend)
//! }
//!
//! fn draw(t: &mut Terminal<RawBackend>) -> Result<(), io::Error> {
//!
//!     let size = t.size()?;
//!
//!     Block::default()
//!         .title("Block")
//!         .borders(Borders::ALL)
//!         .render(t, &size);
//!
//!     t.draw()
//! }
//! ```
//!
//! ## Layout
//!
//! The library comes with a basic yet useful layout management object called
//! `Group`. As you may see below and in the examples, the library makes heavy
//! use of the builder pattern to provide full customization. And the `Group`
//! object is no exception:
//!
//! ```rust,no_run
//! extern crate tui;
//!
//! use std::io;
//!
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Group, Size, Direction};
//!
//! fn main() {
//!     let mut terminal = init().expect("Failed initialization");
//!     draw(&mut terminal).expect("Failed to draw");
//! }
//!
//! fn init() -> Result<Terminal<RawBackend>, io::Error> {
//!     let backend = RawBackend::new()?;
//!     Terminal::new(backend)
//! }
//!
//! fn draw(t: &mut Terminal<RawBackend>) -> Result<(), io::Error> {
//!
//!     let size = t.size()?;
//!
//!     Group::default()
//!         .direction(Direction::Vertical)
//!         .margin(1)
//!         .sizes(&[Size::Percent(10), Size::Percent(80), Size::Percent(10)])
//!         .render(t, &size, |t, chunks| {
//!             Block::default()
//!                 .title("Block")
//!                 .borders(Borders::ALL)
//!                 .render(t, &chunks[0]);
//!             Block::default()
//!                 .title("Block 2")
//!                 .borders(Borders::ALL)
//!                 .render(t, &chunks[2]);
//!         });
//!
//!     t.draw()
//! }
//! ```
//!
//! This let you describe responsive terminal UI by nesting groups. You should note
//! that by default the computed layout tries to fill the available space
//! completely. So if for any reason you might need a blank space somewhere, try to
//! pass an additional size to the group and don't use the corresponding area inside
//! the render method.
//!
//! Once you have finished to describe the UI, you just need to call `draw`
//! on `Terminal` to actually flush to the terminal.

#[macro_use]
extern crate bitflags;
extern crate cassowary;
#[macro_use]
extern crate log;
extern crate unicode_segmentation;
extern crate unicode_width;

pub mod buffer;
pub mod symbols;
pub mod backend;
pub mod terminal;
pub mod widgets;
pub mod style;
pub mod layout;

pub use self::terminal::Terminal;
<|endoftext|>