<commit_before>//! Types of the Telegram API.
//!
//! This module contains definitions of the types defined
//! [here](https://core.telegram.org/bots/api/#available-types). Many Telegram
//! types, like "Location", map directly to Rust-structs. Other Telegram types,
//! like "Message", was made more rusty by using enums.
//!
//! All types implement `Decodable` and `Encodable`, so they can be serialized
//! as JSON. Non existing JSON-fields will result in `None` values for `Option`
//! types. `None` values don't result in JSON fields.
//!

use rustc_serialize::{Decodable, Encodable, Decoder, Encoder};

// ===========================================================================
// Helpers
// ===========================================================================
// Macro to implement "Encodable" quickly. "None" fields won't be encoded.
macro_rules! impl_encode {
    (
        $ty:ident, $count:expr,
        [$($id:expr => $field:ident),*],
        [$($o_id:expr => $o_field:ident),*]
    ) => {
        impl Encodable for $ty {
            fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
                e.emit_struct(stringify!($ty), $count, |e| {
                    $(
                        try!(e.emit_struct_field(stringify!($field), $id, |e| {
                            self.$field.encode(e)
                        }));
                    )*
                    $
(                        if let Some(ref v) = self.$o_field {
                            try!(e.emit_struct_field(
                                stringify!($o_field), $o_id, |e| {
                                v.encode(e)
                            }));
                        }
                    )*

                    Ok(())
                })
            }
        }
    }
}

// ===========================================================================
// Telegram primitive types
// ===========================================================================
/// The Telegram "Integer": Currently i32.
pub type Integer = i32;
/// The Telegram "Float": Currently f32.
pub type Float = f32;

#[derive(RustcDecodable, Debug)]
pub struct Response<T: Decodable> {
    pub ok: bool,
    pub description: Option<String>,
    pub result: Option<T>,
}

#[derive(RustcDecodable, Debug)]
pub struct GroupChat {
    pub id: Integer,
    pub title: String,
}

#[derive(Debug)]
pub enum Chat {
    User(User),
    Group(GroupChat),
}

impl Chat {
    pub fn id(&self) -> Integer {
        match self {
            &Chat::User(ref u) => u.id,
            &Chat::Group(ref g) => g.id,
        }
    }
}

impl Decodable for Chat {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match User::decode(d) {
            Ok(user) => Ok(Chat::User(user)),
            Err(_) => Ok(Chat::Group(try!(Decodable::decode(d)))),
        }
    }
}

#[derive(RustcDecodable, Debug)]
pub struct Update {
    pub update_id: Integer,
    pub message: Option<Message>
}

#[derive(Debug)]
pub struct Message {
    pub message_id: Integer,
    pub from: User,
    pub chat: Chat,
    pub date: Integer,

    // forward_from and forward_date in one
    pub forward: Option<(User, Integer)>,
    pub reply: Option<Box<Message>>,

    pub msg: MessageType,
}

impl Decodable for Message {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        // Decodes a field with a given name. If successful: Return decoded
        // value. If not: Exist function with error value.
        macro_rules! try_field {
            ($d:ident, $name:expr) => {
                try!($d.read_struct_field($name, 0, |d| Decodable::decode(d)))
            }
        }

        d.read_struct("", 0, |d| {
            Ok(Message {
                message_id: try_field!(d, "message_id"),
                from:       try_field!(d, "from"),
                chat:       try_field!(d, "chat"),
                date:       try_field!(d, "date"),
                forward:    try_field!(d, "forward"),
                reply:      try_field!(d, "reply"),
                msg: try!(MessageType::decode(d)),
            })
        })
    }
}

#[derive(Debug)]
pub enum MessageType {
    Text(String),
    Audio,
    File,
    Photo,
    Sticker,
    Video,
    Contact(Contact),
    Location(Location),
    NewChatParticipant(User),
    LeftChatParticipant(User),
    NewChatTitle(String),
    NewChatPhoto,
    DeleteChatPhoto,
    GroupChatCreated,
}

impl Decodable for MessageType {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        // Tries to decode a field with the given name. If the field does NOT
        // exist: Does nothing. If the field does exist: Return the decoded
        // value. If any other decoder error occured: Return the error.
        macro_rules! maybe_field {
            ($d:ident, $name:expr, $variant:ident) => {{
                if let Some(val) = try!($d.read_struct_field(
                    $name, 0, |d| Decodable::decode(d))) {
                    return Ok(MessageType::$variant(val));
                };
            }}
        }

        // There is always just one of these fields used, so we can infer the
        // enum variant from it.
        maybe_field!(d, "text", Text);
        maybe_field!(d, "contact", Contact);
        maybe_field!(d, "location", Location);
        maybe_field!(d, "new_chat_participant", NewChatParticipant);
        maybe_field!(d, "left_chat_participant", LeftChatParticipant);
        maybe_field!(d, "new_chat_title", NewChatTitle);

        // delete_chat_photo
        if let Some(true) = try!(d.read_struct_field(
            "delete_chat_photo", 0, |d| Decodable::decode(d))) {
            return Ok(MessageType::DeleteChatPhoto);
        };
        // group_chat_created
        if let Some(true) = try!(d.read_struct_field(
            "group_chat_created", 0, |d| Decodable::decode(d))) {
            return Ok(MessageType::GroupChatCreated);
        };

        // TODO: Implement missing MessageType's. If no field is set: Error.
        Ok(MessageType::Text("!!! Not implemented !!!".into()))
    }
}

// ===========================================================================
// Telegram types directly mapped to Rust types
// ===========================================================================

/// Telegram type "ReplyKeyboardMarkup" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct ReplyKeyboardMarkup {
    pub keyboard: Vec<Vec<String>>,
    pub resize_keyboard: Option<bool>,
    pub one_time_keyboard: Option<bool>,
    pub selective: Option<bool>,
}

impl Default for ReplyKeyboardMarkup {
    fn default() -> Self {
        ReplyKeyboardMarkup {
            keyboard: Vec::new(),
            resize_keyboard: None,
            one_time_keyboard: None,
            selective: None,
        }
    }
}

impl_encode!(ReplyKeyboardMarkup, 4,
    [0 => keyboard],
    [1 => resize_keyboard, 2 => one_time_keyboard, 3 => selective]);

// ---------------------------------------------------------------------------
/// Telegram type "PhotoSize" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct PhotoSize {
    pub file_id: String,
    pub width: Integer,
    pub height: Integer,
    pub file_size: Option<Integer>,
}

impl_encode!(PhotoSize, 4,
    [0 => file_id, 1 => width, 2 => height],
    [3 => file_size]);

// ---------------------------------------------------------------------------
/// Telegram type "Contact" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Contact {
    pub phone_number: String,
    pub first_name: String,
    pub last_name: Option<String>,
    pub user_id: Option<String>,
}

impl_encode!(Contact, 4,
    [0 => phone_number, 1 => first_name],
    [2 => last_name, 3 => user_id]);

// ---------------------------------------------------------------------------
/// Telegram type "Location" (directly mapped)
#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq)]
pub struct Location {
    pub longitude: Float,
    pub latitude: Float,
}

// ---------------------------------------------------------------------------
/// Telegram type "User" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct User {
    pub id: Integer,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: Option<String>,
}

impl_encode!(User, 4,
    [0 => id, 1 => first_name],
    [2 => last_name, 3 => username]);

// ===========================================================================
// Unit tests (mainly encode & decode)
// ===========================================================================
mod test;
<commit_msg>Add a bunch of directly mapped Telegram types<commit_after>//! Types of the Telegram API.
//!
//! This module contains definitions of the types defined
//! [here](https://core.telegram.org/bots/api/#available-types). Many Telegram
//! types, like "Location", map directly to Rust-structs. Other Telegram types,
//! like "Message", was made more rusty by using enums.
//!
//! All types implement `Decodable` and `Encodable`, so they can be serialized
//! as JSON. Non existing JSON-fields will result in `None` values for `Option`
//! types. `None` values don't result in JSON fields.
//!

use rustc_serialize::{Decodable, Encodable, Decoder, Encoder};

// ===========================================================================
// Helpers
// ===========================================================================
// Macro to implement "Encodable" quickly. "None" fields won't be encoded.
macro_rules! impl_encode {
    (
        $ty:ident, $count:expr,
        [$($id:expr => $field:ident),*],
        [$($o_id:expr => $o_field:ident),*]
    ) => {
        impl Encodable for $ty {
            fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
                e.emit_struct(stringify!($ty), $count, |e| {
                    $(
                        try!(e.emit_struct_field(stringify!($field), $id, |e| {
                            self.$field.encode(e)
                        }));
                    )*
                    $(
                        if let Some(ref v) = self.$o_field {
                            try!(e.emit_struct_field(
                                stringify!($o_field), $o_id, |e| {
                                v.encode(e)
                            }));
                        }
                    )*

                    Ok(())
                })
            }
        }
    }
}

// ===========================================================================
// Telegram primitive types
// ===========================================================================
/// The Telegram "Integer": Currently i32.
pub type Integer = i32;
/// The Telegram "Float": Currently f32.
pub type Float = f32;

#[derive(RustcDecodable, Debug)]
pub struct Response<T: Decodable> {
    pub ok: bool,
    pub description: Option<String>,
    pub result: Option<T>,
}

#[derive(RustcDecodable, Debug)]
pub struct GroupChat {
    pub id: Integer,
    pub title: String,
}

#[derive(Debug)]
pub enum Chat {
    User(User),
    Group(GroupChat),
}

impl Chat {
    pub fn id(&self) -> Integer {
        match self {
            &Chat::User(ref u) => u.id,
            &Chat::Group(ref g) => g.id,
        }
    }
}

impl Decodable for Chat {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match User::decode(d) {
            Ok(user) => Ok(Chat::User(user)),
            Err(_) => Ok(Chat::Group(try!(Decodable::decode(d)))),
        }
    }
}

#[derive(RustcDecodable, Debug)]
pub struct Update {
    pub update_id: Integer,
    pub message: Option<Message>
}

#[derive(Debug)]
pub struct Message {
    pub message_id: Integer,
    pub from: User,
    pub chat: Chat,
    pub date: Integer,

    // forward_from and forward_date in one
    pub forward: Option<(User, Integer)>,
    pub reply: Option<Box<Message>>,

    pub msg: MessageType,
}

impl Decodable for Message {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        // Decodes a field with a given name. If successful: Return decoded
        // value. If not: Exist function with error value.
        macro_rules! try_field {
            ($d:ident, $name:expr) => {
                try!($d.read_struct_field($name, 0, |d| Decodable::decode(d)))
            }
        }

        d.read_struct("", 0, |d| {
            Ok(Message {
                message_id: try_field!(d, "message_id"),
                from:       try_field!(d, "from"),
                chat:       try_field!(d, "chat"),
                date:       try_field!(d, "date"),
                forward:    try_field!(d, "forward"),
                reply:      try_field!(d, "reply"),
                msg: try!(MessageType::decode(d)),
            })
        })
    }
}

#[derive(Debug)]
pub enum MessageType {
    Text(String),
    Audio,
    File,
    Photo,
    Sticker,
    Video,
    Contact(Contact),
    Location(Location),
    NewChatParticipant(User),
    LeftChatParticipant(User),
    NewChatTitle(String),
    NewChatPhoto,
    DeleteChatPhoto,
    GroupChatCreated,
}

impl Decodable for MessageType {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        // Tries to decode a field with the given name. If the field does NOT
        // exist: Does nothing. If the field does exist: Return the decoded
        // value. If any other decoder error occured: Return the error.
        macro_rules! maybe_field {
            ($d:ident, $name:expr, $variant:ident) => {{
                if let Some(val) = try!($d.read_struct_field(
                    $name, 0, |d| Decodable::decode(d))) {
                    return Ok(MessageType::$variant(val));
                };
            }}
        }

        // There is always just one of these fields used, so we can infer the
        // enum variant from it.
        maybe_field!(d, "text", Text);
        maybe_field!(d, "contact", Contact);
        maybe_field!(d, "location", Location);
        maybe_field!(d, "new_chat_participant", NewChatParticipant);
        maybe_field!(d, "left_chat_participant", LeftChatParticipant);
        maybe_field!(d, "new_chat_title", NewChatTitle);

        // delete_chat_photo
        if let Some(true) = try!(d.read_struct_field(
            "delete_chat_photo", 0, |d| Decodable::decode(d))) {
            return Ok(MessageType::DeleteChatPhoto);
        };
        // group_chat_created
        if let Some(true) = try!(d.read_struct_field(
            "group_chat_created", 0, |d| Decodable::decode(d))) {
            return Ok(MessageType::GroupChatCreated);
        };

        // TODO: Implement missing MessageType's. If no field is set: Error.
        Ok(MessageType::Text("!!! Not implemented !!!".into()))
    }
}

// ===========================================================================
// Telegram types directly mapped to Rust types
// ===========================================================================
/// Telegram type "User" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct User {
    pub id: Integer,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: Option<String>,
}

impl_encode!(User, 4,
    [0 => id, 1 => first_name],
    [2 => last_name, 3 => username]);

// ---------------------------------------------------------------------------
/// Telegram type "PhotoSize" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct PhotoSize {
    pub file_id: String,
    pub width: Integer,
    pub height: Integer,
    pub file_size: Option<Integer>,
}

impl_encode!(PhotoSize, 4,
    [0 => file_id, 1 => width, 2 => height],
    [3 => file_size]);

// ---------------------------------------------------------------------------
/// Telegram type "PhotoSize" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Audio {
    pub file_id: String,
    pub duration: Integer,
    pub mime_type: Option<String>,
    pub file_size: Option<Integer>,
}

impl_encode!(Audio, 4,
    [0 => file_id, 1 => duration],
    [2 => mime_type, 3 => file_size]);

// ---------------------------------------------------------------------------
/// Telegram type "PhotoSize" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Document {
    pub file_id: String,
    pub thumb: PhotoSize,
    pub file_name: Option<String>,
    pub mime_type: Option<String>,
    pub file_size: Option<Integer>,
}

impl_encode!(Document, 5,
    [0 => file_id, 1 => thumb],
    [2 => file_name, 3 => mime_type, 4 => file_size]);

// ---------------------------------------------------------------------------
/// Telegram type "PhotoSize" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Sticker {
    pub file_id: String,
    pub width: Integer,
    pub height: Integer,
    pub thumb: PhotoSize,
    pub file_size: Option<Integer>,
}

impl_encode!(Sticker, 5,
    [0 => file_id, 1 => width, 2 => height, 3 => thumb],
    [4 => file_size]);

// ---------------------------------------------------------------------------
/// Telegram type "PhotoSize" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Video {
    pub file_id: String,
    pub width: Integer,
    pub height: Integer,
    pub duration: Integer,
    pub thumb: PhotoSize,
    pub mime_type: Option<String>,
    pub file_size: Option<Integer>,
    pub caption: Option<String>,
}

impl_encode!(Video, 8,
    [0 => file_id, 1 => width, 2 => height, 3 => duration, 4 => thumb],
    [5 => mime_type, 6 => file_size, 7 => caption]);

// ---------------------------------------------------------------------------
/// Telegram type "Contact" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Contact {
    pub phone_number: String,
    pub first_name: String,
    pub last_name: Option<String>,
    pub user_id: Option<String>,
}

impl_encode!(Contact, 4,
    [0 => phone_number, 1 => first_name],
    [2 => last_name, 3 => user_id]);

// ---------------------------------------------------------------------------
/// Telegram type "Location" (directly mapped)
#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq)]
pub struct Location {
    pub longitude: Float,
    pub latitude: Float,
}

// ---------------------------------------------------------------------------
/// Telegram type "Location" (directly mapped)
#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq)]
pub struct UserProfilePhotos {
    pub total_count: Integer,
    pub photos: Vec<Vec<PhotoSize>>,
}

// ---------------------------------------------------------------------------
/// Telegram type "ReplyKeyboardMarkup" (directly mapped)
#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct ReplyKeyboardMarkup {
    pub keyboard: Vec<Vec<String>>,
    pub resize_keyboard: Option<bool>,
    pub one_time_keyboard: Option<bool>,
    pub selective: Option<bool>,
}

impl Default for ReplyKeyboardMarkup {
    fn default() -> Self {
        ReplyKeyboardMarkup {
            keyboard: Vec::new(),
            resize_keyboard: None,
            one_time_keyboard: None,
            selective: None,
        }
    }
}

impl_encode!(ReplyKeyboardMarkup, 4,
    [0 => keyboard],
    [1 => resize_keyboard, 2 => one_time_keyboard, 3 => selective]);

// ===========================================================================
// Unit tests (mainly encode & decode)
// ===========================================================================
mod test;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Counting Bloom filter tuned for use as an ancestor filter for selector
//! matching.

use fnv::FnvHasher;
use std::hash::{Hash, Hasher};

// The top 8 bits of the 32-bit hash value are not used by the bloom filter.
// Consumers may rely on this to pack hashes more efficiently.
pub const BLOOM_HASH_MASK: u32 = 0x00ffffff;
const KEY_SIZE: usize = 12;

const ARRAY_SIZE: usize = 1 << KEY_SIZE;
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;

/// A counting Bloom filter with 8-bit counters.
pub type BloomFilter = CountingBloomFilter<BloomStorageU8>;

/// A counting Bloom filter with parameterized storage to handle
/// counters of different sizes.  For now we assume that having two hash
/// functions is enough, but we may revisit that decision later.
///
/// The filter uses an array with 2**KeySize entries.
///
/// Assuming a well-distributed hash function, a Bloom filter with
/// array size M containing N elements and
/// using k hash function has expected false positive rate exactly
///
/// $  (1 - (1 - 1/M)^{kN})^k  $
///
/// because each array slot has a
///
/// $  (1 - 1/M)^{kN}  $
///
/// chance of being 0, and the expected false positive rate is the
/// probability that all of the k hash functions will hit a nonzero
/// slot.
///
/// For reasonable assumptions (M large, kN large, which should both
/// hold if we're worried about false positives) about M and kN this
/// becomes approximately
///
/// $$  (1 - \exp(-kN/M))^k   $$
///
/// For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
/// or in other words
///
/// $$    N/M = -0.5 * \ln(1 - \sqrt(r))   $$
///
/// where r is the false positive rate.  This can be used to compute
/// the desired KeySize for a given load N and false positive rate r.
///
/// If N/M is assumed small, then the false positive rate can
/// further be approximated as 4*N^2/M^2.  So increasing KeySize by
/// 1, which doubles M, reduces the false positive rate by about a
/// factor of 4, and a false positive rate of 1% corresponds to
/// about M/N == 20.
///
/// What this means in practice is that for a few hundred keys using a
/// KeySize of 12 gives false positive rates on the order of 0.25-4%.
///
/// Similarly, using a KeySize of 10 would lead to a 4% false
/// positive rate for N == 100 and to quite bad false positive
/// rates for larger N.
#[derive(Clone)]
pub struct CountingBloomFilter<S> where S: BloomStorage {
    storage: S,
}

impl<S> CountingBloomFilter<S> where S: BloomStorage {
    /// Creates a new bloom filter.
    #[inline]
    pub fn new() -> Self {
        CountingBloomFilter {
            storage: Default::default(),
        }
    }

    #[inline]
    pub fn clear(&mut self) {
        self.storage = Default::default();
    }

    // Slow linear accessor to make sure the bloom filter is zeroed. This should
    // never be used in release builds.
    #[cfg(debug_assertions)]
    pub fn is_zeroed(&self) -> bool {
        self.storage.is_zeroed()
    }

    #[cfg(not(debug_assertions))]
    pub fn is_zeroed(&self) -> bool {
        unreachable!()
    }

    #[inline]
    pub fn insert_hash(&mut self, hash: u32) {
        self.storage.adjust_first_slot(hash, true);
        self.storage.adjust_second_slot(hash, true);
    }

    /// Inserts an item into the bloom filter.
    #[inline]
    pub fn insert<T: Hash>(&mut self, elem: &T) {
        self.insert_hash(hash(elem))
    }

    #[inline]
    pub fn remove_hash(&mut self, hash: u32) {
        self.storage.adjust_first_slot(hash, false);
        self.storage.adjust_second_slot(hash, false);
    }

    /// Removes an item from the bloom filter.
    #[inline]
    pub fn remove<T: Hash>(&mut self, elem: &T) {
        self.remove_hash(hash(elem))
    }

    #[inline]
    pub fn might_contain_hash(&self, hash: u32) -> bool {
        !self.storage.first_slot_is_empty(hash) &&
            !self.storage.second_slot_is_empty(hash)
    }

    /// Check whether the filter might contain an item.  This can
    /// sometimes return true even if the item is not in the filter,
    /// but will never return false for items that are actually in the
    /// filter.
    #[inline]
    pub fn might_contain<T: Hash>(&self, elem: &T) -> bool {
        self.might_contain_hash(hash(elem))
    }
}

pub trait BloomStorage : Clone + Default {
    fn slot_is_empty(&self, index: usize) -> bool;
    fn adjust_slot(&mut self, index: usize, increment: bool);
    fn is_zeroed(&self) -> bool;

    #[inline]
    fn first_slot_is_empty(&self, hash: u32) -> bool {
        self.slot_is_empty(Self::first_slot_index(hash))
    }

    #[inline]
    fn second_slot_is_empty(&self, hash: u32) -> bool {
        self.slot_is_empty(Self::second_slot_index(hash))
    }

    #[inline]
    fn adjust_first_slot(&mut self, hash: u32, increment: bool) {
        self.adjust_slot(Self::first_slot_index(hash), increment)
    }

    #[inline]
    fn adjust_second_slot(&mut self, hash: u32, increment: bool) {
        self.adjust_slot(Self::second_slot_index(hash), increment)
    }

    #[inline]
    fn first_slot_index(hash: u32) -> usize {
        hash1(hash) as usize
    }

    #[inline]
    fn second_slot_index(hash: u32) -> usize {
        hash2(hash) as usize
    }
}

/// Storage class for a CountingBloomFilter that has 8-bit counters.
pub struct BloomStorageU8 {
    counters: [u8; ARRAY_SIZE],
}

impl BloomStorage for BloomStorageU8 {
    #[inline]
    fn adjust_slot(&mut self, index: usize, increment: bool) {
        let slot = &mut self.counters[index];
        if *slot != 0xff {  // full
            if increment {
                *slot += 1;
            } else {
                *slot -= 1;
            }
        }
    }

    #[inline]
    fn slot_is_empty(&self, index: usize) -> bool {
        self.counters[index] == 0
    }

    #[inline]
    fn is_zeroed(&self) -> bool {
        self.counters.iter().all(|x| *x == 0)
    }
}

impl Default for BloomStorageU8 {
    fn default() -> Self {
        BloomStorageU8 {
            counters: [0; ARRAY_SIZE],
        }
    }
}

impl Clone for BloomStorageU8 {
    fn clone(&self) -> Self {
        BloomStorageU8 {
            counters: self.counters,
        }
    }
}

fn hash<T: Hash>(elem: &T) -> u32 {
    let mut hasher = FnvHasher::default();
    elem.hash(&mut hasher);
    let hash: u64 = hasher.finish();
    (hash >> 32) as u32 ^ (hash as u32)
}

#[inline]
fn hash1(hash: u32) -> u32 {
    hash & KEY_MASK
}

#[inline]
fn hash2(hash: u32) -> u32 {
    (hash >> KEY_SIZE) & KEY_MASK
}

#[test]
fn create_and_insert_some_stuff() {
    let mut bf = BloomFilter::new();

    for i in 0_usize .. 1000 {
        bf.insert(&i);
    }

    for i in 0_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives =
        (1001_usize .. 2000).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 150, "{} is not < 150", false_positives); // 15%.

    for i in 0_usize .. 100 {
        bf.remove(&i);
    }

    for i in 100_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives = (0_usize .. 100).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 20, "{} is not < 20", false_positives); // 20%.

    bf.clear();

    for i in 0_usize .. 2000 {
        assert!(!bf.might_contain(&i));
    }
}

#[cfg(feature = "unstable")]
#[cfg(test)]
mod bench {
    extern crate test;
    use super::BloomFilter;

    #[derive(Default)]
    struct HashGenerator(u32);

    impl HashGenerator {
        fn next(&mut self) -> u32 {
            // Each hash is split into two twelve-bit segments, which are used
            // as an index into an array. We increment each by 64 so that we
            // hit the next cache line, and then another 1 so that our wrapping
            // behavior leads us to different entries each time.
            //
            // Trying to simulate cold caches is rather difficult with the cargo
            // benchmarking setup, so it may all be moot depending on the number
            // of iterations that end up being run. But we might as well.
            self.0 += (65) + (65 << super::KEY_SIZE);
            self.0
        }
    }

    #[bench]
    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
        b.iter(|| {
            let mut gen1 = HashGenerator::default();
            let mut gen2 = HashGenerator::default();
            let mut bf = BloomFilter::new();
            for _ in 0_usize .. 1000 {
                bf.insert_hash(gen1.next());
            }
            for _ in 0_usize .. 100 {
                bf.remove_hash(gen2.next());
            }
            for _ in 100_usize .. 200 {
                test::black_box(bf.might_contain_hash(gen2.next()));
            }
        });
    }

    #[bench]
    fn might_contain_10(b: &mut test::Bencher) {
        let bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.might_contain_hash(gen.next()));
        });
    }

    #[bench]
    fn clear(b: &mut test::Bencher) {
        let mut bf = Box::new(BloomFilter::new());
        b.iter(|| test::black_box(&mut bf).clear());
    }

    #[bench]
    fn insert_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.insert_hash(gen.next()));
        });
    }

    #[bench]
    fn remove_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        // Note: this will underflow, and that's ok.
        b.iter(|| for _ in 0..10 {
            bf.remove_hash(gen.next())
        });
    }
}
<commit_msg>selectors: Add a non-counting Bloom filter type.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Counting and non-counting Bloom filters tuned for use as ancestor filters
//! for selector matching.

use fnv::FnvHasher;
use std::hash::{Hash, Hasher};

// The top 8 bits of the 32-bit hash value are not used by the bloom filter.
// Consumers may rely on this to pack hashes more efficiently.
pub const BLOOM_HASH_MASK: u32 = 0x00ffffff;
const KEY_SIZE: usize = 12;

const ARRAY_SIZE: usize = 1 << KEY_SIZE;
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;

/// A counting Bloom filter with 8-bit counters.
pub type BloomFilter = CountingBloomFilter<BloomStorageU8>;

/// A non-counting Bloom filter.
///
/// Effectively a counting Bloom filter with 1-bit counters.
pub type NonCountingBloomFilter = CountingBloomFilter<BloomStorageBool>;

/// A counting Bloom filter with parameterized storage to handle
/// counters of different sizes.  For now we assume that having two hash
/// functions is enough, but we may revisit that decision later.
///
/// The filter uses an array with 2**KeySize entries.
///
/// Assuming a well-distributed hash function, a Bloom filter with
/// array size M containing N elements and
/// using k hash function has expected false positive rate exactly
///
/// $  (1 - (1 - 1/M)^{kN})^k  $
///
/// because each array slot has a
///
/// $  (1 - 1/M)^{kN}  $
///
/// chance of being 0, and the expected false positive rate is the
/// probability that all of the k hash functions will hit a nonzero
/// slot.
///
/// For reasonable assumptions (M large, kN large, which should both
/// hold if we're worried about false positives) about M and kN this
/// becomes approximately
///
/// $$  (1 - \exp(-kN/M))^k   $$
///
/// For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
/// or in other words
///
/// $$    N/M = -0.5 * \ln(1 - \sqrt(r))   $$
///
/// where r is the false positive rate.  This can be used to compute
/// the desired KeySize for a given load N and false positive rate r.
///
/// If N/M is assumed small, then the false positive rate can
/// further be approximated as 4*N^2/M^2.  So increasing KeySize by
/// 1, which doubles M, reduces the false positive rate by about a
/// factor of 4, and a false positive rate of 1% corresponds to
/// about M/N == 20.
///
/// What this means in practice is that for a few hundred keys using a
/// KeySize of 12 gives false positive rates on the order of 0.25-4%.
///
/// Similarly, using a KeySize of 10 would lead to a 4% false
/// positive rate for N == 100 and to quite bad false positive
/// rates for larger N.
#[derive(Clone)]
pub struct CountingBloomFilter<S> where S: BloomStorage {
    storage: S,
}

impl<S> CountingBloomFilter<S> where S: BloomStorage {
    /// Creates a new bloom filter.
    #[inline]
    pub fn new() -> Self {
        CountingBloomFilter {
            storage: Default::default(),
        }
    }

    #[inline]
    pub fn clear(&mut self) {
        self.storage = Default::default();
    }

    // Slow linear accessor to make sure the bloom filter is zeroed. This should
    // never be used in release builds.
    #[cfg(debug_assertions)]
    pub fn is_zeroed(&self) -> bool {
        self.storage.is_zeroed()
    }

    #[cfg(not(debug_assertions))]
    pub fn is_zeroed(&self) -> bool {
        unreachable!()
    }

    #[inline]
    pub fn insert_hash(&mut self, hash: u32) {
        self.storage.adjust_first_slot(hash, true);
        self.storage.adjust_second_slot(hash, true);
    }

    /// Inserts an item into the bloom filter.
    #[inline]
    pub fn insert<T: Hash>(&mut self, elem: &T) {
        self.insert_hash(hash(elem))
    }

    #[inline]
    pub fn remove_hash(&mut self, hash: u32) {
        self.storage.adjust_first_slot(hash, false);
        self.storage.adjust_second_slot(hash, false);
    }

    /// Removes an item from the bloom filter.
    #[inline]
    pub fn remove<T: Hash>(&mut self, elem: &T) {
        self.remove_hash(hash(elem))
    }

    #[inline]
    pub fn might_contain_hash(&self, hash: u32) -> bool {
        !self.storage.first_slot_is_empty(hash) &&
            !self.storage.second_slot_is_empty(hash)
    }

    /// Check whether the filter might contain an item.  This can
    /// sometimes return true even if the item is not in the filter,
    /// but will never return false for items that are actually in the
    /// filter.
    #[inline]
    pub fn might_contain<T: Hash>(&self, elem: &T) -> bool {
        self.might_contain_hash(hash(elem))
    }
}

pub trait BloomStorage : Clone + Default {
    fn slot_is_empty(&self, index: usize) -> bool;
    fn adjust_slot(&mut self, index: usize, increment: bool);
    fn is_zeroed(&self) -> bool;

    #[inline]
    fn first_slot_is_empty(&self, hash: u32) -> bool {
        self.slot_is_empty(Self::first_slot_index(hash))
    }

    #[inline]
    fn second_slot_is_empty(&self, hash: u32) -> bool {
        self.slot_is_empty(Self::second_slot_index(hash))
    }

    #[inline]
    fn adjust_first_slot(&mut self, hash: u32, increment: bool) {
        self.adjust_slot(Self::first_slot_index(hash), increment)
    }

    #[inline]
    fn adjust_second_slot(&mut self, hash: u32, increment: bool) {
        self.adjust_slot(Self::second_slot_index(hash), increment)
    }

    #[inline]
    fn first_slot_index(hash: u32) -> usize {
        hash1(hash) as usize
    }

    #[inline]
    fn second_slot_index(hash: u32) -> usize {
        hash2(hash) as usize
    }
}

/// Storage class for a CountingBloomFilter that has 8-bit counters.
pub struct BloomStorageU8 {
    counters: [u8; ARRAY_SIZE],
}

impl BloomStorage for BloomStorageU8 {
    #[inline]
    fn adjust_slot(&mut self, index: usize, increment: bool) {
        let slot = &mut self.counters[index];
        if *slot != 0xff {  // full
            if increment {
                *slot += 1;
            } else {
                *slot -= 1;
            }
        }
    }

    #[inline]
    fn slot_is_empty(&self, index: usize) -> bool {
        self.counters[index] == 0
    }

    #[inline]
    fn is_zeroed(&self) -> bool {
        self.counters.iter().all(|x| *x == 0)
    }
}

impl Default for BloomStorageU8 {
    fn default() -> Self {
        BloomStorageU8 {
            counters: [0; ARRAY_SIZE],
        }
    }
}

impl Clone for BloomStorageU8 {
    fn clone(&self) -> Self {
        BloomStorageU8 {
            counters: self.counters,
        }
    }
}

/// Storage class for a CountingBloomFilter that has 1-bit counters.
pub struct BloomStorageBool {
    counters: [u8; ARRAY_SIZE / 8],
}

impl BloomStorage for BloomStorageBool {
    #[inline]
    fn adjust_slot(&mut self, index: usize, increment: bool) {
        let bit = 1 << (index % 8);
        let byte = &mut self.counters[index / 8];

        // Since we have only one bit for storage, decrementing it
        // should never do anything.  Assert against an accidental
        // decrementing of a bit that was never set.
        assert!(increment || (*byte & bit) != 0,
                "should not decrement if slot is already false");

        if increment {
            *byte |= bit;
        }
    }

    #[inline]
    fn slot_is_empty(&self, index: usize) -> bool {
        let bit = 1 << (index % 8);
        (self.counters[index / 8] & bit) == 0
    }

    #[inline]
    fn is_zeroed(&self) -> bool {
        self.counters.iter().all(|x| *x == 0)
    }
}

impl Default for BloomStorageBool {
    fn default() -> Self {
        BloomStorageBool {
            counters: [0; ARRAY_SIZE / 8],
        }
    }
}

impl Clone for BloomStorageBool {
    fn clone(&self) -> Self {
        BloomStorageBool {
            counters: self.counters,
        }
    }
}

fn hash<T: Hash>(elem: &T) -> u32 {
    let mut hasher = FnvHasher::default();
    elem.hash(&mut hasher);
    let hash: u64 = hasher.finish();
    (hash >> 32) as u32 ^ (hash as u32)
}

#[inline]
fn hash1(hash: u32) -> u32 {
    hash & KEY_MASK
}

#[inline]
fn hash2(hash: u32) -> u32 {
    (hash >> KEY_SIZE) & KEY_MASK
}

#[test]
fn create_and_insert_some_stuff() {
    use std::mem::transmute;

    let mut bf = BloomFilter::new();

    // Statically assert that ARRAY_SIZE is a multiple of 8, which
    // BloomStorageBool relies on.
    unsafe {
        transmute::<[u8; ARRAY_SIZE % 8], [u8; 0]>([]);
    }

    for i in 0_usize .. 1000 {
        bf.insert(&i);
    }

    for i in 0_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives =
        (1001_usize .. 2000).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 150, "{} is not < 150", false_positives); // 15%.

    for i in 0_usize .. 100 {
        bf.remove(&i);
    }

    for i in 100_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives = (0_usize .. 100).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 20, "{} is not < 20", false_positives); // 20%.

    bf.clear();

    for i in 0_usize .. 2000 {
        assert!(!bf.might_contain(&i));
    }
}

#[cfg(feature = "unstable")]
#[cfg(test)]
mod bench {
    extern crate test;
    use super::BloomFilter;

    #[derive(Default)]
    struct HashGenerator(u32);

    impl HashGenerator {
        fn next(&mut self) -> u32 {
            // Each hash is split into two twelve-bit segments, which are used
            // as an index into an array. We increment each by 64 so that we
            // hit the next cache line, and then another 1 so that our wrapping
            // behavior leads us to different entries each time.
            //
            // Trying to simulate cold caches is rather difficult with the cargo
            // benchmarking setup, so it may all be moot depending on the number
            // of iterations that end up being run. But we might as well.
            self.0 += (65) + (65 << super::KEY_SIZE);
            self.0
        }
    }

    #[bench]
    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
        b.iter(|| {
            let mut gen1 = HashGenerator::default();
            let mut gen2 = HashGenerator::default();
            let mut bf = BloomFilter::new();
            for _ in 0_usize .. 1000 {
                bf.insert_hash(gen1.next());
            }
            for _ in 0_usize .. 100 {
                bf.remove_hash(gen2.next());
            }
            for _ in 100_usize .. 200 {
                test::black_box(bf.might_contain_hash(gen2.next()));
            }
        });
    }

    #[bench]
    fn might_contain_10(b: &mut test::Bencher) {
        let bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.might_contain_hash(gen.next()));
        });
    }

    #[bench]
    fn clear(b: &mut test::Bencher) {
        let mut bf = Box::new(BloomFilter::new());
        b.iter(|| test::black_box(&mut bf).clear());
    }

    #[bench]
    fn insert_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.insert_hash(gen.next()));
        });
    }

    #[bench]
    fn remove_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        // Note: this will underflow, and that's ok.
        b.iter(|| for _ in 0..10 {
            bf.remove_hash(gen.next())
        });
    }
}
<|endoftext|>


<commit_before>//! The types modules describes all the structures to express FITS files.

use std::str::FromStr;

/// A keyword record contains information about a FITS header.
pub struct KeywordRecord {
    /// The keyword of this record.
    keyword: Keyword,
}

#[derive(Debug, PartialEq)]
/// A unit tuple that will act as a placeholder for blank records.
pub struct BlankRecord;

#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types, missing_docs)]
/// The various keywords that can be found in headers.
pub enum Keyword {
    SIMPLE,
    BITPIX,
    NAXIS,
    NAXISn(u8),
    EXTEND,
    NEXTEND,
    EXTVER,
    ORIGIN,
    DATE,
    CREATOR,
    PROCVER,
    FILEVER,
    TIMVERSN,
    TELESCOP,
    INSTRUME,
    OBJECT,
    KEPLERID,
    CHANNEL,
    MODULE,
    OUTPUT,
    CAMPAIGN,
    DATA_REL,
    OBSMODE,
    MISSION,
    TTABLEID,
    RADESYS,
    RA_OBJ,
    DEC_OBJ,
    EQUINOX,
    PMRA,
    PMDEC,
    PMTOTAL,
    PARALLAX,
    GLON,
    GLAT,
    GMAG,
    RMAG,
    IMAG,
    ZMAG,
    JMAG,
    HMAG,
    KMAG,
    KEPMAG,
    GRCOLOR,
    JKCOLOR,
    GKCOLOR,
    TEFF,
    LOGG,
    FEH,
    EBMINUSV,
    AV,
    RADIUS,
    TMINDEX,
    CHECKSUM,
    DATASUM,
    END,
}

/// Problems that could occur when parsing a `str` are enumerated here.
#[derive(Debug)]
pub enum ParseKeywordError {
    /// When a str can not be recognized as a keyword, this error will be returned
    UnknownKeyword
}

impl FromStr for Keyword {
    type Err = ParseKeywordError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO parse NAXIS1 correctly
        match s.trim_right() {
            "SIMPLE" => Ok(Keyword::SIMPLE),
            "BITPIX" => Ok(Keyword::BITPIX),
            "NAXIS" => Ok(Keyword::NAXIS),
            "EXTEND" => Ok(Keyword::EXTEND),
            "NEXTEND" => Ok(Keyword::NEXTEND),
            "EXTVER" => Ok(Keyword::EXTVER),
            "ORIGIN" => Ok(Keyword::ORIGIN),
            "DATE" => Ok(Keyword::DATE),
            "CREATOR" => Ok(Keyword::CREATOR),
            "PROCVER" => Ok(Keyword::PROCVER),
            "FILEVER" => Ok(Keyword::FILEVER),
            "TIMVERSN" => Ok(Keyword::TIMVERSN),
            "TELESCOP" => Ok(Keyword::TELESCOP),
            "INSTRUME" => Ok(Keyword::INSTRUME),
            "OBJECT" => Ok(Keyword::OBJECT),
            "KEPLERID" => Ok(Keyword::KEPLERID),
            "CHANNEL" => Ok(Keyword::CHANNEL),
            "MODULE" => Ok(Keyword::MODULE),
            "OUTPUT" => Ok(Keyword::OUTPUT),
            "CAMPAIGN" => Ok(Keyword::CAMPAIGN),
            "DATA_REL" => Ok(Keyword::DATA_REL),
            "OBSMODE" => Ok(Keyword::OBSMODE),
            "MISSION" => Ok(Keyword::MISSION),
            "TTABLEID" => Ok(Keyword::TTABLEID),
            "RADESYS" => Ok(Keyword::RADESYS),
            "RA_OBJ" => Ok(Keyword::RA_OBJ),
            "DEC_OBJ" => Ok(Keyword::DEC_OBJ),
            "EQUINOX" => Ok(Keyword::EQUINOX),
            "PMRA" => Ok(Keyword::PMRA),
            "PMDEC" => Ok(Keyword::PMDEC),
            "PMTOTAL" => Ok(Keyword::PMTOTAL),
            "PARALLAX" => Ok(Keyword::PARALLAX),
            "GLON" => Ok(Keyword::GLON),
            "GLAT" => Ok(Keyword::GLAT),
            "GMAG" => Ok(Keyword::GMAG),
            "RMAG" => Ok(Keyword::RMAG),
            "IMAG" => Ok(Keyword::IMAG),
            "ZMAG" => Ok(Keyword::ZMAG),
            "JMAG" => Ok(Keyword::JMAG),
            "HMAG" => Ok(Keyword::HMAG),
            "KMAG" => Ok(Keyword::KMAG),
            "KEPMAG" => Ok(Keyword::KEPMAG),
            "GRCOLOR" => Ok(Keyword::GRCOLOR),
            "JKCOLOR" => Ok(Keyword::JKCOLOR),
            "GKCOLOR" => Ok(Keyword::GKCOLOR),
            "TEFF" => Ok(Keyword::TEFF),
            "LOGG" => Ok(Keyword::LOGG),
            "FEH" => Ok(Keyword::FEH),
            "EBMINUSV" => Ok(Keyword::EBMINUSV),
            "AV" => Ok(Keyword::AV),
            "RADIUS" => Ok(Keyword::RADIUS),
            "TMINDEX" => Ok(Keyword::TMINDEX),
            "CHECKSUM" => Ok(Keyword::CHECKSUM),
            "DATASUM" => Ok(Keyword::DATASUM),
            "END" => Ok(Keyword::END),
            _ => Err(ParseKeywordError::UnknownKeyword)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;
    use super::Keyword;

    #[test]
    fn keywords_could_be_constructed_from_str() {
        let data = vec!(
            ("SIMPLE", Keyword::SIMPLE),
            ("BITPIX", Keyword::BITPIX),
            ("NAXIS", Keyword::NAXIS),
            ("EXTEND", Keyword::EXTEND),
            ("NEXTEND", Keyword::NEXTEND),
            ("EXTVER", Keyword::EXTVER),
            ("ORIGIN", Keyword::ORIGIN),
            ("DATE", Keyword::DATE),
            ("CREATOR", Keyword::CREATOR),
            ("PROCVER", Keyword::PROCVER),
            ("FILEVER", Keyword::FILEVER),
            ("TIMVERSN", Keyword::TIMVERSN),
            ("TELESCOP", Keyword::TELESCOP),
            ("INSTRUME", Keyword::INSTRUME),
            ("OBJECT", Keyword::OBJECT),
            ("KEPLERID", Keyword::KEPLERID),
            ("CHANNEL", Keyword::CHANNEL),
            ("MODULE", Keyword::MODULE),
            ("OUTPUT", Keyword::OUTPUT),
            ("CAMPAIGN", Keyword::CAMPAIGN),
            ("DATA_REL", Keyword::DATA_REL),
            ("OBSMODE", Keyword::OBSMODE),
            ("MISSION", Keyword::MISSION),
            ("TTABLEID", Keyword::TTABLEID),
            ("RADESYS", Keyword::RADESYS),
            ("RA_OBJ", Keyword::RA_OBJ),
            ("DEC_OBJ", Keyword::DEC_OBJ),
            ("EQUINOX", Keyword::EQUINOX),
            ("PMRA", Keyword::PMRA),
            ("PMDEC", Keyword::PMDEC),
            ("PMTOTAL", Keyword::PMTOTAL),
            ("PARALLAX", Keyword::PARALLAX),
            ("GLON", Keyword::GLON),
            ("GLAT", Keyword::GLAT),
            ("GMAG", Keyword::GMAG),
            ("RMAG", Keyword::RMAG),
            ("IMAG", Keyword::IMAG),
            ("ZMAG", Keyword::ZMAG),
            ("JMAG", Keyword::JMAG),
            ("HMAG", Keyword::HMAG),
            ("KMAG", Keyword::KMAG),
            ("KEPMAG", Keyword::KEPMAG),
            ("GRCOLOR", Keyword::GRCOLOR),
            ("JKCOLOR", Keyword::JKCOLOR),
            ("GKCOLOR", Keyword::GKCOLOR),
            ("TEFF", Keyword::TEFF),
            ("LOGG", Keyword::LOGG),
            ("FEH", Keyword::FEH),
            ("EBMINUSV", Keyword::EBMINUSV),
            ("AV", Keyword::AV),
            ("RADIUS", Keyword::RADIUS),
            ("TMINDEX", Keyword::TMINDEX),
            ("CHECKSUM", Keyword::CHECKSUM),
            ("DATASUM", Keyword::DATASUM),
            ("END", Keyword::END),
        );

        for (input, expected) in data {
            assert_eq!(Keyword::from_str(input).unwrap(), expected);
        }
    }

    #[test]
    fn should_also_parse_whitespace_keywords() {
        assert_eq!(Keyword::from_str("SIMPLE  ").unwrap(), Keyword::SIMPLE);
    }
}
<commit_msg>Create a KeywordRecord<commit_after>//! The types modules describes all the structures to express FITS files.

use std::str::FromStr;

#[derive(Debug, PartialEq)]
/// A keyword record contains information about a FITS header.
pub struct KeywordRecord {
    /// The keyword of this record.
    keyword: Keyword,
}

impl KeywordRecord {
    /// Create a `KeywordRecord` from a specific `Keyword`.
    pub fn create(keyword: Keyword) -> KeywordRecord {
        KeywordRecord { keyword : keyword }
    }
}

#[derive(Debug, PartialEq)]
/// A unit tuple that will act as a placeholder for blank records.
pub struct BlankRecord;

#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types, missing_docs)]
/// The various keywords that can be found in headers.
pub enum Keyword {
    SIMPLE,
    BITPIX,
    NAXIS,
    NAXISn(u8),
    EXTEND,
    NEXTEND,
    EXTVER,
    ORIGIN,
    DATE,
    CREATOR,
    PROCVER,
    FILEVER,
    TIMVERSN,
    TELESCOP,
    INSTRUME,
    OBJECT,
    KEPLERID,
    CHANNEL,
    MODULE,
    OUTPUT,
    CAMPAIGN,
    DATA_REL,
    OBSMODE,
    MISSION,
    TTABLEID,
    RADESYS,
    RA_OBJ,
    DEC_OBJ,
    EQUINOX,
    PMRA,
    PMDEC,
    PMTOTAL,
    PARALLAX,
    GLON,
    GLAT,
    GMAG,
    RMAG,
    IMAG,
    ZMAG,
    JMAG,
    HMAG,
    KMAG,
    KEPMAG,
    GRCOLOR,
    JKCOLOR,
    GKCOLOR,
    TEFF,
    LOGG,
    FEH,
    EBMINUSV,
    AV,
    RADIUS,
    TMINDEX,
    CHECKSUM,
    DATASUM,
    END,
}

/// Problems that could occur when parsing a `str` are enumerated here.
#[derive(Debug)]
pub enum ParseKeywordError {
    /// When a str can not be recognized as a keyword, this error will be returned
    UnknownKeyword
}

impl FromStr for Keyword {
    type Err = ParseKeywordError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO parse NAXIS1 correctly
        match s.trim_right() {
            "SIMPLE" => Ok(Keyword::SIMPLE),
            "BITPIX" => Ok(Keyword::BITPIX),
            "NAXIS" => Ok(Keyword::NAXIS),
            "EXTEND" => Ok(Keyword::EXTEND),
            "NEXTEND" => Ok(Keyword::NEXTEND),
            "EXTVER" => Ok(Keyword::EXTVER),
            "ORIGIN" => Ok(Keyword::ORIGIN),
            "DATE" => Ok(Keyword::DATE),
            "CREATOR" => Ok(Keyword::CREATOR),
            "PROCVER" => Ok(Keyword::PROCVER),
            "FILEVER" => Ok(Keyword::FILEVER),
            "TIMVERSN" => Ok(Keyword::TIMVERSN),
            "TELESCOP" => Ok(Keyword::TELESCOP),
            "INSTRUME" => Ok(Keyword::INSTRUME),
            "OBJECT" => Ok(Keyword::OBJECT),
            "KEPLERID" => Ok(Keyword::KEPLERID),
            "CHANNEL" => Ok(Keyword::CHANNEL),
            "MODULE" => Ok(Keyword::MODULE),
            "OUTPUT" => Ok(Keyword::OUTPUT),
            "CAMPAIGN" => Ok(Keyword::CAMPAIGN),
            "DATA_REL" => Ok(Keyword::DATA_REL),
            "OBSMODE" => Ok(Keyword::OBSMODE),
            "MISSION" => Ok(Keyword::MISSION),
            "TTABLEID" => Ok(Keyword::TTABLEID),
            "RADESYS" => Ok(Keyword::RADESYS),
            "RA_OBJ" => Ok(Keyword::RA_OBJ),
            "DEC_OBJ" => Ok(Keyword::DEC_OBJ),
            "EQUINOX" => Ok(Keyword::EQUINOX),
            "PMRA" => Ok(Keyword::PMRA),
            "PMDEC" => Ok(Keyword::PMDEC),
            "PMTOTAL" => Ok(Keyword::PMTOTAL),
            "PARALLAX" => Ok(Keyword::PARALLAX),
            "GLON" => Ok(Keyword::GLON),
            "GLAT" => Ok(Keyword::GLAT),
            "GMAG" => Ok(Keyword::GMAG),
            "RMAG" => Ok(Keyword::RMAG),
            "IMAG" => Ok(Keyword::IMAG),
            "ZMAG" => Ok(Keyword::ZMAG),
            "JMAG" => Ok(Keyword::JMAG),
            "HMAG" => Ok(Keyword::HMAG),
            "KMAG" => Ok(Keyword::KMAG),
            "KEPMAG" => Ok(Keyword::KEPMAG),
            "GRCOLOR" => Ok(Keyword::GRCOLOR),
            "JKCOLOR" => Ok(Keyword::JKCOLOR),
            "GKCOLOR" => Ok(Keyword::GKCOLOR),
            "TEFF" => Ok(Keyword::TEFF),
            "LOGG" => Ok(Keyword::LOGG),
            "FEH" => Ok(Keyword::FEH),
            "EBMINUSV" => Ok(Keyword::EBMINUSV),
            "AV" => Ok(Keyword::AV),
            "RADIUS" => Ok(Keyword::RADIUS),
            "TMINDEX" => Ok(Keyword::TMINDEX),
            "CHECKSUM" => Ok(Keyword::CHECKSUM),
            "DATASUM" => Ok(Keyword::DATASUM),
            "END" => Ok(Keyword::END),
            _ => Err(ParseKeywordError::UnknownKeyword)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;
    use super::{KeywordRecord, Keyword};

    #[test]
    fn keyword_record_constructed_from_the_create_function_should_eq_hand_construction() {
        assert_eq!(
            KeywordRecord { keyword: Keyword::ORIGIN },
            KeywordRecord::create(Keyword::ORIGIN));
    }

    #[test]
    fn keywords_could_be_constructed_from_str() {
        let data = vec!(
            ("SIMPLE", Keyword::SIMPLE),
            ("BITPIX", Keyword::BITPIX),
            ("NAXIS", Keyword::NAXIS),
            ("EXTEND", Keyword::EXTEND),
            ("NEXTEND", Keyword::NEXTEND),
            ("EXTVER", Keyword::EXTVER),
            ("ORIGIN", Keyword::ORIGIN),
            ("DATE", Keyword::DATE),
            ("CREATOR", Keyword::CREATOR),
            ("PROCVER", Keyword::PROCVER),
            ("FILEVER", Keyword::FILEVER),
            ("TIMVERSN", Keyword::TIMVERSN),
            ("TELESCOP", Keyword::TELESCOP),
            ("INSTRUME", Keyword::INSTRUME),
            ("OBJECT", Keyword::OBJECT),
            ("KEPLERID", Keyword::KEPLERID),
            ("CHANNEL", Keyword::CHANNEL),
            ("MODULE", Keyword::MODULE),
            ("OUTPUT", Keyword::OUTPUT),
            ("CAMPAIGN", Keyword::CAMPAIGN),
            ("DATA_REL", Keyword::DATA_REL),
            ("OBSMODE", Keyword::OBSMODE),
            ("MISSION", Keyword::MISSION),
            ("TTABLEID", Keyword::TTABLEID),
            ("RADESYS", Keyword::RADESYS),
            ("RA_OBJ", Keyword::RA_OBJ),
            ("DEC_OBJ", Keyword::DEC_OBJ),
            ("EQUINOX", Keyword::EQUINOX),
            ("PMRA", Keyword::PMRA),
            ("PMDEC", Keyword::PMDEC),
            ("PMTOTAL", Keyword::PMTOTAL),
            ("PARALLAX", Keyword::PARALLAX),
            ("GLON", Keyword::GLON),
            ("GLAT", Keyword::GLAT),
            ("GMAG", Keyword::GMAG),
            ("RMAG", Keyword::RMAG),
            ("IMAG", Keyword::IMAG),
            ("ZMAG", Keyword::ZMAG),
            ("JMAG", Keyword::JMAG),
            ("HMAG", Keyword::HMAG),
            ("KMAG", Keyword::KMAG),
            ("KEPMAG", Keyword::KEPMAG),
            ("GRCOLOR", Keyword::GRCOLOR),
            ("JKCOLOR", Keyword::JKCOLOR),
            ("GKCOLOR", Keyword::GKCOLOR),
            ("TEFF", Keyword::TEFF),
            ("LOGG", Keyword::LOGG),
            ("FEH", Keyword::FEH),
            ("EBMINUSV", Keyword::EBMINUSV),
            ("AV", Keyword::AV),
            ("RADIUS", Keyword::RADIUS),
            ("TMINDEX", Keyword::TMINDEX),
            ("CHECKSUM", Keyword::CHECKSUM),
            ("DATASUM", Keyword::DATASUM),
            ("END", Keyword::END),
        );

        for (input, expected) in data {
            assert_eq!(Keyword::from_str(input).unwrap(), expected);
        }
    }

    #[test]
    fn should_also_parse_whitespace_keywords() {
        assert_eq!(Keyword::from_str("SIMPLE  ").unwrap(), Keyword::SIMPLE);
    }
}
<|endoftext|>


<commit_before>use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::cmp::{min, max, Ordering};
use std::default::Default;
use std::fs;
use std::io::Read;
use std::ops::Range;
use std::path::Path;

use cfg::ColorScheme;
use gfx::MouseCursor;
use glyph::Metrics;
use graphics::character::CharacterCache;

use ui::{BB, Px};
use ui::layout::{RectBB, RectBounded, Layout};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

use ide::highlight;

pub struct Editor {
    bb: RectBB,
    font: text::Mono,
    font_bold: text::MonoBold,
    font_metrics: Cell<Metrics>,
    over: Cell<bool>,
    down: Cell<bool>,

    // Caret is visible between [0, 0.5) and hidden between [0.5, 1).
    blink_phase: Cell<f32>,
    scroll_start: Cell<usize>,

    selection_start: Cell<Caret>,
    caret: Cell<Caret>,

    lines: RefCell<Vec<Line>>
}

#[derive(Copy, Clone)]
struct Caret {
    row: usize,
    col: usize,
    offset: usize
}

impl PartialEq for Caret {
    fn eq(&self, other: &Caret) -> bool {
        self.row == other.row && self.col == other.col
    }
}

impl Eq for Caret {}

impl PartialOrd for Caret {
    fn partial_cmp(&self, other: &Caret) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Caret {
    fn cmp(&self, other: &Caret) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

#[derive(Debug)]
struct Line {
    data: String,
    hl_depth: usize,
    ranges: Vec<(usize, highlight::Style)>
}

impl Editor {
    pub fn open<P: AsRef<Path>>(path: P) -> Editor {
        let mut data = String::new();
        fs::File::open(path).unwrap().read_to_string(&mut data).unwrap();
        let lines = data.split('\n').map(|line| Line {
            data: line.to_owned(),
            hl_depth: 1,
            ranges: vec![]
        }).collect();

        let caret = Caret {
            row: 0,
            col: 0,
            offset: 0
        };

        let editor = Editor {
            bb: RectBB::default(),
            font: text::Mono,
            font_bold: text::MonoBold,
            font_metrics: Cell::new(Metrics::default()),
            over: Cell::new(false),
            down: Cell::new(false),
            scroll_start: Cell::new(0),

            blink_phase: Cell::new(0.0),
            selection_start: Cell::new(caret),
            caret: Cell::new(caret),

            lines: RefCell::new(lines)
        };

        let num_lines = editor.lines.borrow().len();
        editor.update_hl(0..num_lines);

        editor
    }

    fn pos_to_caret(&self, [x, y]: [Px; 2]) -> Option<Caret> {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return None;
        }

        let bb = self.bb();
        if x > bb.x2 || y > bb.y2 {
            return None;
        }
        let [x, y] = [x - bb.x1, y - bb.y1];
        if x < 0.0 || y < 0.0 {
            return None;
        }
        let row = (y / metrics.height) as usize + self.scroll_start.get();

        let mut x2 = 0.0;
        let mut col = 0;
        let mut offset = 0;
        for c in self.lines.borrow()[row].data.chars() {
            let w = c.width(false).unwrap_or(1);
            x2 += w as Px * metrics.width;
            if x2 > x {
                break;
            }
            col += w;
            offset += c.len_utf8();
        }
        Some(Caret { row: row, col: col, offset: offset })
    }

    fn move_to(&self, caret: Caret) {
        self.selection_start.set(caret);
        self.caret.set(caret);
        self.blink_phase.set(0.0);
    }

    fn update_hl(&self, mut range: Range<usize>) {
        let mut lines = self.lines.borrow_mut();
        while lines[range.start].hl_depth > 0 && range.start > 0 {
            range.start -= 1;
        }
        while lines[range.end - 1].hl_depth > 0 && range.end < lines.len() {
            range.end += 1;
        }

        let (d, mut hl) = highlight::Rust::run(lines[range.clone()].iter().map(|line| &line.data[..]));

        // Fallback to re-highlight everything until the end.
        if d > 0 {
            hl = highlight::Rust::run(lines.iter().map(|line| &line.data[..])).1;
            range = 0..lines.len();
        }

        for (line, (hl_depth, ranges)) in lines[range].iter_mut().zip(hl.into_iter()) {
            line.hl_depth = hl_depth;
            line.ranges = ranges;
        }
    }

    fn remove(&self, range: Range<Caret>) {
        let (s1, s2) = (range.start, range.end);
        let mut lines = self.lines.borrow_mut();

        // Remove part of the first line.
        if s1.row == s2.row {
            let line = &mut lines[s1.row].data;

            let final_len = line.len() - (s2.offset - s1.offset);
            while line.len() > final_len {
                line.remove(s1.offset);
            }
        } else {
            lines[s1.row].data.truncate(s1.offset);
        }

        // Add part of last line to first line (if range has at least 2 lines).
        if s1.row < s2.row {
            let (dest, src) = lines[s1.row..].split_at_mut(1);
            let dest = &mut dest[0].data;
            let src = &mut src[s2.row - s1.row - 1].data;

            dest.push_str(&src[s2.offset..]);
        }

        // Remove all other lines.
        for _ in s1.row+1..s2.row+1 {
            lines.remove(s1.row + 1);
        }
    }

    fn insert(&self, data: &str) {
        let (s1, s2) = (self.selection_start.get(), self.caret.get());
        let (s1, s2) = (min(s1, s2), max(s1, s2));

        if s1 != s2 {
            self.remove(s1..s2);
        }

        let mut k = s1;
        {
            let mut lines = self.lines.borrow_mut();
            let line = &mut lines[k.row].data;
            for ch in data.chars() {
                line.insert(k.offset, ch);
                k.offset += ch.len_utf8();
                k.col += 1;
            }
        }

        self.update_hl(k.row..k.row+1);
        self.move_to(k);
    }
}

impl RectBounded for Editor {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<editor>" }
}

impl Draw for Editor {
    fn draw(&self, cx: &mut DrawCx) {
        let mut metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            metrics = cx.fonts.metrics(self.font);
            self.font_metrics.set(metrics);
        }

        if self.over.get() {
            cx.cursor = MouseCursor::Text;
        }

        let bb = self.bb();
        let start = self.scroll_start.get();
        let end = start + (((bb.y2 - bb.y1) / metrics.height) as usize);
        let lines = self.lines.borrow();
        let lines = if end < lines.len() {
            &lines[start..end]
        } else {
            &lines[start..]
        };

        cx.rect(bb, ColorScheme.back_view());

        let s1 = self.selection_start.get();
        let s2 = self.caret.get();
        let k = s2;
        if start <= s2.row && s2.row <= end {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x2, y2: y + metrics.height
            }, ColorScheme.back_view_alt());
        }

        // First line of the selection.
        let (s1, s2) = (min(s1, s2), max(s1, s2));
        if start <= s1.row && s1.row <= end {
            let y = bb.y1 + ((s1.row - start) as Px * metrics.height);
            let x1 = bb.x1 + (s1.col as Px) * metrics.width;
            let x2 = if s1.row == s2.row {
                bb.x1 + (s2.col as Px) * metrics.width
            } else {
                bb.x2
            };
            cx.rect(BB {
                x1: x1, y1: y,
                x2: x2, y2: y + metrics.height
            }, ColorScheme.focus());
        }
        // Subsequent lines.
        for row in (s1.row + 1)..s2.row {
            if start <= row && row <= end {
                let y = bb.y1 + ((row - start) as Px * metrics.height);
                cx.rect(BB {
                    x1: bb.x1, y1: y,
                    x2: bb.x2, y2: y + metrics.height
                }, ColorScheme.focus());
            }
        }
        // Last line (if selection has at least 2 lines).
        if start <= s2.row && s2.row <= end && s1.row < s2.row {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x1 + (s2.col as Px) * metrics.width, y2: y + metrics.height
            }, ColorScheme.focus());
        }

        // The actual text in each line.
        for (i, line) in lines.iter().enumerate() {
            let y = bb.y1 + i as Px * metrics.height;
            let mut pos = 0;
            for &(len, style) in &line.ranges {
                let x = bb.x1 + (pos as Px) * metrics.width;
                if style.bold {
                    self.font_bold.draw(cx, [x, y], style.color, &line.data[pos..pos+len]);
                } else {
                    self.font.draw(cx, [x, y], style.color, &line.data[pos..pos+len]);
                }
                pos += len;
            }
        }

        // Caret on top of everything else.
        if self.blink_phase.get() < BLINK_SPACING && start <= k.row && k.row <= end {
            let y = bb.y1 + ((k.row - start) as Px * metrics.height);

            // TODO proper BB scissoring.
            let x = bb.x1 + (k.col as Px) * metrics.width;
            let w = 2.0;
            if bb.x1 <= x && x + w <= bb.x2 {
                cx.rect(BB {
                    x1: x, y1: y,
                    x2: x + w, y2: y + metrics.height
                }, ColorScheme.normal());
            }
        }
    }
}

impl Dispatch<MouseDown> for Editor {
    fn dispatch(&self, ev: &MouseDown) -> bool {
        self.down.set(true);

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            self.move_to(caret);
            true
        } else {
            false
        }
    }
}

impl Dispatch<MouseUp> for Editor {
    fn dispatch(&self, _: &MouseUp) -> bool {
        self.down.set(false);
        false
    }
}

impl Dispatch<MouseMove> for Editor {
    fn dispatch(&self, ev: &MouseMove) -> bool {
        let over = self.bb().contains([ev.x, ev.y]);
        let mut dirty = false;
        if over != self.over.get() {
            self.over.set(over);
            dirty = true;
        }

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            if self.down.get() {
                self.caret.set(caret);
                dirty = true;
            }
        }

        dirty
    }
}

impl Dispatch<MouseScroll> for Editor {
    fn dispatch(&self, ev: &MouseScroll) -> bool {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return false;
        }

        let [_, dy] = ev.delta();
        if dy == 0.0 {
            return false;
        }

        let dy = -dy;
        let bb = self.bb();
        let sy = self.scroll_start.get();
        let new_sy = if dy < 0.0 {
            let dy = -dy as usize;
            if sy < dy { sy } else { sy - dy }
        } else {
            let dy = dy as usize;
            if ((self.lines.borrow().len() as Px)  - ((sy + dy) as Px)) * metrics.height <= (bb.y2 - bb.y1) {
                sy
            } else {
                sy + dy
            }
        };
        self.scroll_start.set(new_sy);
        new_sy != sy
    }
}

const BLINK_SPACING: f32 = 0.5;

impl Dispatch<Update> for Editor {
    fn dispatch(&self, &Update(dt): &Update) -> bool {
        let mut dirty = false;

        let blink = (self.blink_phase.get() + dt) % (BLINK_SPACING * 2.0);
        dirty |= (blink >= BLINK_SPACING) != (self.blink_phase.get() >= BLINK_SPACING);
        self.blink_phase.set(blink);

        dirty
    }
}

impl<'a> Dispatch<TextInput<'a>> for Editor {
    fn dispatch(&self, ev: &TextInput) -> bool {
        self.insert(ev.0);
        true
    }
}
<commit_msg>editor: draw multibyte characters at the right column.<commit_after>use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::cmp::{min, max, Ordering};
use std::default::Default;
use std::fs;
use std::io::Read;
use std::ops::Range;
use std::path::Path;

use cfg::ColorScheme;
use gfx::MouseCursor;
use glyph::Metrics;
use graphics::character::CharacterCache;

use ui::{BB, Px};
use ui::layout::{RectBB, RectBounded, Layout};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

use ide::highlight;

pub struct Editor {
    bb: RectBB,
    font: text::Mono,
    font_bold: text::MonoBold,
    font_metrics: Cell<Metrics>,
    over: Cell<bool>,
    down: Cell<bool>,

    // Caret is visible between [0, 0.5) and hidden between [0.5, 1).
    blink_phase: Cell<f32>,
    scroll_start: Cell<usize>,

    selection_start: Cell<Caret>,
    caret: Cell<Caret>,

    lines: RefCell<Vec<Line>>
}

#[derive(Copy, Clone)]
struct Caret {
    row: usize,
    col: usize,
    offset: usize
}

impl Caret {
    fn advance(&mut self, c: char, forward: bool) {
        let w = if c == '\t' {
            // FIXME this won't work backwards.
            7 - (self.col + 7) % 8
        } else {
            c.width(false).unwrap_or(1)
        };
        let l = c.len_utf8();

        if forward {
            self.col += w;
            self.offset += l;
        } else {
            self.col -= w;
            self.offset -= l;
        }
    }
}

impl PartialEq for Caret {
    fn eq(&self, other: &Caret) -> bool {
        self.row == other.row && self.col == other.col
    }
}

impl Eq for Caret {}

impl PartialOrd for Caret {
    fn partial_cmp(&self, other: &Caret) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Caret {
    fn cmp(&self, other: &Caret) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

#[derive(Debug)]
struct Line {
    data: String,
    hl_depth: usize,
    ranges: Vec<(usize, highlight::Style)>
}

impl Editor {
    pub fn open<P: AsRef<Path>>(path: P) -> Editor {
        let mut data = String::new();
        fs::File::open(path).unwrap().read_to_string(&mut data).unwrap();
        let lines = data.split('\n').map(|line| Line {
            data: line.to_owned(),
            hl_depth: 1,
            ranges: vec![]
        }).collect();

        let caret = Caret {
            row: 0,
            col: 0,
            offset: 0
        };

        let editor = Editor {
            bb: RectBB::default(),
            font: text::Mono,
            font_bold: text::MonoBold,
            font_metrics: Cell::new(Metrics::default()),
            over: Cell::new(false),
            down: Cell::new(false),
            scroll_start: Cell::new(0),

            blink_phase: Cell::new(0.0),
            selection_start: Cell::new(caret),
            caret: Cell::new(caret),

            lines: RefCell::new(lines)
        };

        let num_lines = editor.lines.borrow().len();
        editor.update_hl(0..num_lines);

        editor
    }

    fn pos_to_caret(&self, [x, y]: [Px; 2]) -> Option<Caret> {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return None;
        }

        let bb = self.bb();
        if x > bb.x2 || y > bb.y2 {
            return None;
        }
        let [x, y] = [x - bb.x1, y - bb.y1];
        if x < 0.0 || y < 0.0 {
            return None;
        }

        let mut k = Caret {
            row:  (y / metrics.height) as usize + self.scroll_start.get(),
            col: 0,
            offset: 0
        };

        let lines = self.lines.borrow();
        if k.row >= lines.len() {
            return None;
        }

        for c in lines[k.row].data.chars() {
            let prev_k = k;
            k.advance(c, true);
            if k.col as Px * metrics.width > x {
                return Some(prev_k);
            }
        }
        Some(k)
    }

            }
            col += w;
            offset += c.len_utf8();
        }
        Some(Caret { row: row, col: col, offset: offset })
    }

    fn move_to(&self, caret: Caret) {
        self.selection_start.set(caret);
        self.caret.set(caret);
        self.blink_phase.set(0.0);
    }

    fn update_hl(&self, mut range: Range<usize>) {
        let mut lines = self.lines.borrow_mut();
        while lines[range.start].hl_depth > 0 && range.start > 0 {
            range.start -= 1;
        }
        while lines[range.end - 1].hl_depth > 0 && range.end < lines.len() {
            range.end += 1;
        }

        let (d, mut hl) = highlight::Rust::run(lines[range.clone()].iter().map(|line| &line.data[..]));

        // Fallback to re-highlight everything until the end.
        if d > 0 {
            hl = highlight::Rust::run(lines.iter().map(|line| &line.data[..])).1;
            range = 0..lines.len();
        }

        for (line, (hl_depth, ranges)) in lines[range].iter_mut().zip(hl.into_iter()) {
            line.hl_depth = hl_depth;
            line.ranges = ranges;
        }
    }

    fn remove(&self, range: Range<Caret>) {
        let (s1, s2) = (range.start, range.end);
        let mut lines = self.lines.borrow_mut();

        // Remove part of the first line.
        if s1.row == s2.row {
            let line = &mut lines[s1.row].data;

            let final_len = line.len() - (s2.offset - s1.offset);
            while line.len() > final_len {
                line.remove(s1.offset);
            }
        } else {
            lines[s1.row].data.truncate(s1.offset);
        }

        // Add part of last line to first line (if range has at least 2 lines).
        if s1.row < s2.row {
            let (dest, src) = lines[s1.row..].split_at_mut(1);
            let dest = &mut dest[0].data;
            let src = &mut src[s2.row - s1.row - 1].data;

            dest.push_str(&src[s2.offset..]);
        }

        // Remove all other lines.
        for _ in s1.row+1..s2.row+1 {
            lines.remove(s1.row + 1);
        }
    }

    fn insert(&self, data: &str) {
        let (s1, s2) = (self.selection_start.get(), self.caret.get());
        let (s1, s2) = (min(s1, s2), max(s1, s2));

        if s1 != s2 {
            self.remove(s1..s2);
        }

        let mut k = s1;
        {
            let mut lines = self.lines.borrow_mut();
            let line = &mut lines[k.row].data;
            for ch in data.chars() {
                line.insert(k.offset, ch);
                k.offset += ch.len_utf8();
                k.col += 1;
            }
        }

        self.update_hl(k.row..k.row+1);
        self.move_to(k);
    }
}

impl RectBounded for Editor {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<editor>" }
}

impl Draw for Editor {
    fn draw(&self, cx: &mut DrawCx) {
        let mut metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            metrics = cx.fonts.metrics(self.font);
            self.font_metrics.set(metrics);
        }

        if self.over.get() {
            cx.cursor = MouseCursor::Text;
        }

        let bb = self.bb();
        let start = self.scroll_start.get();
        let end = start + (((bb.y2 - bb.y1) / metrics.height) as usize);
        let lines = self.lines.borrow();
        let lines = if end < lines.len() {
            &lines[start..end]
        } else {
            &lines[start..]
        };

        cx.rect(bb, ColorScheme.back_view());

        let s1 = self.selection_start.get();
        let s2 = self.caret.get();
        let k = s2;
        if start <= s2.row && s2.row <= end {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x2, y2: y + metrics.height
            }, ColorScheme.back_view_alt());
        }

        // First line of the selection.
        let (s1, s2) = (min(s1, s2), max(s1, s2));
        if start <= s1.row && s1.row <= end {
            let y = bb.y1 + ((s1.row - start) as Px * metrics.height);
            let x1 = bb.x1 + (s1.col as Px) * metrics.width;
            let x2 = if s1.row == s2.row {
                bb.x1 + (s2.col as Px) * metrics.width
            } else {
                bb.x2
            };
            cx.rect(BB {
                x1: x1, y1: y,
                x2: x2, y2: y + metrics.height
            }, ColorScheme.focus());
        }
        // Subsequent lines.
        for row in (s1.row + 1)..s2.row {
            if start <= row && row <= end {
                let y = bb.y1 + ((row - start) as Px * metrics.height);
                cx.rect(BB {
                    x1: bb.x1, y1: y,
                    x2: bb.x2, y2: y + metrics.height
                }, ColorScheme.focus());
            }
        }
        // Last line (if selection has at least 2 lines).
        if start <= s2.row && s2.row <= end && s1.row < s2.row {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x1 + (s2.col as Px) * metrics.width, y2: y + metrics.height
            }, ColorScheme.focus());
        }

        // The actual text in each line.
        for (i, line) in lines.iter().enumerate() {
            let y = bb.y1 + i as Px * metrics.height;
            let mut draw_k = Caret {
                row: 0,
                col: 0,
                offset: 0
            };
            for &(len, style) in &line.ranges {
                let x = bb.x1 + (draw_k.col as Px) * metrics.width;
                let data = &line.data[draw_k.offset..draw_k.offset+len];
                if style.bold {
                    self.font_bold.draw(cx, [x, y], style.color, data);
                } else {
                    self.font.draw(cx, [x, y], style.color, data);
                }
                for c in data.chars() {
                    draw_k.advance(c, true);
                }
            }
        }

        // Caret on top of everything else.
        if self.blink_phase.get() < BLINK_SPACING && start <= k.row && k.row <= end {
            let y = bb.y1 + ((k.row - start) as Px * metrics.height);

            // TODO proper BB scissoring.
            let x = bb.x1 + (k.col as Px) * metrics.width;
            let w = 2.0;
            if bb.x1 <= x && x + w <= bb.x2 {
                cx.rect(BB {
                    x1: x, y1: y,
                    x2: x + w, y2: y + metrics.height
                }, ColorScheme.normal());
            }
        }
    }
}

impl Dispatch<MouseDown> for Editor {
    fn dispatch(&self, ev: &MouseDown) -> bool {
        self.down.set(true);

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            self.move_to(caret);
            true
        } else {
            false
        }
    }
}

impl Dispatch<MouseUp> for Editor {
    fn dispatch(&self, _: &MouseUp) -> bool {
        self.down.set(false);
        false
    }
}

impl Dispatch<MouseMove> for Editor {
    fn dispatch(&self, ev: &MouseMove) -> bool {
        let over = self.bb().contains([ev.x, ev.y]);
        let mut dirty = false;
        if over != self.over.get() {
            self.over.set(over);
            dirty = true;
        }

        if let Some(caret) = self.pos_to_caret([ev.x, ev.y]) {
            if self.down.get() {
                self.caret.set(caret);
                dirty = true;
            }
        }

        dirty
    }
}

impl Dispatch<MouseScroll> for Editor {
    fn dispatch(&self, ev: &MouseScroll) -> bool {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return false;
        }

        let [_, dy] = ev.delta();
        if dy == 0.0 {
            return false;
        }

        let dy = -dy;
        let bb = self.bb();
        let sy = self.scroll_start.get();
        let new_sy = if dy < 0.0 {
            let dy = -dy as usize;
            if sy < dy { sy } else { sy - dy }
        } else {
            let dy = dy as usize;
            if ((self.lines.borrow().len() as Px)  - ((sy + dy) as Px)) * metrics.height <= (bb.y2 - bb.y1) {
                sy
            } else {
                sy + dy
            }
        };
        self.scroll_start.set(new_sy);
        new_sy != sy
    }
}

const BLINK_SPACING: f32 = 0.5;

impl Dispatch<Update> for Editor {
    fn dispatch(&self, &Update(dt): &Update) -> bool {
        let mut dirty = false;

        let blink = (self.blink_phase.get() + dt) % (BLINK_SPACING * 2.0);
        dirty |= (blink >= BLINK_SPACING) != (self.blink_phase.get() >= BLINK_SPACING);
        self.blink_phase.set(blink);

        dirty
    }
}

impl<'a> Dispatch<TextInput<'a>> for Editor {
    fn dispatch(&self, ev: &TextInput) -> bool {
        self.insert(ev.0);
        true
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate rand;
extern crate byteorder;
extern crate mqtt3;
extern crate netopt;

mod error;
mod sub;
mod client;

pub use sub::{
    ToSubTopics,
    ToUnSubTopics
};

pub use client::{
    Client,
    ClientOptions
};

use std::sync::Arc;
use std::ops;
use std::time::Duration;
use mqtt3::{QoS, ToTopicPath};
use error::Result;

const MAX_QOS: QoS = mqtt3::QoS::AtLeastOnce;

pub trait Mqttc {
    fn publish<T: ToTopicPath, P: ToPayload>(&mut self, topic: T, payload: P, pubopt: PubOpt) -> Result<()>;
    fn subscribe<S: ToSubTopics>(&mut self, subs: S) -> Result<()>;
    fn unsubscribe<U: ToUnSubTopics>(&mut self, unsubs: U) -> Result<()>;
    fn disconnect(self) -> Result<()>;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClientState {
    Handshake,
    Connected,
    Disconnected
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReconnectMethod {
    ForeverDisconnect,
    ReconnectAfter(Duration)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PubOpt(u8);

impl PubOpt {
    #[inline]
    pub fn at_most_once() -> PubOpt {
        PubOpt(0x00)
    }

    #[inline]
    pub fn at_least_once() -> PubOpt {
        PubOpt(0x01)
    }

    #[inline]
    pub fn exactly_once() -> PubOpt {
        PubOpt(0x02)
    }

    #[inline]
    pub fn retain() -> PubOpt {
        PubOpt(0x04)
    }

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

    pub fn qos(&self) -> QoS {
        if (self.0 & PubOpt::exactly_once().bits()) != 0 {
            return QoS::ExactlyOnce;
        }
        if (self.0 & PubOpt::at_least_once().bits()) != 0 {
            return QoS::AtLeastOnce;
        }
        QoS::AtMostOnce
    }

    pub fn is_retain(&self) -> bool {
        (self.0 & PubOpt::retain().bits()) != 0
    }
}

impl ops::BitOr for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() | other.bits())
    }
}

impl ops::BitXor for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitxor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() ^ other.bits())
    }
}

impl ops::BitAnd for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitand(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & other.bits())
    }
}

impl ops::Sub for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn sub(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & !other.bits())
    }
}

impl ops::Not for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn not(self) -> PubOpt {
        PubOpt(!self.bits() & 0b111)
    }
}

pub type Payload = Arc<Vec<u8>>;

pub trait ToPayload {
    fn to_payload(&self) -> Payload;
}

impl ToPayload for String {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone().into_bytes())
    }
}

impl<'a> ToPayload for &'a str {
    fn to_payload(&self) -> Payload {
        Arc::new(self.as_bytes().to_vec())
    }
}

impl ToPayload for Vec<u8> {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone())
    }
}

impl ToPayload for Arc<Vec<u8>> {
    fn to_payload(&self) -> Payload {
        self.clone()
    }
}

#[cfg(test)]
mod test {
    use super::PubOpt;
    use mqtt3::QoS;

    #[test]
    fn pubopt_test() {
        let pubopt = PubOpt::at_least_once();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_most_once();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::exactly_once();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_least_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::at_most_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::exactly_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(pubopt.is_retain());
    }
}
<commit_msg>mqttc: Require rust feature time2<commit_after>#![feature(time2)]
#[macro_use] extern crate log;
extern crate rand;
extern crate byteorder;
extern crate mqtt3;
extern crate netopt;

mod error;
mod sub;
mod client;

pub use sub::{
    ToSubTopics,
    ToUnSubTopics
};

pub use client::{
    Client,
    ClientOptions
};

use std::sync::Arc;
use std::ops;
use std::time::Duration;
use mqtt3::{QoS, ToTopicPath};
use error::Result;

const MAX_QOS: QoS = mqtt3::QoS::AtLeastOnce;

pub trait Mqttc {
    fn publish<T: ToTopicPath, P: ToPayload>(&mut self, topic: T, payload: P, pubopt: PubOpt) -> Result<()>;
    fn subscribe<S: ToSubTopics>(&mut self, subs: S) -> Result<()>;
    fn unsubscribe<U: ToUnSubTopics>(&mut self, unsubs: U) -> Result<()>;
    fn disconnect(self) -> Result<()>;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClientState {
    Handshake,
    Connected,
    Disconnected
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReconnectMethod {
    ForeverDisconnect,
    ReconnectAfter(Duration)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PubOpt(u8);

impl PubOpt {
    #[inline]
    pub fn at_most_once() -> PubOpt {
        PubOpt(0x00)
    }

    #[inline]
    pub fn at_least_once() -> PubOpt {
        PubOpt(0x01)
    }

    #[inline]
    pub fn exactly_once() -> PubOpt {
        PubOpt(0x02)
    }

    #[inline]
    pub fn retain() -> PubOpt {
        PubOpt(0x04)
    }

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

    pub fn qos(&self) -> QoS {
        if (self.0 & PubOpt::exactly_once().bits()) != 0 {
            return QoS::ExactlyOnce;
        }
        if (self.0 & PubOpt::at_least_once().bits()) != 0 {
            return QoS::AtLeastOnce;
        }
        QoS::AtMostOnce
    }

    pub fn is_retain(&self) -> bool {
        (self.0 & PubOpt::retain().bits()) != 0
    }
}

impl ops::BitOr for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() | other.bits())
    }
}

impl ops::BitXor for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitxor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() ^ other.bits())
    }
}

impl ops::BitAnd for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitand(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & other.bits())
    }
}

impl ops::Sub for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn sub(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & !other.bits())
    }
}

impl ops::Not for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn not(self) -> PubOpt {
        PubOpt(!self.bits() & 0b111)
    }
}

pub type Payload = Arc<Vec<u8>>;

pub trait ToPayload {
    fn to_payload(&self) -> Payload;
}

impl ToPayload for String {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone().into_bytes())
    }
}

impl<'a> ToPayload for &'a str {
    fn to_payload(&self) -> Payload {
        Arc::new(self.as_bytes().to_vec())
    }
}

impl ToPayload for Vec<u8> {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone())
    }
}

impl ToPayload for Arc<Vec<u8>> {
    fn to_payload(&self) -> Payload {
        self.clone()
    }
}

#[cfg(test)]
mod test {
    use super::PubOpt;
    use mqtt3::QoS;

    #[test]
    fn pubopt_test() {
        let pubopt = PubOpt::at_least_once();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_most_once();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::exactly_once();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_least_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::at_most_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::exactly_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(pubopt.is_retain());
    }
}
<|endoftext|>