<commit_before>#![feature(globs)]
#![feature(phase)]
#[phase(plugin)]

extern crate regex_macros;
extern crate regex;

use std::io::*;
use std::sync::atomic::*;
use std::sync::{Arc, RWLock};
use std::ascii::AsciiExt;
use regex::Regex;
use std::collections::HashMap;

pub struct IrcClient {
    socket: TcpStream,
    reader: Option<BufferedReader<TcpStream>>,
    reader_created: Arc<AtomicBool>,
    pub permitted: Regex,
    listeners: Arc<RWLock<HashMap<String, Vec<|&mut IrcEvent|:'static>>>>
}

impl IrcClient {
    pub fn new(addr: &str) -> IrcClient {
        return IrcClient {
            socket: TcpStream::connect(addr).unwrap(),
            reader: None,
            reader_created: Arc::new(AtomicBool::new(false)),
            permitted: regex!(r"^Dabo[^!]*![^@]*@me.dabo.guru$"),
            listeners: Arc::new(RWLock::new(HashMap::new()))
        }
    }

    pub fn send(&mut self, command: &str) {
        println!("Sending: {}", command)
        self.socket.write(command.as_bytes()).ok().expect("Failed to write to stream");
        self.socket.write(b"\n").ok().expect("Failed to write to stream");
        self.socket.flush().ok().expect("Failed to flush stream");
    }

    pub fn start_receiving(&self) {
        let reader = self.clone();
        reader.spawn_reading_thread();
    }

    pub fn add_listener(&mut self, command: &str, f: |&mut IrcEvent|:'static) {
        let command_string = command.into_string().to_ascii_lower();
        let mut listener_map = self.listeners.write();
        {
            // I can't use a match here because then I would be borrowing listener_map mutably twice?
            // Once for the match statement, and a second time inside the None branch
            if listener_map.contains_key(&command_string) {
                listener_map.get_mut(&command_string).expect("Wat").push(f);
            } else {
                listener_map.insert(command_string, vec!(f));
            }
        }
        listener_map.downgrade();
    }

    fn init_reader(&mut self) {
        if self.reader_created.swap(true, Ordering::Relaxed) {
            panic!("Reader already created");
        }
        self.reader = Some(BufferedReader::new(self.socket.clone()));
    }

    fn read_line(&mut self) -> Result<String, IoError> {
        let reader = self.reader.as_mut().unwrap();
        let whole_line = try!(reader.read_line());
        return Ok(whole_line.trim_right().into_string())
    }

    fn spawn_reading_thread(mut self) {
        spawn(proc() {
            self.init_reader();
            loop {
                let input = match self.read_line() {
                    Ok(v) => v,
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
                let message_split: Vec<&str> = input.split(' ').collect();
                let (command, args, possible_mask) = if message_split[0].starts_with(":") {
                    (message_split[1], message_split.slice_from(2), Some(message_split[0].slice_from(1)))
                } else {
                    (message_split[0], message_split.slice_from(1), None)
                };
                match possible_mask {
                    Some(v) => println!("Received {} from {}: {}", command, v, args.connect(" ")),
                    None => println!("Received {}: {}", command, args.connect(" "))
                }
                let shared_self = &mut self.clone();
                let event = &mut IrcEvent::new(shared_self, command, args, possible_mask);
                let mut listener_map = self.listeners.write();
                {
                    let mut command_listeners = match listener_map.get_mut(&command.to_ascii_lower()) {
                        Some(v) => v,
                        None => continue
                    };

                    for listener in command_listeners.iter_mut() {
                        (*listener)(event);
                    }
                }
                listener_map.downgrade();
            }
        });
    }
}

impl Clone for IrcClient {
    fn clone(&self) -> IrcClient {
        return IrcClient {
            socket: self.socket.clone(),
            reader: None,
            reader_created: self.reader_created.clone(),
            permitted: self.permitted.clone(),
            listeners: self.listeners.clone()
        }
    }

    fn clone_from(&mut self, source: &IrcClient) {
        self.socket = source.socket.clone();
        self.reader = None;
        self.reader_created = source.reader_created.clone();
        self.permitted = source.permitted.clone();
        self.listeners = source.listeners.clone();
    }
}

// TODO: Make all of these variables private and use methods to access them
pub struct IrcEvent<'a> {
    pub client: &'a mut IrcClient,
    pub command: &'a str,
    pub args: &'a [&'a str],
    pub mask: Option<&'a str>
}

impl <'a> IrcEvent<'a> {
    pub fn new<'b>(client: &'b mut IrcClient, command: &'b str, args: &'b [&'b str], mask: Option<&'b str>) -> IrcEvent<'b> {
        return IrcEvent {
            client: client,
            command: command,
            args: args,
            mask: mask
        }
    }
}
<commit_msg>Use 'a instead of 'b<commit_after>#![feature(globs)]
#![feature(phase)]
#[phase(plugin)]

extern crate regex_macros;
extern crate regex;

use std::io::*;
use std::sync::atomic::*;
use std::sync::{Arc, RWLock};
use std::ascii::AsciiExt;
use regex::Regex;
use std::collections::HashMap;

pub struct IrcClient {
    socket: TcpStream,
    reader: Option<BufferedReader<TcpStream>>,
    reader_created: Arc<AtomicBool>,
    pub permitted: Regex,
    listeners: Arc<RWLock<HashMap<String, Vec<|&mut IrcEvent|:'static>>>>
}

impl IrcClient {
    pub fn new(addr: &str) -> IrcClient {
        return IrcClient {
            socket: TcpStream::connect(addr).unwrap(),
            reader: None,
            reader_created: Arc::new(AtomicBool::new(false)),
            permitted: regex!(r"^Dabo[^!]*![^@]*@me.dabo.guru$"),
            listeners: Arc::new(RWLock::new(HashMap::new()))
        }
    }

    pub fn send(&mut self, command: &str) {
        println!("Sending: {}", command)
        self.socket.write(command.as_bytes()).ok().expect("Failed to write to stream");
        self.socket.write(b"\n").ok().expect("Failed to write to stream");
        self.socket.flush().ok().expect("Failed to flush stream");
    }

    pub fn start_receiving(&self) {
        let reader = self.clone();
        reader.spawn_reading_thread();
    }

    pub fn add_listener(&mut self, command: &str, f: |&mut IrcEvent|:'static) {
        let command_string = command.into_string().to_ascii_lower();
        let mut listener_map = self.listeners.write();
        {
            // I can't use a match here because then I would be borrowing listener_map mutably twice?
            // Once for the match statement, and a second time inside the None branch
            if listener_map.contains_key(&command_string) {
                listener_map.get_mut(&command_string).expect("Wat").push(f);
            } else {
                listener_map.insert(command_string, vec!(f));
            }
        }
        listener_map.downgrade();
    }

    fn init_reader(&mut self) {
        if self.reader_created.swap(true, Ordering::Relaxed) {
            panic!("Reader already created");
        }
        self.reader = Some(BufferedReader::new(self.socket.clone()));
    }

    fn read_line(&mut self) -> Result<String, IoError> {
        let reader = self.reader.as_mut().unwrap();
        let whole_line = try!(reader.read_line());
        return Ok(whole_line.trim_right().into_string())
    }

    fn spawn_reading_thread(mut self) {
        spawn(proc() {
            self.init_reader();
            loop {
                let input = match self.read_line() {
                    Ok(v) => v,
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
                let message_split: Vec<&str> = input.split(' ').collect();
                let (command, args, possible_mask) = if message_split[0].starts_with(":") {
                    (message_split[1], message_split.slice_from(2), Some(message_split[0].slice_from(1)))
                } else {
                    (message_split[0], message_split.slice_from(1), None)
                };
                match possible_mask {
                    Some(v) => println!("Received {} from {}: {}", command, v, args.connect(" ")),
                    None => println!("Received {}: {}", command, args.connect(" "))
                }
                let shared_self = &mut self.clone();
                let event = &mut IrcEvent::new(shared_self, command, args, possible_mask);
                let mut listener_map = self.listeners.write();
                {
                    let mut command_listeners = match listener_map.get_mut(&command.to_ascii_lower()) {
                        Some(v) => v,
                        None => continue
                    };

                    for listener in command_listeners.iter_mut() {
                        (*listener)(event);
                    }
                }
                listener_map.downgrade();
            }
        });
    }
}

impl Clone for IrcClient {
    fn clone(&self) -> IrcClient {
        return IrcClient {
            socket: self.socket.clone(),
            reader: None,
            reader_created: self.reader_created.clone(),
            permitted: self.permitted.clone(),
            listeners: self.listeners.clone()
        }
    }

    fn clone_from(&mut self, source: &IrcClient) {
        self.socket = source.socket.clone();
        self.reader = None;
        self.reader_created = source.reader_created.clone();
        self.permitted = source.permitted.clone();
        self.listeners = source.listeners.clone();
    }
}

// TODO: Make all of these variables private and use methods to access them
pub struct IrcEvent<'a> {
    pub client: &'a mut IrcClient,
    pub command: &'a str,
    pub args: &'a [&'a str],
    pub mask: Option<&'a str>
}

impl <'a> IrcEvent<'a> {
    pub fn new(client: &'a mut IrcClient, command: &'a str, args: &'a [&'a str], mask: Option<&'a str>) -> IrcEvent<'a> {
        return IrcEvent {
            client: client,
            command: command,
            args: args,
            mask: mask
        }
    }
}
<|endoftext|>


<commit_before>
extern crate byteorder;

pub mod klv;
pub mod serializer;

#[cfg(test)]
mod test {
  // use serializer::decoder::*;
  use serializer::encoder::*;
  use klv::klv::*;
  use klv::key::partition::*;
  use klv::key::dict::*;
  use klv::key::key::Key;
  use klv::value::value::*;
  use klv::value::essence_identifiers::*;
  use klv::value::value::Element;

  #[test]
  fn read_empty_file() {
    use std::io::Cursor;

    let mut stream = Cursor::new(vec![0; 0]);

    let result = next_klv(&mut stream);

    assert!(result.is_ok());
    assert!(result.unwrap() == None);
  }

  #[test]
  fn read_first_key() {
    use std::io::Cursor;

    let data = vec![
      0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x00,
      0x83, 0x00, 0x00, 0x68,
      0x00, 0x01, 0x00, 0x02, // version major and minor
      0x00, 0x00, 0x01, 0x00, // kag size
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // this partition
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // previous partition
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // footer partition
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // header byte count
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // index byte count
      0x00, 0x00, 0x00, 0x00, // index sid
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // body byte count
      0x00, 0x00, 0x00, 0x00, // body sid
      0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x00, // operational pattern
      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, // UL batch
      0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x02, 0x0d, 0x01, 0x03, 0x01, 0x02, 0x04, 0x97, 0x01, // Mpeg ES
    ];

    let mut stream = Cursor::new(data);

    let valid_klv = next_klv(&mut stream).unwrap().unwrap();

    assert_eq!(valid_klv, Klv {
      key: Key {
        identifier: KeyIdentifier::HeaderPartition{status: PartitionStatus::ClosedAndComplete}
      },
      value: Value {
        elements: vec![
          Element {
            identifier: ElementIdentifier::PartitionMajor {value: 1},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionMinor {value: 2},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionKagSize {size: 256},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionThisPartition {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionPreviousPartition {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionFooterPartition {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionHeaderByteCount {size: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionIndexByteCount {size: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionIndexSid {value: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionByteOffset {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionBodySid {value: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionOperationalPattern {
              item_complexity: 1,
              package_complexity: 'a',
              internal_essence: true,
              stream_file: true,
              uni_track: false
            },
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionEssenceContainers {
              essences: vec![EssenceIdentifier::MpegEsWithStreamIdFrameWrapped]
            },
            value: None
          }
        ]
      }
    });
  }

  #[test]
  fn generate_file() {
    use std::io::prelude::*;
    use std::io::Cursor;

    let mut stream = Cursor::new(vec![0; 0]);

    let header_key = Key {
      identifier: KeyIdentifier::HeaderPartition{status: PartitionStatus::ClosedAndComplete}
    };

    let header_value = Value {
      elements: vec![
        Element{
          identifier: ElementIdentifier::PartitionMajor{value: 1},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionMinor{value: 2},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionKagSize{size: 512},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionThisPartition{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionPreviousPartition{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionFooterPartition{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionHeaderByteCount{size: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionIndexByteCount{size: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionIndexSid{value: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionByteOffset{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionBodySid{value: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionOperationalPattern {
            item_complexity: 1,
            package_complexity: 'a',
            internal_essence: true,
            stream_file: true,
            uni_track: false
          },
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionEssenceContainers { essences: vec![] },
          value: None
        },
      ]
    };

    let header_klv = Klv {
      key: header_key,
      value: header_value
    };

    stream.write(Encoder::serialise(&header_klv).as_ref()).unwrap();

    // let static_track_key = Key {
    //   identifier: KeyIdentifier::StaticTrackSet
    // };

    // let static_track_value = Value {
    //   elements: vec![
    //     Element{
    //       identifier: ElementIdentifier::InstanceUid,
    //       value: None
    //     },
    //     Element{
    //       identifier: ElementIdentifier::GenerationIdentifier,
    //       value: None
    //     },
    //   ]
    // };

    // let static_track_klv = Klv {
    //   key: static_track_key,
    //   value: static_track_value
    // };

    // stream.write(Encoder::serialise(&static_track_klv).as_ref()).unwrap();

    let frame_key = Key {
      identifier: KeyIdentifier::PictureItemMpegFrameWrappedPictureElement
    };

    let frame_value = Value {
      elements: vec![
        Element{
          identifier: ElementIdentifier::Data{data: vec![0x00; 2000]},
          value: None
        }
      ]
    };

    let frame_klv = Klv {
      key: frame_key,
      value: frame_value
    };

    stream.write(Encoder::serialise(&frame_klv).as_ref()).unwrap();
    assert_eq!(stream.get_ref().len(), 2124);
  }
}
<commit_msg>fix unit tests<commit_after>
extern crate byteorder;

pub mod klv;
pub mod serializer;

#[cfg(test)]
mod test {
  // use serializer::decoder::*;
  use serializer::encoder::*;
  use klv::klv::*;
  use klv::klv_reader::*;
  use klv::key::partition::*;
  use klv::key::dict::*;
  use klv::key::key::Key;
  use klv::value::value::*;
  use klv::value::essence_identifiers::*;
  use klv::value::value::Element;

  #[test]
  fn read_empty_file() {
    use std::io::Cursor;

    let stream = Cursor::new(vec![0; 0]);
    let mut reader = KlvReader{
      stream: stream,
      elements: vec![]
    };

    let result = next_klv(&mut reader);

    assert!(result.is_ok());
    assert!(result.unwrap() == None);
  }

  #[test]
  fn read_first_key() {
    use std::io::Cursor;

    let data = vec![
      0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x00,
      0x83, 0x00, 0x00, 0x68,
      0x00, 0x01, 0x00, 0x02, // version major and minor
      0x00, 0x00, 0x01, 0x00, // kag size
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // this partition
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // previous partition
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // footer partition
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // header byte count
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // index byte count
      0x00, 0x00, 0x00, 0x00, // index sid
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // body byte count
      0x00, 0x00, 0x00, 0x00, // body sid
      0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x00, // operational pattern
      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, // UL batch
      0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x02, 0x0d, 0x01, 0x03, 0x01, 0x02, 0x04, 0x97, 0x01, // Mpeg ES
    ];

    let stream = Cursor::new(data);
    let mut reader = KlvReader{
      stream: stream,
      elements: vec![]
    };

    let valid_klv = next_klv(&mut reader).unwrap().unwrap();

    assert_eq!(valid_klv, Klv {
      key: Key {
        identifier: KeyIdentifier::HeaderPartition{status: PartitionStatus::ClosedAndComplete}
      },
      value: Value {
        elements: vec![
          Element {
            identifier: ElementIdentifier::PartitionMajor {value: 1},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionMinor {value: 2},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionKagSize {size: 256},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionThisPartition {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionPreviousPartition {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionFooterPartition {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionHeaderByteCount {size: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionIndexByteCount {size: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionIndexSid {value: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionByteOffset {offset: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionBodySid {value: 0},
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionOperationalPattern {
              item_complexity: 1,
              package_complexity: 'a',
              internal_essence: true,
              stream_file: true,
              uni_track: false
            },
            value: None
          },
          Element {
            identifier: ElementIdentifier::PartitionEssenceContainers {
              essences: vec![EssenceIdentifier::MpegEsWithStreamIdFrameWrapped]
            },
            value: None
          }
        ]
      }
    });
  }

  #[test]
  fn generate_file() {
    use std::io::prelude::*;
    use std::io::Cursor;

    let mut stream = Cursor::new(vec![0; 0]);

    let header_key = Key {
      identifier: KeyIdentifier::HeaderPartition{status: PartitionStatus::ClosedAndComplete}
    };

    let header_value = Value {
      elements: vec![
        Element{
          identifier: ElementIdentifier::PartitionMajor{value: 1},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionMinor{value: 2},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionKagSize{size: 512},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionThisPartition{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionPreviousPartition{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionFooterPartition{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionHeaderByteCount{size: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionIndexByteCount{size: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionIndexSid{value: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionByteOffset{offset: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionBodySid{value: 0},
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionOperationalPattern {
            item_complexity: 1,
            package_complexity: 'a',
            internal_essence: true,
            stream_file: true,
            uni_track: false
          },
          value: None
        },
        Element{
          identifier: ElementIdentifier::PartitionEssenceContainers { essences: vec![] },
          value: None
        },
      ]
    };

    let header_klv = Klv {
      key: header_key,
      value: header_value
    };

    stream.write(Encoder::serialise(&header_klv).as_ref()).unwrap();

    // let static_track_key = Key {
    //   identifier: KeyIdentifier::StaticTrackSet
    // };

    // let static_track_value = Value {
    //   elements: vec![
    //     Element{
    //       identifier: ElementIdentifier::InstanceUid,
    //       value: None
    //     },
    //     Element{
    //       identifier: ElementIdentifier::GenerationIdentifier,
    //       value: None
    //     },
    //   ]
    // };

    // let static_track_klv = Klv {
    //   key: static_track_key,
    //   value: static_track_value
    // };

    // stream.write(Encoder::serialise(&static_track_klv).as_ref()).unwrap();

    let frame_key = Key {
      identifier: KeyIdentifier::PictureItemMpegFrameWrappedPictureElement
    };

    let frame_value = Value {
      elements: vec![
        Element{
          identifier: ElementIdentifier::Data{data: vec![0x00; 2000]},
          value: None
        }
      ]
    };

    let frame_klv = Klv {
      key: frame_key,
      value: frame_value
    };

    stream.write(Encoder::serialise(&frame_klv).as_ref()).unwrap();
    assert_eq!(stream.get_ref().len(), 2124);
  }
}
<|endoftext|>


<commit_before>/*!
Simple persistent generic HashMap/Key-value store, using file locking to limit
writing between threads.

This is in a beta state at the moment.

Basic usage:

```
extern crate typedb;

use typedb::{ KV, Value };

fn main() {
    let mut test_store = KV::<String, Value>::new("./db.cab").unwrap();

    let _ = test_store.insert("key".to_string(), Value::String("value".to_string()));
    println!("{:?}", test_store.get("key".to_string()));
    let _ = test_store.remove("key".to_string());
}
```

Usage with user defined Key and Value types:

```
#[macro_use]
extern crate typedb;
#[macro_use]
extern crate serde_derive;

use typedb::KV;

key!(MyKey:
    String(String),
    Int(i32),
);

value!(MyValue:
    String(String),
    Int(i32),
);

fn main() {
    let mut test_store = KV::<MyKey, MyValue>::new("./db.cab").unwrap();

    let _ = test_store.insert(MyKey::Int(1i32), MyValue::String("value".to_string()));
    println!("{:?}", test_store.get(MyKey::Int(1i32)));
    let _ = test_store.remove(MyKey::Int(1i32));
}
```

*/

#![deny(missing_docs)]

extern crate bincode;
#[macro_use]
extern crate log;
extern crate persy;
extern crate serde;
#[macro_use]
extern crate serde_derive;

/// Macros for simplifying custom key and value types definition
pub mod macros;

use std::collections::HashMap;
use std::hash::Hash;

use bincode::{deserialize, serialize};
use serde::ser::Serialize;
use serde::de::Deserialize;

use persy::{Config, PRes, Persy, PersyError};

/// A default value type to use with KV
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub enum Value {
    /// Cab default Value type for strings
    String(String),
    /// Cab default Value type for intergers
    Int(i32),
    /// Cab default Value type for floats
    Float(f32),
    /// Cab default Value type for a sub map
    Map(HashMap<String, Value>),
    /// Cab default Value type for list
    List(Vec<Value>),
}

/// Type alias for results from KV
type KVResult = PRes<bool>;

/// Type alias for PersyError
type KVError = PersyError;

/// The type that represents the key-value store
pub struct KV<K, V> {
    cab: HashMap<K, V>,
    persy: Persy,
}

impl<K, V> KV<K, V>
where
    K: Clone + Serialize + for<'kde> Deserialize<'kde> + Eq + Hash,
    V: Clone + Serialize + for<'vde> Deserialize<'vde>,
{
    /// Creates a new instance of the KV store
    pub fn new(p: &'static str) -> Result<KV<K, V>, PersyError> {
        // create the KV instance
        Persy::create(p)?;
        let persy = Persy::open(p, Config::new())?;

        let mut store = KV {
            cab: HashMap::new(),
            persy: persy,
        };

        match store.load_from_persist() {
            Ok(f) => trace!("{}", f),
            Err(e) => {
                return Err(e);
            }
        };

        Ok(store)
    }

    /// Inserta a key, value pair into the key-value store
    pub fn insert(&mut self, key: K, value: V) -> KVResult {
        // make sure mem version up to date
        self.load_from_persist()?;
        // insert into the HashMap
        self.cab.insert(key, value);
        // persist
        self.write_to_persist()
    }

    /// Get the value from a key
    pub fn get(&mut self, key: K) -> Result<Option<V>, KVError> {
        // make sure mem version up to date
        self.load_from_persist()?;
        // get the value from the cab
        match self.cab.get(&key) {
            Some(v) => Ok(Some((*v).clone())),
            None => Ok(None),
        }
    }

    /// Removes a key and associated value from the key-value Store
    pub fn remove(&mut self, key: K) -> KVResult {
        // make sure mem version up to date
        self.load_from_persist()?;
        // remove from the HashMap
        self.cab.remove(&key);
        // persist
        self.write_to_persist()
    }

    /// get all the keys contained in the KV Store
    pub fn keys(&mut self) -> Result<Vec<K>, KVError> {
        // make sure mem version up to date
        self.load_from_persist()?;
        // create a vec from the cabs keys
        Ok(self.cab.keys().map(|k| k.clone()).collect())
    }

    /// Writes the key-value Store to file
    fn write_to_persist(&mut self) -> KVResult {
        // attempt to write to the cab
        let mut tx = self.persy.begin()?;
        self.persy.create_segment(&mut tx, "tdb")?;

        // serialize the cab as a u8 vec
        let byte_vec: Vec<u8> = match serialize(&mut self.cab) {
            Ok(bv) => bv,
            Err(e) => {
                error!("serialize: {}", e);
                return Err(PersyError::Err("Couldn't encode".to_string()));
            }
        };

        self.persy.insert_record(&mut tx, "tdb", &byte_vec)?;

        let prepared = self.persy.prepare_commit(tx)?;
        self.persy.commit(prepared)?;

        Ok(true)
    }

    /// Loads key-value store from file
    fn load_from_persist(&mut self) -> KVResult {
        if self.persy.exists_segment("tdb")? {
            for rec in self.persy.scan_records("tdb")? {
                let byte_vec = rec.content;
                // deserialize u8 vec back into HashMap
                match deserialize(byte_vec.as_slice()) {
                    Ok(f) => {
                        // assign read HashMap back to self
                        self.cab = f;
                    }
                    Err(e) => {
                        error!("{}", e);
                        continue;
                    }
                };
            }
        }

        Ok(true)
    }
}
<commit_msg>Check if segment exists before trying to create<commit_after>/*!
Simple persistent generic HashMap/Key-value store, using file locking to limit
writing between threads.

This is in a beta state at the moment.

Basic usage:

```
extern crate typedb;

use typedb::{ KV, Value };

fn main() {
    let mut test_store = KV::<String, Value>::new("./db.cab").unwrap();

    let _ = test_store.insert("key".to_string(), Value::String("value".to_string()));
    println!("{:?}", test_store.get("key".to_string()));
    let _ = test_store.remove("key".to_string());
}
```

Usage with user defined Key and Value types:

```
#[macro_use]
extern crate typedb;
#[macro_use]
extern crate serde_derive;

use typedb::KV;

key!(MyKey:
    String(String),
    Int(i32),
);

value!(MyValue:
    String(String),
    Int(i32),
);

fn main() {
    let mut test_store = KV::<MyKey, MyValue>::new("./db.cab").unwrap();

    let _ = test_store.insert(MyKey::Int(1i32), MyValue::String("value".to_string()));
    println!("{:?}", test_store.get(MyKey::Int(1i32)));
    let _ = test_store.remove(MyKey::Int(1i32));
}
```

*/

#![deny(missing_docs)]

extern crate bincode;
#[macro_use]
extern crate log;
extern crate persy;
extern crate serde;
#[macro_use]
extern crate serde_derive;

/// Macros for simplifying custom key and value types definition
pub mod macros;

use std::collections::HashMap;
use std::hash::Hash;

use bincode::{deserialize, serialize};
use serde::ser::Serialize;
use serde::de::Deserialize;

use persy::{Config, PRes, Persy, PersyError};

/// A default value type to use with KV
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub enum Value {
    /// Cab default Value type for strings
    String(String),
    /// Cab default Value type for intergers
    Int(i32),
    /// Cab default Value type for floats
    Float(f32),
    /// Cab default Value type for a sub map
    Map(HashMap<String, Value>),
    /// Cab default Value type for list
    List(Vec<Value>),
}

/// Type alias for results from KV
type KVResult = PRes<bool>;

/// Type alias for PersyError
type KVError = PersyError;

/// The type that represents the key-value store
pub struct KV<K, V> {
    cab: HashMap<K, V>,
    persy: Persy,
}

impl<K, V> KV<K, V>
where
    K: Clone + Serialize + for<'kde> Deserialize<'kde> + Eq + Hash,
    V: Clone + Serialize + for<'vde> Deserialize<'vde>,
{
    /// Creates a new instance of the KV store
    pub fn new(p: &'static str) -> Result<KV<K, V>, PersyError> {
        // create the KV instance
        Persy::create(p)?;
        let persy = Persy::open(p, Config::new())?;

        let mut store = KV {
            cab: HashMap::new(),
            persy: persy,
        };

        match store.load_from_persist() {
            Ok(f) => trace!("{}", f),
            Err(e) => {
                return Err(e);
            }
        };

        Ok(store)
    }

    /// Inserta a key, value pair into the key-value store
    pub fn insert(&mut self, key: K, value: V) -> KVResult {
        // make sure mem version up to date
        self.load_from_persist()?;
        // insert into the HashMap
        self.cab.insert(key, value);
        // persist
        self.write_to_persist()
    }

    /// Get the value from a key
    pub fn get(&mut self, key: K) -> Result<Option<V>, KVError> {
        // make sure mem version up to date
        self.load_from_persist()?;
        // get the value from the cab
        match self.cab.get(&key) {
            Some(v) => Ok(Some((*v).clone())),
            None => Ok(None),
        }
    }

    /// Removes a key and associated value from the key-value Store
    pub fn remove(&mut self, key: K) -> KVResult {
        // make sure mem version up to date
        self.load_from_persist()?;
        // remove from the HashMap
        self.cab.remove(&key);
        // persist
        self.write_to_persist()
    }

    /// get all the keys contained in the KV Store
    pub fn keys(&mut self) -> Result<Vec<K>, KVError> {
        // make sure mem version up to date
        self.load_from_persist()?;
        // create a vec from the cabs keys
        Ok(self.cab.keys().map(|k| k.clone()).collect())
    }

    /// Writes the key-value Store to file
    fn write_to_persist(&mut self) -> KVResult {
        // attempt to write to the cab
        let mut tx = self.persy.begin()?;
        // if the segment doesn't exist create
        // TODO poss move to db creation
        if !self.persy.exists_segment("tdb")? {
            self.persy.create_segment(&mut tx, "tdb")?;
        }

        // serialize the cab as a u8 vec
        let byte_vec: Vec<u8> = match serialize(&mut self.cab) {
            Ok(bv) => bv,
            Err(e) => {
                error!("serialize: {}", e);
                return Err(PersyError::Err("Couldn't encode".to_string()));
            }
        };

        self.persy.insert_record(&mut tx, "tdb", &byte_vec)?;

        let prepared = self.persy.prepare_commit(tx)?;
        self.persy.commit(prepared)?;

        Ok(true)
    }

    /// Loads key-value store from file
    fn load_from_persist(&mut self) -> KVResult {
        if self.persy.exists_segment("tdb")? {
            for rec in self.persy.scan_records("tdb")? {
                let byte_vec = rec.content;
                // deserialize u8 vec back into HashMap
                match deserialize(byte_vec.as_slice()) {
                    Ok(f) => {
                        // assign read HashMap back to self
                        self.cab = f;
                    }
                    Err(e) => {
                        error!("{}", e);
                        continue;
                    }
                };
            }
        }

        Ok(true)
    }
}
<|endoftext|>


<commit_before>#![feature(untagged_unions)]

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

use std::marker::PhantomData;

/// `PointerIter` iterates over an array of things that are
/// pointed to by an pointer.
pub struct PointerIter<'a, T: 'a> {
    ptr: *const T,
    count: usize,
    current: usize,
    lifetime: PhantomData<&'a T>,
}

impl<'a, T: 'a> PointerIter<'a, T> {
    pub fn new(ptr: *const T, count: usize) -> Self {
        PointerIter {
            ptr: ptr,
            count: count,
            current: 0,
            lifetime: PhantomData,
        }
    }
}

impl<'a, T: 'a> Iterator for PointerIter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        if self.current >= self.count {
            None
        } else {
            let object = unsafe { &*self.ptr.offset(self.current as _) };
            self.current += 1;
            Some(object)
        }
    }
}

impl cs_arm {
    pub fn operand_iter(&self) -> PointerIter<cs_arm_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_arm64 {
    pub fn operand_iter(&self) -> PointerIter<cs_arm64_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_mips {
    pub fn operand_iter(&self) -> PointerIter<cs_mips_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_ppc {
    pub fn operand_iter(&self) -> PointerIter<cs_ppc_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_sparc {
    pub fn operand_iter(&self) -> PointerIter<cs_sparc_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_sysz {
    pub fn operand_iter(&self) -> PointerIter<cs_sysz_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_x86 {
    pub fn operand_iter(&self) -> PointerIter<cs_x86_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_xcore {
    pub fn operand_iter(&self) -> PointerIter<cs_xcore_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_detail {
    pub fn regs_read_iter(&self) -> PointerIter<u8> {
        PointerIter::new(self.regs_read.as_ptr(), self.regs_read_count as _)
    }

    pub fn regs_write_iter(&self) -> PointerIter<u8> {
        PointerIter::new(self.regs_write.as_ptr(), self.regs_write_count as _)
    }

    pub fn groups_iter(&self) -> PointerIter<u8> {
        PointerIter::new(self.groups.as_ptr(), self.groups_count as _)
    }
}

use std::fmt;
impl fmt::Debug for cs_insn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("cs_insn")
            .field("id", &self.id)
            .field("address", &self.address)
            .field("size", &self.size)
            .field("bytes", &self.bytes)
            .field("mnemonic", &self.mnemonic)
            .field("detail", &self.detail)
            .finish()
    }
}
<commit_msg>Add functions to get strings from cs_insn<commit_after>#![feature(untagged_unions)]

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

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

/// `PointerIter` iterates over an array of things that are
/// pointed to by an pointer.
pub struct PointerIter<'a, T: 'a> {
    ptr: *const T,
    count: usize,
    current: usize,
    lifetime: PhantomData<&'a T>,
}

impl<'a, T: 'a> PointerIter<'a, T> {
    pub fn new(ptr: *const T, count: usize) -> Self {
        PointerIter {
            ptr: ptr,
            count: count,
            current: 0,
            lifetime: PhantomData,
        }
    }
}

impl<'a, T: 'a> Iterator for PointerIter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        if self.current >= self.count {
            None
        } else {
            let object = unsafe { &*self.ptr.offset(self.current as _) };
            self.current += 1;
            Some(object)
        }
    }
}

impl cs_arm {
    pub fn operand_iter(&self) -> PointerIter<cs_arm_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_arm64 {
    pub fn operand_iter(&self) -> PointerIter<cs_arm64_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_mips {
    pub fn operand_iter(&self) -> PointerIter<cs_mips_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_ppc {
    pub fn operand_iter(&self) -> PointerIter<cs_ppc_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_sparc {
    pub fn operand_iter(&self) -> PointerIter<cs_sparc_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_sysz {
    pub fn operand_iter(&self) -> PointerIter<cs_sysz_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_x86 {
    pub fn operand_iter(&self) -> PointerIter<cs_x86_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_xcore {
    pub fn operand_iter(&self) -> PointerIter<cs_xcore_op> {
        PointerIter::new(self.operands.as_ptr(), self.op_count as _)
    }
}

impl cs_detail {
    pub fn regs_read_iter(&self) -> PointerIter<u8> {
        PointerIter::new(self.regs_read.as_ptr(), self.regs_read_count as _)
    }

    pub fn regs_write_iter(&self) -> PointerIter<u8> {
        PointerIter::new(self.regs_write.as_ptr(), self.regs_write_count as _)
    }

    pub fn groups_iter(&self) -> PointerIter<u8> {
        PointerIter::new(self.groups.as_ptr(), self.groups_count as _)
    }
}

impl cs_insn {
    pub fn get_mnemonic(&self) -> String {
        unsafe { CStr::from_ptr(self.mnemonic.as_ptr()) }.to_string_lossy().into_owned()
    }

    pub fn get_op_str(&self) -> String {
        unsafe { CStr::from_ptr(self.op_str.as_ptr()) }.to_string_lossy().into_owned()
    }
}

use std::fmt;
impl fmt::Debug for cs_insn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("cs_insn")
            .field("id", &self.id)
            .field("address", &self.address)
            .field("size", &self.size)
            .field("bytes", &self.bytes)
            .field("mnemonic", &self.mnemonic)
            .field("detail", &self.detail)
            .finish()
    }
}
<|endoftext|>


<commit_before>//! An outline of how linear types could be combined with JS-managed data
//!
//! The idea is to use the JS context as a linear token.
//! Each JS context gets its own type `Cx` which implements the `JSContext` trait.
//! Access to read-only JS-managed data requires a context of type `&Cx`.
//! Access to read-write JS-managed data requires a context of type `&mut Cx`.
//! Linear use of the context ensures that Rust's memory safety is preserved (er, I hope).
//!
//! This API doesn't address tracing, which would have to be handled similarly
//! to servo's current JS bindings.
//!
//! One thing that makes this easier is that we can arrange for any
//! calls into the JS engine which might trigger GC to take a `&mut Cx`
//! argument, which gives us control of when GC might occur.

use std::marker::PhantomData;

/// The trait for accessing JS-managed data.
pub trait JSAccess<Cx>: Sized {
    /// Read-only access to native JS-managed data.
    fn get<'a, 'b:'a, T>(&'a self, managed: JSManaged<'b, Cx, T>) -> &'a T::ChangeLifetime where
        T: JSManageable<'a>;

    /// Read-write access to native JS-managed data.
    fn get_mut<'a, 'b:'a, T>(&'a mut self, managed: JSManaged<'b, Cx, T>) -> &'a mut T::ChangeLifetime where
        T: JSManageable<'a>;
}

/// The trait for JS contexts.
pub trait JSContext: JSAccess<Self> {
    /// Get a snapshot of the JS state.
    /// The snapshot only allows access to the methods that are guaranteed not to call GC,
    /// so we don't need to root JS-managed pointers during the lifetime of a snapshot.
    fn snapshot(&mut self) -> JSSnapshot<Self>;

    /// Give ownership of data to JS.
    /// This allocates JS heap, which may trigger GC.
    fn manage<'a, T>(&'a self, value: T) -> JSManaged<'a, Self, T>
        where T: 'a + JSManageable<'a, ChangeLifetime=T>;

    // A real implementation would also have JS methods such as those in jsapi.
}

/// A placholder for the real `JSTraceable`.
pub unsafe trait JSTraceable {}

/// The trait for native JS-manageable data.
pub unsafe trait JSManageable<'a>: JSTraceable {
    /// This type should have the same mnemory represention as `Self`.
    /// The only difference between `Self` and `Self::ChangeLifetime`
    /// is that any `JSManaged<'b, Cx, T>` should be replaced by
    /// `JSManaged<'a, Cx, T::ChangeLifetime>`.
    type ChangeLifetime: 'a;
}

/// A user of a JS context implements `JSContextConsumer`, which is called back
/// with a fresh JS context.
pub trait JSContextConsumer<T> {
    /// This callback is called with a fresh JS context.
    fn consume<Cx>(self, cx: &mut Cx) -> T where Cx: JSContext;
}

/// To create a fresh JS context, the user implements `JSContextConsumer`
/// for their type `K`, builds a `k:K`, then calls `with_js_context(k)`.
/// ```rust
///   struct MyConsumer;
///   impl JSContextConsumer<()> for MyConsumer {
///      fn consume<Cx: JSContext>(cx: Cx) {
///         // Do stuff with the JS context cx.
///      }
///   }
///   with_js_context(MyConsumer);
/// ```
pub fn with_js_context<C, T>(consumer: C) -> T where
    C: JSContextConsumer<T>
{
    // A real implementation would allocate a JS context
    let mut cx = JSContextImpl {};
    consumer.consume(&mut cx)
}

/// The type of JS-managed data in a JS context `Cx`, with lifetime `'a`.
///
/// If the user has access to a `JSManaged`, then the JS-managed
/// data is live for the given lifetime.
pub struct JSManaged<'a, Cx, T> {
    // JS reflector goes here
    raw: *mut T,
    marker: PhantomData<(&'a(),Cx)>,
}

impl<'a, Cx, T> Clone for JSManaged<'a, Cx, T> {
    fn clone(&self) -> Self {
        JSManaged {
            raw: self.raw,
            marker: self.marker,
        }
    }
}

impl<'a, Cx, T> Copy for JSManaged<'a, Cx, T> {
}

unsafe impl<'a, Cx, T> JSTraceable for JSManaged<'a, Cx, T> where
    T: JSTraceable
{
}

unsafe impl<'a, 'b, Cx, T> JSManageable<'b> for JSManaged<'a, Cx, T> where
    Cx: 'b,
    T: JSManageable<'b>,
{
    type ChangeLifetime = JSManaged<'b, Cx, T::ChangeLifetime>;
}

impl<'a, Cx, T> JSManaged<'a, Cx, T> {
    /// Convenience method for `access.get(self)`.
    pub fn get<'b, Access: JSAccess<Cx>>(self, access: &'b Access) -> &'b T::ChangeLifetime where
        T: JSManageable<'b>,
        'a: 'b,
    {
        access.get(self)
    }

    /// Convenience method for `access.get_mut(self)`.
    pub fn get_mut<'b, Access: JSAccess<Cx>>(self, access: &'b mut Access) -> &'b mut T::ChangeLifetime where
        T: JSManageable<'b>,
        'a: 'b,
    {
        access.get_mut(self)
    }

    /// Change the lifetime of JS-managed data.
    pub unsafe fn change_lifetime<'b>(self) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
    {
        JSManaged {
            raw: self.raw as *mut T::ChangeLifetime,
            marker: PhantomData,
        }
    }

    /// It's safe to contract the lifetime of JS-managed data.
    pub fn contract_lifetime<'b>(self) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
        'a: 'b,
    {
        unsafe { self.change_lifetime() }
    }

    /// It's safe to extend the lifetime of JS-managed data if it has been snapshotted.
    pub fn extend_lifetime<'b>(self, _: &JSSnapshot<'b, Cx>) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
        'b: 'a,
    {
        unsafe { self.change_lifetime() }
    }

    /// It's safe to extend the lifetime of JS-managed data by rooting it.
    pub fn root<'b>(self, _: &'b JSRoots<Cx>) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
        'b: 'a,
    {
        // The real thing would add the reflector to the root set.
        unsafe { self.change_lifetime() }
    }
}

/// A root set.
pub struct JSRoots<Cx> {
    // The real thing would contain a set of rooted JS objects.
    marker: PhantomData<Cx>,
}

impl<Cx> JSRoots<Cx> {
    pub fn new() -> JSRoots<Cx> {
        JSRoots {
            marker: PhantomData,
        }
    }
}

impl<Cx> Drop for JSRoots<Cx> {
    fn drop(&mut self) {
        // The real thing would unroot the root set.
    }
}

/// A snapshot of a JS context.
///
/// The idea here is that during the lifetime of a JSSnapshot<Cx>, the JS state
/// doesn't change, and in particular GC doesn't happen. This allows us to avoid
/// some rooting.
pub struct JSSnapshot<'c, Cx: 'c>(&'c mut Cx);

impl<'c, Cx: 'c> JSSnapshot<'c, Cx> where
    Cx: JSContext
{
    /// Build a new snapshot
    pub fn new(cx: &mut Cx) -> JSSnapshot<Cx> {
        JSSnapshot(cx)
    }
}

impl<'c, Cx: 'c> JSAccess<Cx> for JSSnapshot<'c, Cx> where
    Cx: JSContext
{
    /// Read-only access to native JS-managed data.
    fn get<'a, 'b:'a, T>(&'a self, managed: JSManaged<'b, Cx, T>) -> &'a T::ChangeLifetime where
        T: JSManageable<'a>
    {
        self.0.get(managed)
    }

    /// Read-write access to native JS-managed data.
    fn get_mut<'a, 'b:'a, T>(&'a mut self, managed: JSManaged<'b, Cx, T>) -> &'a mut T::ChangeLifetime where
        T: JSManageable<'a>
    {
        self.0.get_mut(managed)
    }
}

// It is important for safety that this implemention is not made public!
struct JSContextImpl {
    // JS context implementation goes here
}

impl JSAccess<JSContextImpl> for JSContextImpl {
    // The claim is that this is safe, since it takes a `&self`.
    fn get<'a, 'b:'a, T>(&'a self, managed: JSManaged<'b, Self, T>) -> &'a T::ChangeLifetime where
        T: JSManageable<'a>
    {
        unsafe { &*(managed.raw as *mut T::ChangeLifetime) }
    }

    // The claim is that this is safe, since it takes a `&mut self`.
    fn get_mut<'a, 'b:'a, T>(&'a mut self, managed: JSManaged<'b, Self, T>) -> &'a mut T::ChangeLifetime where
        T: JSManageable<'a>
    {
        unsafe { &mut *(managed.raw as *mut T::ChangeLifetime) }
    }
}

impl JSContext for JSContextImpl {
    fn snapshot(&mut self) -> JSSnapshot<Self> {
        JSSnapshot(self)
    }

    // This outline implementation just space-leaks all data,
    // the real thing would create a reflector, and add a finalizer hook.
    fn manage<'a, T>(&'a self, value: T) -> JSManaged<'a, Self, T>
        where T: 'a + JSManageable<'a, ChangeLifetime=T>
    {
        JSManaged {
            raw: Box::into_raw(Box::new(value)),
            marker: PhantomData,
        }
    }
}

#[test]
// This test constructs a two-node cyclic graph, which is the smallest
// example of something that uses `RefCell`s in servo's JS bindings.
fn test() {
    // A graph type
    type Graph<'a, Cx> = JSManaged<'a, Cx, NativeGraph<'a, Cx>>;
    struct NativeGraph<'a, Cx> {
        nodes: Vec<Node<'a, Cx>>,
    }
    unsafe impl<'a, Cx> JSTraceable for NativeGraph<'a, Cx> {}
    unsafe impl<'a, 'b, Cx: 'b> JSManageable<'b> for NativeGraph<'a, Cx> { type ChangeLifetime = NativeGraph<'b, Cx>; }
    // A node type
    type Node<'a, Cx> = JSManaged<'a, Cx, NativeNode<'a, Cx>>;
    struct NativeNode<'a, Cx> {
        data: usize,
        edges: Vec<Node<'a, Cx>>,
    }
    unsafe impl<'a, Cx> JSTraceable for NativeNode<'a, Cx> {}
    unsafe impl<'a, 'b, Cx: 'b> JSManageable<'b> for NativeNode<'a, Cx> { type ChangeLifetime = NativeNode<'b, Cx>; }
    // Build a cyclic graph
    struct Test;
    impl JSContextConsumer<()> for Test {
        fn consume<Cx>(self, cx: &mut Cx) where Cx: JSContext {
            let roots = JSRoots::new();
            let graph = cx.manage(NativeGraph { nodes: vec![] }).root(&roots);
            self.add_nodes(cx, graph);
            assert_eq!(graph.get(cx).nodes[0].get(cx).data, 1);
            assert_eq!(graph.get(cx).nodes[1].get(cx).data, 2);
            self.add_edges(&mut cx.snapshot(), graph);
            assert_eq!(graph.get(cx).nodes[0].get(cx).edges[0].get(cx).data, 2);
            assert_eq!(graph.get(cx).nodes[1].get(cx).edges[0].get(cx).data, 1);
        }
    }
    impl Test {
        fn add_nodes<'a, 'b, Cx: JSContext>(&'a self, cx: &'a mut Cx, graph: Graph<'b, Cx>) {
            // Creating nodes does memory allocation, which may trigger GC,
            // so the nodes need to be rooted while they are being added.
            let roots = JSRoots::new();
            let node1 = cx.manage(NativeNode { data: 1, edges: vec![] }).root(&roots);
            let node2 = cx.manage(NativeNode { data: 2, edges: vec![] }).root(&roots);
            graph.get_mut(cx).nodes.push(node1.contract_lifetime());
            graph.get_mut(cx).nodes.push(node2.contract_lifetime());
        }
        fn add_edges<Cx: JSContext>(&self, cx: &mut JSSnapshot<Cx>, graph: Graph<Cx>) {
            // Note that there's no rooting here.
            let node1 = graph.get(cx).nodes[0].extend_lifetime(cx);
            let node2 = graph.get(cx).nodes[1].extend_lifetime(cx);
            node1.get_mut(cx).edges.push(node2.contract_lifetime());
            node2.get_mut(cx).edges.push(node1.contract_lifetime());
        }
    }
    #[allow(dead_code)]
    // Test that we can contract the lifetimes of nodes and graphs.
    fn contract_graph<'a, 'b:'a, Cx: 'b>(graph: Graph<'b, Cx>) -> Graph<'a, Cx> {
        graph.contract_lifetime()
    }
    with_js_context(Test);
}

#[test]
fn test_covariant() {
    #[allow(dead_code)]
    fn cast<'a, 'b:'a, Cx, T>(managed: JSManaged<'b, Cx, T>)
                              -> JSManaged<'a, Cx, T>
    {
        managed
    }
}
<commit_msg>Made roots a method in JSContext.<commit_after>//! An outline of how linear types could be combined with JS-managed data
//!
//! The idea is to use the JS context as a linear token.
//! Each JS context gets its own type `Cx` which implements the `JSContext` trait.
//! Access to read-only JS-managed data requires a context of type `&Cx`.
//! Access to read-write JS-managed data requires a context of type `&mut Cx`.
//! Linear use of the context ensures that Rust's memory safety is preserved (er, I hope).
//!
//! This API doesn't address tracing, which would have to be handled similarly
//! to servo's current JS bindings.
//!
//! One thing that makes this easier is that we can arrange for any
//! calls into the JS engine which might trigger GC to take a `&mut Cx`
//! argument, which gives us control of when GC might occur.

use std::marker::PhantomData;

/// The trait for accessing JS-managed data.
pub trait JSAccess<Cx>: Sized {
    /// Read-only access to native JS-managed data.
    fn get<'a, 'b:'a, T>(&'a self, managed: JSManaged<'b, Cx, T>) -> &'a T::ChangeLifetime where
        T: JSManageable<'a>;

    /// Read-write access to native JS-managed data.
    fn get_mut<'a, 'b:'a, T>(&'a mut self, managed: JSManaged<'b, Cx, T>) -> &'a mut T::ChangeLifetime where
        T: JSManageable<'a>;
}

/// The trait for JS contexts.
pub trait JSContext: JSAccess<Self> {
    /// Get a snapshot of the JS state.
    /// The snapshot only allows access to the methods that are guaranteed not to call GC,
    /// so we don't need to root JS-managed pointers during the lifetime of a snapshot.
    fn snapshot(&mut self) -> JSSnapshot<Self>;

    /// Add a new root set to the context.
    fn roots(&mut self) -> JSRoots<Self>;

    /// Give ownership of data to JS.
    /// This allocates JS heap, which may trigger GC.
    fn manage<'a, T>(&'a self, value: T) -> JSManaged<'a, Self, T>
        where T: 'a + JSManageable<'a, ChangeLifetime=T>;

    // A real implementation would also have JS methods such as those in jsapi.
}

/// A placholder for the real `JSTraceable`.
pub unsafe trait JSTraceable {}

/// The trait for native JS-manageable data.
pub unsafe trait JSManageable<'a>: JSTraceable {
    /// This type should have the same mnemory represention as `Self`.
    /// The only difference between `Self` and `Self::ChangeLifetime`
    /// is that any `JSManaged<'b, Cx, T>` should be replaced by
    /// `JSManaged<'a, Cx, T::ChangeLifetime>`.
    type ChangeLifetime: 'a;
}

/// A user of a JS context implements `JSContextConsumer`, which is called back
/// with a fresh JS context.
pub trait JSContextConsumer<T> {
    /// This callback is called with a fresh JS context.
    fn consume<Cx>(self, cx: &mut Cx) -> T where Cx: JSContext;
}

/// To create a fresh JS context, the user implements `JSContextConsumer`
/// for their type `K`, builds a `k:K`, then calls `with_js_context(k)`.
/// ```rust
///   struct MyConsumer;
///   impl JSContextConsumer<()> for MyConsumer {
///      fn consume<Cx: JSContext>(cx: Cx) {
///         // Do stuff with the JS context cx.
///      }
///   }
///   with_js_context(MyConsumer);
/// ```
pub fn with_js_context<C, T>(consumer: C) -> T where
    C: JSContextConsumer<T>
{
    // A real implementation would allocate a JS context
    let mut cx = JSContextImpl {};
    consumer.consume(&mut cx)
}

/// The type of JS-managed data in a JS context `Cx`, with lifetime `'a`.
///
/// If the user has access to a `JSManaged`, then the JS-managed
/// data is live for the given lifetime.
pub struct JSManaged<'a, Cx, T> {
    // JS reflector goes here
    raw: *mut T,
    marker: PhantomData<(&'a(),Cx)>,
}

impl<'a, Cx, T> Clone for JSManaged<'a, Cx, T> {
    fn clone(&self) -> Self {
        JSManaged {
            raw: self.raw,
            marker: self.marker,
        }
    }
}

impl<'a, Cx, T> Copy for JSManaged<'a, Cx, T> {
}

unsafe impl<'a, Cx, T> JSTraceable for JSManaged<'a, Cx, T> where
    T: JSTraceable
{
}

unsafe impl<'a, 'b, Cx, T> JSManageable<'b> for JSManaged<'a, Cx, T> where
    Cx: 'b,
    T: JSManageable<'b>,
{
    type ChangeLifetime = JSManaged<'b, Cx, T::ChangeLifetime>;
}

impl<'a, Cx, T> JSManaged<'a, Cx, T> {
    /// Convenience method for `access.get(self)`.
    pub fn get<'b, Access: JSAccess<Cx>>(self, access: &'b Access) -> &'b T::ChangeLifetime where
        T: JSManageable<'b>,
        'a: 'b,
    {
        access.get(self)
    }

    /// Convenience method for `access.get_mut(self)`.
    pub fn get_mut<'b, Access: JSAccess<Cx>>(self, access: &'b mut Access) -> &'b mut T::ChangeLifetime where
        T: JSManageable<'b>,
        'a: 'b,
    {
        access.get_mut(self)
    }

    /// Change the lifetime of JS-managed data.
    pub unsafe fn change_lifetime<'b>(self) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
    {
        JSManaged {
            raw: self.raw as *mut T::ChangeLifetime,
            marker: PhantomData,
        }
    }

    /// It's safe to contract the lifetime of JS-managed data.
    pub fn contract_lifetime<'b>(self) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
        'a: 'b,
    {
        unsafe { self.change_lifetime() }
    }

    /// It's safe to extend the lifetime of JS-managed data if it has been snapshotted.
    pub fn extend_lifetime<'b>(self, _: &JSSnapshot<'b, Cx>) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
        'b: 'a,
    {
        unsafe { self.change_lifetime() }
    }

    /// It's safe to extend the lifetime of JS-managed data by rooting it.
    pub fn root<'b>(self, _: &'b JSRoots<Cx>) -> JSManaged<'b, Cx, T::ChangeLifetime> where
        T: JSManageable<'b>,
        'b: 'a,
    {
        // The real thing would add the reflector to the root set.
        unsafe { self.change_lifetime() }
    }
}

/// A root set.
pub struct JSRoots<Cx> {
    // The real thing would contain a set of rooted JS objects.
    marker: PhantomData<Cx>,
}

impl<Cx> Drop for JSRoots<Cx> {
    fn drop(&mut self) {
        // The real thing would unroot the root set.
    }
}

/// A snapshot of a JS context.
///
/// The idea here is that during the lifetime of a JSSnapshot<Cx>, the JS state
/// doesn't change, and in particular GC doesn't happen. This allows us to avoid
/// some rooting.
pub struct JSSnapshot<'c, Cx: 'c>(&'c mut Cx);

impl<'c, Cx: 'c> JSSnapshot<'c, Cx> where
    Cx: JSContext
{
    /// Build a new snapshot
    pub fn new(cx: &mut Cx) -> JSSnapshot<Cx> {
        JSSnapshot(cx)
    }
}

impl<'c, Cx: 'c> JSAccess<Cx> for JSSnapshot<'c, Cx> where
    Cx: JSContext
{
    /// Read-only access to native JS-managed data.
    fn get<'a, 'b:'a, T>(&'a self, managed: JSManaged<'b, Cx, T>) -> &'a T::ChangeLifetime where
        T: JSManageable<'a>
    {
        self.0.get(managed)
    }

    /// Read-write access to native JS-managed data.
    fn get_mut<'a, 'b:'a, T>(&'a mut self, managed: JSManaged<'b, Cx, T>) -> &'a mut T::ChangeLifetime where
        T: JSManageable<'a>
    {
        self.0.get_mut(managed)
    }
}

// It is important for safety that this implemention is not made public!
struct JSContextImpl {
    // JS context implementation goes here
}

impl JSAccess<JSContextImpl> for JSContextImpl {
    // The claim is that this is safe, since it takes a `&self`.
    fn get<'a, 'b:'a, T>(&'a self, managed: JSManaged<'b, Self, T>) -> &'a T::ChangeLifetime where
        T: JSManageable<'a>
    {
        unsafe { &*(managed.raw as *mut T::ChangeLifetime) }
    }

    // The claim is that this is safe, since it takes a `&mut self`.
    fn get_mut<'a, 'b:'a, T>(&'a mut self, managed: JSManaged<'b, Self, T>) -> &'a mut T::ChangeLifetime where
        T: JSManageable<'a>
    {
        unsafe { &mut *(managed.raw as *mut T::ChangeLifetime) }
    }
}

impl JSContext for JSContextImpl {
    fn snapshot(&mut self) -> JSSnapshot<Self> {
        JSSnapshot(self)
    }

    fn roots(&mut self) -> JSRoots<Self> {
        JSRoots { marker: PhantomData }
    }

    // This outline implementation just space-leaks all data,
    // the real thing would create a reflector, and add a finalizer hook.
    fn manage<'a, T>(&'a self, value: T) -> JSManaged<'a, Self, T>
        where T: 'a + JSManageable<'a, ChangeLifetime=T>
    {
        JSManaged {
            raw: Box::into_raw(Box::new(value)),
            marker: PhantomData,
        }
    }
}

#[test]
// This test constructs a two-node cyclic graph, which is the smallest
// example of something that uses `RefCell`s in servo's JS bindings.
fn test() {
    // A graph type
    type Graph<'a, Cx> = JSManaged<'a, Cx, NativeGraph<'a, Cx>>;
    struct NativeGraph<'a, Cx> {
        nodes: Vec<Node<'a, Cx>>,
    }
    unsafe impl<'a, Cx> JSTraceable for NativeGraph<'a, Cx> {}
    unsafe impl<'a, 'b, Cx: 'b> JSManageable<'b> for NativeGraph<'a, Cx> { type ChangeLifetime = NativeGraph<'b, Cx>; }
    // A node type
    type Node<'a, Cx> = JSManaged<'a, Cx, NativeNode<'a, Cx>>;
    struct NativeNode<'a, Cx> {
        data: usize,
        edges: Vec<Node<'a, Cx>>,
    }
    unsafe impl<'a, Cx> JSTraceable for NativeNode<'a, Cx> {}
    unsafe impl<'a, 'b, Cx: 'b> JSManageable<'b> for NativeNode<'a, Cx> { type ChangeLifetime = NativeNode<'b, Cx>; }
    // Build a cyclic graph
    struct Test;
    impl JSContextConsumer<()> for Test {
        fn consume<Cx>(self, cx: &mut Cx) where Cx: JSContext {
            let roots = cx.roots();
            let graph = cx.manage(NativeGraph { nodes: vec![] }).root(&roots);
            self.add_nodes(cx, graph);
            assert_eq!(graph.get(cx).nodes[0].get(cx).data, 1);
            assert_eq!(graph.get(cx).nodes[1].get(cx).data, 2);
            self.add_edges(&mut cx.snapshot(), graph);
            assert_eq!(graph.get(cx).nodes[0].get(cx).edges[0].get(cx).data, 2);
            assert_eq!(graph.get(cx).nodes[1].get(cx).edges[0].get(cx).data, 1);
        }
    }
    impl Test {
        fn add_nodes<'a, 'b, Cx: JSContext>(&'a self, cx: &'a mut Cx, graph: Graph<'b, Cx>) {
            // Creating nodes does memory allocation, which may trigger GC,
            // so the nodes need to be rooted while they are being added.
            let roots = cx.roots();
            let node1 = cx.manage(NativeNode { data: 1, edges: vec![] }).root(&roots);
            let node2 = cx.manage(NativeNode { data: 2, edges: vec![] }).root(&roots);
            graph.get_mut(cx).nodes.push(node1.contract_lifetime());
            graph.get_mut(cx).nodes.push(node2.contract_lifetime());
        }
        fn add_edges<Cx: JSContext>(&self, cx: &mut JSSnapshot<Cx>, graph: Graph<Cx>) {
            // Note that there's no rooting here.
            let node1 = graph.get(cx).nodes[0].extend_lifetime(cx);
            let node2 = graph.get(cx).nodes[1].extend_lifetime(cx);
            node1.get_mut(cx).edges.push(node2.contract_lifetime());
            node2.get_mut(cx).edges.push(node1.contract_lifetime());
        }
    }
    #[allow(dead_code)]
    // Test that we can contract the lifetimes of nodes and graphs.
    fn contract_graph<'a, 'b:'a, Cx: 'b>(graph: Graph<'b, Cx>) -> Graph<'a, Cx> {
        graph.contract_lifetime()
    }
    with_js_context(Test);
}

#[test]
fn test_covariant() {
    #[allow(dead_code)]
    fn cast<'a, 'b:'a, Cx, T>(managed: JSManaged<'b, Cx, T>)
                              -> JSManaged<'a, Cx, T>
    {
        managed
    }
}
<|endoftext|>


<commit_before>//! Represent colors in HSL and convert between HSL and RGB.
//!
//! # Examples
//!
//! ```rust
//! use hsl::HSL;
//!
//! let yellow = [255, 255, 0];
//! let yellow_hsl = HSL::from_rgb(&yellow);
//!
//! assert_eq!(yellow_hsl, HSL { h: 60_f64, s: 1_f64, l: 0.5_f64 });
//! ```

#[cfg(test)] extern crate quickcheck;

/// Color represented in HSL
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
pub struct HSL {
    /// Hue in 0-360 degree
    pub h: f64,
    /// Saturation in 0...1 (percent)
    pub s: f64,
    /// Luminosity in 0...1 (percent)
    pub l: f64,
}

impl HSL {
    /// Convert RGB pixel value to HSL
    ///
    /// Expects RGB pixel to be a slice of three `u8`s representing the red, green and blue values.
    ///
    /// ```rust
    /// use hsl::HSL;
    /// let blue = HSL::from_rgb(&[0, 0, 255]);
    /// ```
    ///
    /// Algorithm from [go-color] by Brandon Thomson <bt@brandonthomson.com>. (Iternally converts
    /// the pixel to RGB before converting it to HSL.)
    ///
    /// [go-color]: https://github.com/bthomson/go-color
    #[cfg_attr(feature = "dev", allow(float_cmp))]
    pub fn from_rgb(rgb: &[u8]) -> HSL {
        use std::cmp::{max, min};

        let mut h: f64;
        let s: f64;
        let l: f64;

        let (r, g, b) = (rgb[0], rgb[1], rgb[2]);

        let max = max(max(r, g), b);
        let min = min(min(r, g), b);

        // Normalized RGB: Divide everything by 255 to get percentages of colors.
        let (r, g, b) = (r as f64 / 255_f64,
                         g as f64 / 255_f64,
                         b as f64 / 255_f64);
        let (min, max) = (min as f64 / 255_f64, max as f64 / 255_f64);

        // Luminosity is the average of the max and min rgb color intensities.
        l = (max + min) / 2_f64;

        // Saturation
        let delta: f64 = max - min;
        if delta == 0_f64 {
    		// it's gray
            return HSL { h: 0_f64, s: 0_f64, l: l };
        }

        // it's not gray
        if l < 0.5_f64 {
            s = delta / (max + min);
        } else {
            s = delta / (2_f64 - max - min);
        }

        // Hue
        let r2 = (((max - r) / 6_f64) + (delta / 2_f64)) / delta;
        let g2 = (((max - g) / 6_f64) + (delta / 2_f64)) / delta;
        let b2 = (((max - b) / 6_f64) + (delta / 2_f64)) / delta;

        h = match max {
            x if x == r => b2 - g2,
            x if x == g => (1_f64 / 3_f64) + r2 - b2,
            _ => (2_f64 / 3_f64) + g2 - r2,
        };

        // Fix wraparounds
        if h < 0 as f64 {
            h += 1_f64;
        } else if h > 1 as f64 {
            h -= 1_f64;
        }

        // Hue is precise to milli-degrees, e.g. `74.52deg`.
        let h_degrees = (h * 360_f64 * 100_f64).round() / 100_f64;

        HSL { h: h_degrees, s: s, l: l }
    }

    /// Convert HSL color to RGB
    ///
    /// ```rust
    /// use hsl::HSL;
    ///
    /// let cyan = HSL { h: 180_f64, s: 1_f64, l: 0.5_f64 };
    /// assert_eq!(cyan.to_rgb(), (0, 255, 255));
    /// ```
    pub fn to_rgb(&self) -> (u8, u8, u8) {
        if self.s == 0.0 {
            // Achromatic, i.e., grey.
            let l = percent_to_byte(self.l);
            return (l, l, l);
        }

        let h = self.h / 360.0; // treat this as 0..1 instead of degrees
        let s = self.s;
        let l = self.l;

        let q = if l < 0.5 {
            l * (1.0 + s)
        } else {
            l + s - (l * s)
        };
        let p = 2.0 * l - q;

        (percent_to_byte(hue_to_rgb(p, q, h + 1.0 / 3.0)),
         percent_to_byte(hue_to_rgb(p, q, h)),
         percent_to_byte(hue_to_rgb(p, q, h - 1.0 / 3.0)))
    }
}

fn percent_to_byte(percent: f64) -> u8 {
    (percent * 255.0).round() as u8
}

/// Convert Hue to RGB Ratio
///
/// From <https://github.com/jariz/vibrant.js/> by Jari Zwarts
fn hue_to_rgb(p: f64, q: f64, t: f64) -> f64 {
    // Normalize
    let t = if t < 0.0 {
        t + 1.0
    } else if t > 1.0 {
        t - 1.0
    } else {
        t
    };

    if t < 1.0 / 6.0 {
        p + (q - p) * 6.0 * t
    } else if t < 1.0 / 2.0 {
        q
    } else if t < 2.0 / 3.0 {
        p + (q - p) * (2.0 / 3.0 - t) * 6.0
    } else {
        p
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::{Arbitrary, Gen, quickcheck};
    use super::*;

    impl Arbitrary for HSL {
        fn arbitrary<G: Gen>(g: &mut G) -> HSL {
            HSL {
                h: g.gen_range(0_f64, 360_f64),
                s: g.gen_range(0_f64, 1_f64),
                l: g.gen_range(0_f64, 1_f64),
            }
        }
    }

    fn sloppy_compare(a: &HSL, b: &HSL) -> bool {
        const EPSILON: f64 = 0.05;

        let res = (a.s == 0_f64 || (a.h - b.h).abs() < (EPSILON * 360_f64)) &&
            (a.s - b.s).abs() < EPSILON &&
            (a.l - b.l).abs() < EPSILON;

        if !res {
            println!("in: {:?}, out: {:?}", a, b);
        }

        res
    }

    fn idemponent(x: HSL) -> bool {
        let (r, g, b) = x.to_rgb();
        sloppy_compare(&x, &HSL::from_rgb(&[r, g, b]))
    }

    #[test]
    fn quickcheck_rgb_and_back() {
        quickcheck(idemponent as fn(HSL) -> bool);
    }
}
<commit_msg>Quickcheck the Other Way Around: RGB -> HSL -> RGB<commit_after>//! Represent colors in HSL and convert between HSL and RGB.
//!
//! # Examples
//!
//! ```rust
//! use hsl::HSL;
//!
//! let yellow = [255, 255, 0];
//! let yellow_hsl = HSL::from_rgb(&yellow);
//!
//! assert_eq!(yellow_hsl, HSL { h: 60_f64, s: 1_f64, l: 0.5_f64 });
//! ```

#[cfg(test)] extern crate quickcheck;

/// Color represented in HSL
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
pub struct HSL {
    /// Hue in 0-360 degree
    pub h: f64,
    /// Saturation in 0...1 (percent)
    pub s: f64,
    /// Luminosity in 0...1 (percent)
    pub l: f64,
}

impl HSL {
    /// Convert RGB pixel value to HSL
    ///
    /// Expects RGB pixel to be a slice of three `u8`s representing the red, green and blue values.
    ///
    /// ```rust
    /// use hsl::HSL;
    /// let blue = HSL::from_rgb(&[0, 0, 255]);
    /// ```
    ///
    /// Algorithm from [go-color] by Brandon Thomson <bt@brandonthomson.com>. (Iternally converts
    /// the pixel to RGB before converting it to HSL.)
    ///
    /// [go-color]: https://github.com/bthomson/go-color
    #[cfg_attr(feature = "dev", allow(float_cmp))]
    pub fn from_rgb(rgb: &[u8]) -> HSL {
        use std::cmp::{max, min};

        let mut h: f64;
        let s: f64;
        let l: f64;

        let (r, g, b) = (rgb[0], rgb[1], rgb[2]);

        let max = max(max(r, g), b);
        let min = min(min(r, g), b);

        // Normalized RGB: Divide everything by 255 to get percentages of colors.
        let (r, g, b) = (r as f64 / 255_f64,
                         g as f64 / 255_f64,
                         b as f64 / 255_f64);
        let (min, max) = (min as f64 / 255_f64, max as f64 / 255_f64);

        // Luminosity is the average of the max and min rgb color intensities.
        l = (max + min) / 2_f64;

        // Saturation
        let delta: f64 = max - min;
        if delta == 0_f64 {
    		// it's gray
            return HSL { h: 0_f64, s: 0_f64, l: l };
        }

        // it's not gray
        if l < 0.5_f64 {
            s = delta / (max + min);
        } else {
            s = delta / (2_f64 - max - min);
        }

        // Hue
        let r2 = (((max - r) / 6_f64) + (delta / 2_f64)) / delta;
        let g2 = (((max - g) / 6_f64) + (delta / 2_f64)) / delta;
        let b2 = (((max - b) / 6_f64) + (delta / 2_f64)) / delta;

        h = match max {
            x if x == r => b2 - g2,
            x if x == g => (1_f64 / 3_f64) + r2 - b2,
            _ => (2_f64 / 3_f64) + g2 - r2,
        };

        // Fix wraparounds
        if h < 0 as f64 {
            h += 1_f64;
        } else if h > 1 as f64 {
            h -= 1_f64;
        }

        // Hue is precise to milli-degrees, e.g. `74.52deg`.
        let h_degrees = (h * 360_f64 * 100_f64).round() / 100_f64;

        HSL { h: h_degrees, s: s, l: l }
    }

    /// Convert HSL color to RGB
    ///
    /// ```rust
    /// use hsl::HSL;
    ///
    /// let cyan = HSL { h: 180_f64, s: 1_f64, l: 0.5_f64 };
    /// assert_eq!(cyan.to_rgb(), (0, 255, 255));
    /// ```
    pub fn to_rgb(&self) -> (u8, u8, u8) {
        if self.s == 0.0 {
            // Achromatic, i.e., grey.
            let l = percent_to_byte(self.l);
            return (l, l, l);
        }

        let h = self.h / 360.0; // treat this as 0..1 instead of degrees
        let s = self.s;
        let l = self.l;

        let q = if l < 0.5 {
            l * (1.0 + s)
        } else {
            l + s - (l * s)
        };
        let p = 2.0 * l - q;

        (percent_to_byte(hue_to_rgb(p, q, h + 1.0 / 3.0)),
         percent_to_byte(hue_to_rgb(p, q, h)),
         percent_to_byte(hue_to_rgb(p, q, h - 1.0 / 3.0)))
    }
}

fn percent_to_byte(percent: f64) -> u8 {
    (percent * 255.0).round() as u8
}

/// Convert Hue to RGB Ratio
///
/// From <https://github.com/jariz/vibrant.js/> by Jari Zwarts
fn hue_to_rgb(p: f64, q: f64, t: f64) -> f64 {
    // Normalize
    let t = if t < 0.0 {
        t + 1.0
    } else if t > 1.0 {
        t - 1.0
    } else {
        t
    };

    if t < 1.0 / 6.0 {
        p + (q - p) * 6.0 * t
    } else if t < 1.0 / 2.0 {
        q
    } else if t < 2.0 / 3.0 {
        p + (q - p) * (2.0 / 3.0 - t) * 6.0
    } else {
        p
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::{Arbitrary, Gen, quickcheck};
    use super::*;

    #[derive(Clone, Debug, Hash, PartialEq)]
    struct RGB {
        r: u8, g: u8, b: u8,
    }

    impl Arbitrary for RGB {
        fn arbitrary<G: Gen>(g: &mut G) -> RGB {
            RGB {
                r: g.gen(),
                g: g.gen(),
                b: g.gen(),
            }
        }
    }

    fn sloppy_rgb_compare(a: RGB, b: RGB) -> bool {
        const EPSILON: i32 = 0;
        let res = (a.r as i32 - b.r as i32 <= EPSILON) &&
                  (a.g as i32 - b.g as i32 <= EPSILON) &&
                  (a.b as i32 - b.b as i32 <= EPSILON);

        if !res {
            println!("in: {:?}, out: {:?}", a, b);
        }

        res
    }

    fn idemponent(input: RGB) -> bool {
        let RGB { r, g, b } = input;
        let (r_out, g_out, b_out) = HSL::from_rgb(&[r, g, b]).to_rgb();
        sloppy_rgb_compare(input, RGB { r: r_out, g: g_out, b: b_out })
    }

    #[test]
    fn quickcheck_rgb_to_hsl_and_back() {
        quickcheck(idemponent as fn(RGB) -> bool);
    }
}
<|endoftext|>


<commit_before>//! Implementations of various isosurface algorithms.
//!
//! See `examples/` for sample code.

mod isoline;
pub use isoline::marching_triangles;
pub use isoline::marching_triangles_with_data_emit;
pub use isoline::Isoline;

mod isosurface;
pub use isosurface::marching_tetrahedra;
pub use isosurface::marching_tetrahedra_with_data;
pub use isosurface::marching_tetrahedra_with_data_emit;

mod interpolate;
<commit_msg>Update lib info<commit_after>//! Implementations of isoline and isosurface algorithms based on the marching tetrahedra
//! algorithm.
//!
//! A. Doi and A. Koide, _An Efficient Method of Triangulating Equi-Valued Surfaces by Using
//! Tetrahedral Cells_, IEICE TRANSACTIONS on Information and Systems **E74-D** (1991), no. 1,
//! 214--224.
//!
//! See `examples/` for sample code.

mod isoline;
pub use isoline::marching_triangles;
pub use isoline::marching_triangles_with_data_emit;
pub use isoline::Isoline;

mod isosurface;
pub use isosurface::marching_tetrahedra;
pub use isosurface::marching_tetrahedra_with_data;
pub use isosurface::marching_tetrahedra_with_data_emit;

mod interpolate;
<|endoftext|>