<commit_before>extern crate lzf;
use std::str;
use lzf::decompress;

mod version {
    pub const SUPPORTED_MINIMUM : u32 = 1;
    pub const SUPPORTED_MAXIMUM : u32 = 6;
}

mod constants {
    pub const RDB_6BITLEN : u8 = 0;
    pub const RDB_14BITLEN : u8 = 1;
    pub const RDB_32BITLEN : u8 = 2;
    pub const RDB_ENCVAL : u8 = 3;
    // REDIS
    pub const RDB_MAGIC : &'static str = "REDIS";
}

mod op_codes {
    pub const EXPIRETIME_MS : u8 = 252;
    pub const EXPIRETIME : u8 = 253;
    pub const SELECTDB   : u8 = 254;
    pub const EOF : u8 = 255;
}

mod types {
    pub const STRING : u8 = 0;
    pub const LIST : u8 = 1;
    pub const SET : u8 = 2;
    pub const ZSET : u8 = 3;
    pub const HASH : u8 = 4;
    pub const HASH_ZIPMAP : u8 = 9;
    pub const LIST_ZIPLIST : u8 = 10;
    pub const SET_INTSET : u8 = 11;
    pub const ZSET_ZIPLIST : u8 = 12;
    pub const HASH_ZIPLIST : u8 = 13;
}

mod encoding {
    pub const INT8 : u32 = 0;
    pub const INT16 : u32 = 1;
    pub const INT32 : u32 = 2;
    pub const LZF : u32 = 3;
}

#[deriving(Show)]
pub enum DataType {
    String(Vec<u8>),
    List(Vec<Vec<u8>>),
    Set(Vec<Vec<u8>>),
    SortedSet(Vec<(f32,Vec<u8>)>),
    Hash(Vec<Vec<u8>>),
    Unknown
}

//pub fn type_mapping(in_type: u8) -> DataType {
    //match in_type {
        //0 => DataType::String,
        //1 | 10 => DataType::List,
        //2 | 11 => DataType::Set,
        //3 | 12 => DataType::SortedSet,
        //4 | 9 | 13 => DataType::Hash,
        //_ => DataType::Unknown
    //}
//}

pub fn parse<T: Reader>(input: &mut T) {

    assert!(verify_magic(input));
    assert!(verify_version(input));

    loop {
        let next_op = input.read_byte().unwrap();
        //println!("next_op: {:x}", next_op);

        match next_op {
            op_codes::SELECTDB => {
                let db = input.read_byte().unwrap();
                println!("SELECTDB: {}", db);
            },
            op_codes::EOF => {
                println!("EOF");
                break ;
            },
            op_codes::EXPIRETIME_MS => {
                let expiretime_ms = input.read_le_u64().unwrap();
                println!("EXPIRETIME_MS: {}", expiretime_ms);
            },
            op_codes::EXPIRETIME => {
                let expiretime = input.read_be_u32().unwrap();
                println!("EXPIRETIME: {}", expiretime);
            }
            _ => {
                let key = read_blob(input);
                println!("Key: {}",
                         str::from_utf8(key.as_slice()).unwrap());
                println!("Val: {}", read_type(next_op, input));
                break;
            }
        }

    }

    return;

}

fn read_type<T: Reader>(value_type: u8, input: &mut T) -> DataType {
    match value_type {
        types::STRING => {
            DataType::String(read_blob(input))
        },
        types::LIST => {
            //DataType::List(read_linked_list(input))

            let mut len = read_length(input);
            let mut list = vec![];

            while len > 0 {
                let blob = read_blob(input);
                list.push(blob);
                len -= 1;
            }

            DataType::List(list)
        },
        types::SET => {
            println!("Value Type not implemented: {}", "SET");

            //DataType::Set(read_set(input))
            DataType::Set(vec![])
        },
        types::ZSET => {
            println!("Value Type not implemented: {}", "ZSET");
            //DataType::SortedSet(read_sorted_set(input))
            DataType::SortedSet(vec![])
        },
        types::HASH => {
            println!("Value Type not implemented: {}", "HASH");
            //DataType::Hash(read_hash(input))
            DataType::Hash(vec![])
        },
        types::HASH_ZIPMAP => {
            println!("Value Type not implemented: {}", "HASH_ZIPMAP");
            //DataType::Hash(read_hash_zipmap(input))
            DataType::Hash(vec![])
        },
        types::LIST_ZIPLIST => {
            println!("Value Type not implemented: {}", "LIST_ZIPLIST");
            //DataType::List(read_list_ziplist(input))
            DataType::List(vec![])
        },
        types::SET_INTSET => {
            println!("Value Type not implemented: {}", "SET_INTSET");
            //DataType::Set(read_set_intset(input))
            DataType::Set(vec![])
        },
        types::ZSET_ZIPLIST => {
            println!("Value Type not implemented: {}", "ZSET_ZIPLIST");
            //DataType::SortedSet(read_zset_ziplist(input))
            DataType::SortedSet(vec![])
        },
        types::HASH_ZIPLIST => {
            println!("Value Type not implemented: {}", "HASH_ZIPLIST");
            //DataType::Hash(read_hash_ziplist(input))
            DataType::Hash(vec![])
        },
        _ => { panic!("Value Type not implemented: {}", value_type) }
    }
}

fn verify_magic<T: Reader>(input: &mut T) -> bool {
    let magic = input.read_exact(5).unwrap();

    // Meeeeeh.
    magic[0] == constants::RDB_MAGIC.as_bytes()[0] &&
        magic[1] == constants::RDB_MAGIC.as_bytes()[1] &&
        magic[2] == constants::RDB_MAGIC.as_bytes()[2] &&
        magic[3] == constants::RDB_MAGIC.as_bytes()[3] &&
        magic[4] == constants::RDB_MAGIC.as_bytes()[4]
}

fn verify_version<T: Reader>(input: &mut T) -> bool {
    let version = input.read_exact(4).unwrap();

    let version = (version[0]-48) as u32 * 1000 +
        (version[1]-48) as u32 * 100 +
        (version[2]-48) as u32 * 10 +
        (version[3]-48) as u32;

    version >= version::SUPPORTED_MINIMUM &&
        version <= version::SUPPORTED_MAXIMUM
}

#[test]
fn test_verify_magic() {
    use std::io::MemReader;

    assert_eq!(
        true,
        verify_magic(&mut MemReader::new(vec!(0x52, 0x45, 0x44, 0x49, 0x53)))
    );

    assert_eq!(
        false,
        verify_magic(&mut MemReader::new(vec!(0x52, 0x0, 0x0, 0x0, 0x0)))
    );
}

#[test]
fn test_verify_version() {
    use std::io::MemReader;

    assert_eq!(
        true,
        verify_version(&mut MemReader::new(vec!(0x30, 0x30, 0x30, 0x33)))
    );

    assert_eq!(
        false,
        verify_version(&mut MemReader::new(vec!(0x30, 0x30, 0x30, 0x3a)))
    );
}

#[deriving(Show,PartialEq)]
enum LengthEncoded {
    LE(u32, bool)
    //Encoded(u32),
    //Plain(u32)
}

fn read_length_with_encoding<T: Reader>(input: &mut T) -> LengthEncoded {
    let mut length;
    let mut is_encoded = false;

    let enc_type = input.read_byte().unwrap();

    match (enc_type & 0xC0) >> 6 {
        constants::RDB_ENCVAL => {
            is_encoded = true;
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_6BITLEN => {
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_14BITLEN => {
            let next_byte = input.read_byte().unwrap();
            length = ((enc_type & 0x3F) as u32 <<8) | next_byte as u32;
        },
        _ => {
            println!("foo");
            length = input.read_le_u32().unwrap();
        }
    }

    LengthEncoded::LE(length, is_encoded)
}

fn read_length<T: Reader>(input: &mut T) -> u32 {
    let LengthEncoded::LE(length, _) = read_length_with_encoding(input);
    length
}

fn read_blob<T: Reader>(input: &mut T) -> Vec<u8> {
    let LengthEncoded::LE(length, is_encoded) = read_length_with_encoding(input);

    if is_encoded {
        match length {
            encoding::INT8 => { input.read_exact(1).unwrap() },
            encoding::INT16 => { input.read_exact(2).unwrap() },
            encoding::INT32 => { input.read_exact(4).unwrap() },
            encoding::LZF => {
                let compressed_length = read_length(input);
                let real_length = read_length(input);
                let data = input.read_exact(compressed_length as uint).unwrap();
                lzf::decompress(data.as_slice(), real_length as uint).unwrap()
            },
            _ => { panic!("Unknown encoding: {}", length) }
        }
    } else {
        input.read_exact(length as uint).unwrap()
    }
}

#[test]
fn test_read_length() {
    use std::io::MemReader;

    assert_eq!(
        LengthEncoded::LE(0, false),
        read_length_with_encoding(&mut MemReader::new(vec!(0x0)))
    );

    assert_eq!(
        LengthEncoded::LE(16383, false),
        read_length_with_encoding(&mut MemReader::new(vec!(0x7f, 0xff)))
    );

    assert_eq!(
        LengthEncoded::LE(4294967295, false),
        read_length_with_encoding(&mut MemReader::new(
                vec!(0x80, 0xff, 0xff, 0xff, 0xff)))
    );

    assert_eq!(
        LengthEncoded::LE(0, true),
        read_length_with_encoding(&mut MemReader::new(
                vec!(0xC0)))
    );
}

#[test]
fn test_read_blob() {
    use std::io::MemReader;

    assert_eq!(
        vec!(0x61, 0x62, 0x63, 0x64),
        read_blob(&mut MemReader::new(vec!(
                    4, 0x61, 0x62, 0x63, 0x64))));
}
<commit_msg>Lists, sets, sorted sets and hashes in their basic format implemented<commit_after>#![feature(slicing_syntax)]
extern crate lzf;
use std::str;
use lzf::decompress;

mod version {
    pub const SUPPORTED_MINIMUM : u32 = 1;
    pub const SUPPORTED_MAXIMUM : u32 = 6;
}

mod constants {
    pub const RDB_6BITLEN : u8 = 0;
    pub const RDB_14BITLEN : u8 = 1;
    pub const RDB_32BITLEN : u8 = 2;
    pub const RDB_ENCVAL : u8 = 3;
    // REDIS
    pub const RDB_MAGIC : &'static str = "REDIS";
}

mod op_codes {
    pub const EXPIRETIME_MS : u8 = 252;
    pub const EXPIRETIME : u8 = 253;
    pub const SELECTDB   : u8 = 254;
    pub const EOF : u8 = 255;
}

mod types {
    pub const STRING : u8 = 0;
    pub const LIST : u8 = 1;
    pub const SET : u8 = 2;
    pub const ZSET : u8 = 3;
    pub const HASH : u8 = 4;
    pub const HASH_ZIPMAP : u8 = 9;
    pub const LIST_ZIPLIST : u8 = 10;
    pub const SET_INTSET : u8 = 11;
    pub const ZSET_ZIPLIST : u8 = 12;
    pub const HASH_ZIPLIST : u8 = 13;
}

mod encoding {
    pub const INT8 : u32 = 0;
    pub const INT16 : u32 = 1;
    pub const INT32 : u32 = 2;
    pub const LZF : u32 = 3;
}

#[deriving(Show)]
pub enum DataType {
    String(Vec<u8>),
    List(Vec<Vec<u8>>),
    Set(Vec<Vec<u8>>),
    SortedSet(Vec<(f64,Vec<u8>)>),
    Hash(Vec<Vec<u8>>),
    Unknown
}

//pub fn type_mapping(in_type: u8) -> DataType {
    //match in_type {
        //0 => DataType::String,
        //1 | 10 => DataType::List,
        //2 | 11 => DataType::Set,
        //3 | 12 => DataType::SortedSet,
        //4 | 9 | 13 => DataType::Hash,
        //_ => DataType::Unknown
    //}
//}

pub fn parse<T: Reader>(input: &mut T) {

    assert!(verify_magic(input));
    assert!(verify_version(input));

    loop {
        let next_op = input.read_byte().unwrap();
        //println!("next_op: {:x}", next_op);

        match next_op {
            op_codes::SELECTDB => {
                let db = input.read_byte().unwrap();
                println!("SELECTDB: {}", db);
            },
            op_codes::EOF => {
                println!("EOF");
                break ;
            },
            op_codes::EXPIRETIME_MS => {
                let expiretime_ms = input.read_le_u64().unwrap();
                println!("EXPIRETIME_MS: {}", expiretime_ms);
            },
            op_codes::EXPIRETIME => {
                let expiretime = input.read_be_u32().unwrap();
                println!("EXPIRETIME: {}", expiretime);
            }
            _ => {
                let key = read_blob(input);
                println!("Key: {}",
                         str::from_utf8(key.as_slice()).unwrap());
                println!("Val: {}", read_type(next_op, input));
                break;
            }
        }

    }

    return;

}

fn read_linked_list<T: Reader>(input: &mut T) -> Vec<Vec<u8>> {
    let mut len = read_length(input);
    let mut list = vec![];

    while len > 0 {
        let blob = read_blob(input);
        list.push(blob);
        len -= 1;
    }

    list
}

fn read_sorted_set<T: Reader>(input: &mut T) -> Vec<(f64,Vec<u8>)> {
    let mut set = vec![];
    let mut set_items = read_length(input);

    while set_items > 0 {
        let val = read_blob(input);
        let score_length = input.read_byte().unwrap();
        let score = input.read_exact(score_length as uint).unwrap();
        let score = unsafe{str::from_utf8_unchecked(score[])}.
            parse::<f64>().unwrap();

        set.push((score, val));

        set_items -= 1;
    }

    set
}

fn read_hash<T: Reader>(input: &mut T) -> Vec<Vec<u8>> {
    let mut hash = vec![];
    let mut hash_items = read_length(input);
    hash_items = 2*hash_items;

    while hash_items > 0 {
        let val = read_blob(input);
        hash.push(val);

        hash_items -= 1;
    }

    hash
}

fn read_type<T: Reader>(value_type: u8, input: &mut T) -> DataType {
    match value_type {
        types::STRING => {
            DataType::String(read_blob(input))
        },
        types::LIST => {
            DataType::List(read_linked_list(input))
        },
        types::SET => {
            DataType::Set(read_linked_list(input))
        },
        types::ZSET => {
            DataType::SortedSet(read_sorted_set(input))
        },
        types::HASH => {
            DataType::Hash(read_hash(input))
        },
        types::HASH_ZIPMAP => {
            println!("Value Type not implemented: {}", "HASH_ZIPMAP");
            //DataType::Hash(read_hash_zipmap(input))
            DataType::Hash(vec![])
        },
        types::LIST_ZIPLIST => {
            println!("Value Type not implemented: {}", "LIST_ZIPLIST");
            //DataType::List(read_list_ziplist(input))
            DataType::List(vec![])
        },
        types::SET_INTSET => {
            println!("Value Type not implemented: {}", "SET_INTSET");
            //DataType::Set(read_set_intset(input))
            DataType::Set(vec![])
        },
        types::ZSET_ZIPLIST => {
            println!("Value Type not implemented: {}", "ZSET_ZIPLIST");
            //DataType::SortedSet(read_zset_ziplist(input))
            DataType::SortedSet(vec![])
        },
        types::HASH_ZIPLIST => {
            println!("Value Type not implemented: {}", "HASH_ZIPLIST");
            //DataType::Hash(read_hash_ziplist(input))
            DataType::Hash(vec![])
        },
        _ => { panic!("Value Type not implemented: {}", value_type) }
    }
}

fn verify_magic<T: Reader>(input: &mut T) -> bool {
    let magic = input.read_exact(5).unwrap();

    // Meeeeeh.
    magic[0] == constants::RDB_MAGIC.as_bytes()[0] &&
        magic[1] == constants::RDB_MAGIC.as_bytes()[1] &&
        magic[2] == constants::RDB_MAGIC.as_bytes()[2] &&
        magic[3] == constants::RDB_MAGIC.as_bytes()[3] &&
        magic[4] == constants::RDB_MAGIC.as_bytes()[4]
}

fn verify_version<T: Reader>(input: &mut T) -> bool {
    let version = input.read_exact(4).unwrap();

    let version = (version[0]-48) as u32 * 1000 +
        (version[1]-48) as u32 * 100 +
        (version[2]-48) as u32 * 10 +
        (version[3]-48) as u32;

    version >= version::SUPPORTED_MINIMUM &&
        version <= version::SUPPORTED_MAXIMUM
}

#[test]
fn test_verify_magic() {
    use std::io::MemReader;

    assert_eq!(
        true,
        verify_magic(&mut MemReader::new(vec!(0x52, 0x45, 0x44, 0x49, 0x53)))
    );

    assert_eq!(
        false,
        verify_magic(&mut MemReader::new(vec!(0x52, 0x0, 0x0, 0x0, 0x0)))
    );
}

#[test]
fn test_verify_version() {
    use std::io::MemReader;

    assert_eq!(
        true,
        verify_version(&mut MemReader::new(vec!(0x30, 0x30, 0x30, 0x33)))
    );

    assert_eq!(
        false,
        verify_version(&mut MemReader::new(vec!(0x30, 0x30, 0x30, 0x3a)))
    );
}

#[deriving(Show,PartialEq)]
enum LengthEncoded {
    LE(u32, bool)
    //Encoded(u32),
    //Plain(u32)
}

fn read_length_with_encoding<T: Reader>(input: &mut T) -> LengthEncoded {
    let mut length;
    let mut is_encoded = false;

    let enc_type = input.read_byte().unwrap();

    match (enc_type & 0xC0) >> 6 {
        constants::RDB_ENCVAL => {
            is_encoded = true;
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_6BITLEN => {
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_14BITLEN => {
            let next_byte = input.read_byte().unwrap();
            length = ((enc_type & 0x3F) as u32 <<8) | next_byte as u32;
        },
        _ => {
            println!("foo");
            length = input.read_le_u32().unwrap();
        }
    }

    LengthEncoded::LE(length, is_encoded)
}

fn read_length<T: Reader>(input: &mut T) -> u32 {
    let LengthEncoded::LE(length, _) = read_length_with_encoding(input);
    length
}

fn read_blob<T: Reader>(input: &mut T) -> Vec<u8> {
    let LengthEncoded::LE(length, is_encoded) = read_length_with_encoding(input);

    if is_encoded {
        match length {
            encoding::INT8 => { input.read_exact(1).unwrap() },
            encoding::INT16 => { input.read_exact(2).unwrap() },
            encoding::INT32 => { input.read_exact(4).unwrap() },
            encoding::LZF => {
                let compressed_length = read_length(input);
                let real_length = read_length(input);
                let data = input.read_exact(compressed_length as uint).unwrap();
                lzf::decompress(data.as_slice(), real_length as uint).unwrap()
            },
            _ => { panic!("Unknown encoding: {}", length) }
        }
    } else {
        input.read_exact(length as uint).unwrap()
    }
}

#[test]
fn test_read_length() {
    use std::io::MemReader;

    assert_eq!(
        LengthEncoded::LE(0, false),
        read_length_with_encoding(&mut MemReader::new(vec!(0x0)))
    );

    assert_eq!(
        LengthEncoded::LE(16383, false),
        read_length_with_encoding(&mut MemReader::new(vec!(0x7f, 0xff)))
    );

    assert_eq!(
        LengthEncoded::LE(4294967295, false),
        read_length_with_encoding(&mut MemReader::new(
                vec!(0x80, 0xff, 0xff, 0xff, 0xff)))
    );

    assert_eq!(
        LengthEncoded::LE(0, true),
        read_length_with_encoding(&mut MemReader::new(
                vec!(0xC0)))
    );
}

#[test]
fn test_read_blob() {
    use std::io::MemReader;

    assert_eq!(
        vec!(0x61, 0x62, 0x63, 0x64),
        read_blob(&mut MemReader::new(vec!(
                    4, 0x61, 0x62, 0x63, 0x64))));
}
<|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/.
 */

#![cfg_attr(feature = "fatal-warnings", deny(warnings))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy::correctness))]
#![cfg_attr(feature = "cargo-clippy", warn(clippy::pedantic))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::match_bool))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::if_not_else))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::stutter))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::similar_names))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::use_self))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::single_match_else))]
// Note: If you change this remember to update `README.md`.  To do so run `./tools/update-readme.sh`.
//! # Rust Persistent Data Structures
//!
//! Rust Persistent Data Structures provides [fully persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure)
//! with structural sharing.
//!
//! ## Setup
//!
//! To use rpds add the following to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rpds = "<version>"
//! ```
//!
//! ## Data Structures
//!
//! This crate offers the following data structures:
//!
//!   1. [`List`](#list)
//!   2. [`Vector`](#vector)
//!   3. [`Stack`](#stack)
//!   4. [`Queue`](#queue)
//!   5. [`HashTrieMap`](#hashtriemap)
//!   6. [`HashTrieSet`](#hashtrieset)
//!   7. [`RedBlackTreeMap`](#redblacktreemap)
//!   8. [`RedBlackTreeSet`](#redblacktreeset)
//!
//! ### `List`
//! [![List documentation](https://img.shields.io/badge/doc-List-303070.svg)](./list/struct.List.html)
//!
//! Your classic functional list.
//!
//! #### Example
//!
//! ```rust
//! use rpds::List;
//!
//! let list = List::new().push_front("list");
//!
//! assert_eq!(list.first(), Some(&"list"));
//!
//! let a_list = list.push_front("a");
//!
//! assert_eq!(a_list.first(), Some(&"a"));
//!
//! let list_dropped = a_list.drop_first().unwrap();
//!
//! assert_eq!(list_dropped, list);
//! ```
//!
//! ### `Vector`
//! [![`Vector` documentation](https://img.shields.io/badge/doc-Vector-303070.svg)](./vector/struct.Vector.html)
//!
//! A sequence that can be indexed.  The implementation is described in
//! [Understanding Persistent Vector Part 1](http://hypirion.com/musings/understanding-persistent-vector-pt-1)
//! and [Understanding Persistent Vector Part 2](http://hypirion.com/musings/understanding-persistent-vector-pt-2).
//!
//! #### Example
//!
//! ```rust
//! use rpds::Vector;
//!
//! let vector = Vector::new()
//!     .push_back("I’m")
//!     .push_back("a")
//!     .push_back("vector");
//!
//! assert_eq!(vector[1], "a");
//!
//! let screaming_vector = vector
//!     .drop_last().unwrap()
//!     .push_back("VECTOR!!!");
//!
//! assert_eq!(screaming_vector[2], "VECTOR!!!");
//! ```
//!
//! ### `Stack`
//! [![`Stack` documentation](https://img.shields.io/badge/doc-Stack-303070.svg)](./stack/struct.Stack.html)
//!
//! A LIFO (last in, first out) data structure.  This is just a [`List`](#list) in disguise.
//!
//! #### Example
//!
//! ```rust
//! use rpds::Stack;
//!
//! let stack = Stack::new().push("stack");
//!
//! assert_eq!(stack.peek(), Some(&"stack"));
//!
//! let a_stack = stack.push("a");
//!
//! assert_eq!(a_stack.peek(), Some(&"a"));
//!
//! let stack_popped = a_stack.pop().unwrap();
//!
//! assert_eq!(stack_popped, stack);
//! ```
//!
//! ### `Queue`
//! [![`Queue` documentation](https://img.shields.io/badge/doc-Queue-303070.svg)](./queue/struct.Queue.html)
//!
//! A FIFO (first in, first out) data structure.
//!
//! #### Example
//!
//! ```rust
//! use rpds::Queue;
//!
//! let queue = Queue::new()
//!     .enqueue("um")
//!     .enqueue("dois")
//!     .enqueue("tres");
//!
//! assert_eq!(queue.peek(), Some(&"um"));
//!
//! let queue_dequeued = queue.dequeue().unwrap();
//!
//! assert_eq!(queue_dequeued.peek(), Some(&"dois"));
//! ```
//!
//! ### `HashTrieMap`
//! [![`HashTrieMap` documentation](https://img.shields.io/badge/doc-HashTrieMap-303070.svg)](./map/hash_trie_map/struct.HashTrieMap.html)
//!
//! A map implemented with a [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie).
//! See [Ideal Hash Trees](https://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf) for
//! details.
//!
//! #### Example
//!
//! ```rust
//! use rpds::HashTrieMap;
//!
//! let map_en = HashTrieMap::new()
//!     .insert(0, "zero")
//!     .insert(1, "one");
//!
//! assert_eq!(map_en.get(&1), Some(&"one"));
//!
//! let map_pt = map_en
//!     .insert(1, "um")
//!     .insert(2, "dois");
//!
//! assert_eq!(map_pt.get(&2), Some(&"dois"));
//!
//! let map_pt_binary = map_pt.remove(&2);
//!
//! assert_eq!(map_pt_binary.get(&2), None);
//! ```
//!
//! ### `HashTrieSet`
//! [![`HashTrieSet` documentation](https://img.shields.io/badge/doc-HashTrieSet-303070.svg)](./set/hash_trie_set/struct.HashTrieSet.html)
//!
//! A set implemented with a [`HashTrieMap`](#hashtriemap).
//!
//! #### Example
//!
//! ```rust
//! use rpds::HashTrieSet;
//!
//! let set = HashTrieSet::new()
//!     .insert("zero")
//!     .insert("one");
//!
//! assert!(set.contains(&"one"));
//!
//! let set_extended = set.insert("two");
//!
//! assert!(set_extended.contains(&"two"));
//!
//! let set_positive = set_extended.remove(&"zero");
//!
//! assert!(!set_positive.contains(&"zero"));
//! ```
//!
//! ### `RedBlackTreeMap`
//! [![`RedBlackTreeMap` documentation](https://img.shields.io/badge/doc-RedBlackTreeMap-303070.svg)](./map/red_black_tree_map/struct.RedBlackTreeMap.html)
//!
//! A map implemented with a [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree).
//!
//! #### Example
//!
//! ```rust
//! use rpds::RedBlackTreeMap;
//!
//! let map_en = RedBlackTreeMap::new()
//!     .insert(0, "zero")
//!     .insert(1, "one");
//!
//! assert_eq!(map_en.get(&1), Some(&"one"));
//!
//! let map_pt = map_en
//!     .insert(1, "um")
//!     .insert(2, "dois");
//!
//! assert_eq!(map_pt.get(&2), Some(&"dois"));
//!
//! let map_pt_binary = map_pt.remove(&2);
//!
//! assert_eq!(map_pt_binary.get(&2), None);
//!
//! assert_eq!(map_pt_binary.first(), Some((&0, &"zero")));
//! ```
//!
//! ### `RedBlackTreeSet`
//! [![`RedBlackTreeSet` documentation](https://img.shields.io/badge/doc-RedBlackTreeSet-303070.svg)](./set/red_black_tree_set/struct.RedBlackTreeSet.html)
//!
//! A set implemented with a [`RedBlackTreeMap`](#redblacktreemap).
//!
//! #### Example
//!
//! ```rust
//! use rpds::RedBlackTreeSet;
//!
//! let set = RedBlackTreeSet::new()
//!     .insert("zero")
//!     .insert("one");
//!
//! assert!(set.contains(&"one"));
//!
//! let set_extended = set.insert("two");
//!
//! assert!(set_extended.contains(&"two"));
//!
//! let set_positive = set_extended.remove(&"zero");
//!
//! assert!(!set_positive.contains(&"zero"));
//!
//! assert_eq!(set_positive.first(), Some(&"one"));
//! ```
//!
//! ## Other Features
//!
//! ### Mutable Methods
//!
//! When you change a data structure you often do not need its previous versions.  For those cases
//! rpds offers you mutable methods which are generally faster:
//!
//! ```rust
//! use rpds::HashTrieSet;
//!
//! let mut set = HashTrieSet::new();
//!
//! set.insert_mut("zero");
//! set.insert_mut("one");
//!
//! let set_0_1 = set.clone();
//! let set_0_1_2 = set.insert("two");
//! ```
//!
//! ### Initialization Macros
//!
//! There are convenient initialization macros for all data structures:
//!
//! ```rust
//! use rpds::*;
//!
//! let vector = vector![3, 1, 4, 1, 5];
//! let map = ht_map!["orange" => "orange", "banana" => "yellow"];
//! ```
//!
//! Check the documentation for initialization macros of other data structures.
//!
//! ### Serialization
//!
//! We support serialization through [serde](https://crates.io/crates/serde).  To use it
//! enable the `serde` feature.  To do so change the rpds dependency in your `Cargo.toml` to
//!
//! ```toml
//! [dependencies]
//! rpds = { version = "<version>", features = ["serde"] }
//! ```

mod utils;
#[macro_use]
pub mod list;
pub mod map;
pub mod queue;
pub mod set;
pub mod stack;
pub mod vector;

pub use crate::list::List;
pub use crate::map::hash_trie_map::HashTrieMap;
pub use crate::map::red_black_tree_map::RedBlackTreeMap;
pub use crate::queue::Queue;
pub use crate::set::hash_trie_set::HashTrieSet;
pub use crate::set::red_black_tree_set::RedBlackTreeSet;
pub use crate::stack::Stack;
pub use crate::vector::Vector;
<commit_msg>Small change.<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/.
 */

#![cfg_attr(feature = "fatal-warnings", deny(warnings))]
#![deny(clippy::correctness)]
#![warn(clippy::pedantic)]
#![allow(clippy::match_bool)]
#![allow(clippy::if_not_else)]
#![allow(clippy::stutter)]
#![allow(clippy::similar_names)]
#![allow(clippy::use_self)]
#![allow(clippy::single_match_else)]
// Note: If you change this remember to update `README.md`.  To do so run `./tools/update-readme.sh`.
//! # Rust Persistent Data Structures
//!
//! Rust Persistent Data Structures provides [fully persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure)
//! with structural sharing.
//!
//! ## Setup
//!
//! To use rpds add the following to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rpds = "<version>"
//! ```
//!
//! ## Data Structures
//!
//! This crate offers the following data structures:
//!
//!   1. [`List`](#list)
//!   2. [`Vector`](#vector)
//!   3. [`Stack`](#stack)
//!   4. [`Queue`](#queue)
//!   5. [`HashTrieMap`](#hashtriemap)
//!   6. [`HashTrieSet`](#hashtrieset)
//!   7. [`RedBlackTreeMap`](#redblacktreemap)
//!   8. [`RedBlackTreeSet`](#redblacktreeset)
//!
//! ### `List`
//! [![List documentation](https://img.shields.io/badge/doc-List-303070.svg)](./list/struct.List.html)
//!
//! Your classic functional list.
//!
//! #### Example
//!
//! ```rust
//! use rpds::List;
//!
//! let list = List::new().push_front("list");
//!
//! assert_eq!(list.first(), Some(&"list"));
//!
//! let a_list = list.push_front("a");
//!
//! assert_eq!(a_list.first(), Some(&"a"));
//!
//! let list_dropped = a_list.drop_first().unwrap();
//!
//! assert_eq!(list_dropped, list);
//! ```
//!
//! ### `Vector`
//! [![`Vector` documentation](https://img.shields.io/badge/doc-Vector-303070.svg)](./vector/struct.Vector.html)
//!
//! A sequence that can be indexed.  The implementation is described in
//! [Understanding Persistent Vector Part 1](http://hypirion.com/musings/understanding-persistent-vector-pt-1)
//! and [Understanding Persistent Vector Part 2](http://hypirion.com/musings/understanding-persistent-vector-pt-2).
//!
//! #### Example
//!
//! ```rust
//! use rpds::Vector;
//!
//! let vector = Vector::new()
//!     .push_back("I’m")
//!     .push_back("a")
//!     .push_back("vector");
//!
//! assert_eq!(vector[1], "a");
//!
//! let screaming_vector = vector
//!     .drop_last().unwrap()
//!     .push_back("VECTOR!!!");
//!
//! assert_eq!(screaming_vector[2], "VECTOR!!!");
//! ```
//!
//! ### `Stack`
//! [![`Stack` documentation](https://img.shields.io/badge/doc-Stack-303070.svg)](./stack/struct.Stack.html)
//!
//! A LIFO (last in, first out) data structure.  This is just a [`List`](#list) in disguise.
//!
//! #### Example
//!
//! ```rust
//! use rpds::Stack;
//!
//! let stack = Stack::new().push("stack");
//!
//! assert_eq!(stack.peek(), Some(&"stack"));
//!
//! let a_stack = stack.push("a");
//!
//! assert_eq!(a_stack.peek(), Some(&"a"));
//!
//! let stack_popped = a_stack.pop().unwrap();
//!
//! assert_eq!(stack_popped, stack);
//! ```
//!
//! ### `Queue`
//! [![`Queue` documentation](https://img.shields.io/badge/doc-Queue-303070.svg)](./queue/struct.Queue.html)
//!
//! A FIFO (first in, first out) data structure.
//!
//! #### Example
//!
//! ```rust
//! use rpds::Queue;
//!
//! let queue = Queue::new()
//!     .enqueue("um")
//!     .enqueue("dois")
//!     .enqueue("tres");
//!
//! assert_eq!(queue.peek(), Some(&"um"));
//!
//! let queue_dequeued = queue.dequeue().unwrap();
//!
//! assert_eq!(queue_dequeued.peek(), Some(&"dois"));
//! ```
//!
//! ### `HashTrieMap`
//! [![`HashTrieMap` documentation](https://img.shields.io/badge/doc-HashTrieMap-303070.svg)](./map/hash_trie_map/struct.HashTrieMap.html)
//!
//! A map implemented with a [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie).
//! See [Ideal Hash Trees](https://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf) for
//! details.
//!
//! #### Example
//!
//! ```rust
//! use rpds::HashTrieMap;
//!
//! let map_en = HashTrieMap::new()
//!     .insert(0, "zero")
//!     .insert(1, "one");
//!
//! assert_eq!(map_en.get(&1), Some(&"one"));
//!
//! let map_pt = map_en
//!     .insert(1, "um")
//!     .insert(2, "dois");
//!
//! assert_eq!(map_pt.get(&2), Some(&"dois"));
//!
//! let map_pt_binary = map_pt.remove(&2);
//!
//! assert_eq!(map_pt_binary.get(&2), None);
//! ```
//!
//! ### `HashTrieSet`
//! [![`HashTrieSet` documentation](https://img.shields.io/badge/doc-HashTrieSet-303070.svg)](./set/hash_trie_set/struct.HashTrieSet.html)
//!
//! A set implemented with a [`HashTrieMap`](#hashtriemap).
//!
//! #### Example
//!
//! ```rust
//! use rpds::HashTrieSet;
//!
//! let set = HashTrieSet::new()
//!     .insert("zero")
//!     .insert("one");
//!
//! assert!(set.contains(&"one"));
//!
//! let set_extended = set.insert("two");
//!
//! assert!(set_extended.contains(&"two"));
//!
//! let set_positive = set_extended.remove(&"zero");
//!
//! assert!(!set_positive.contains(&"zero"));
//! ```
//!
//! ### `RedBlackTreeMap`
//! [![`RedBlackTreeMap` documentation](https://img.shields.io/badge/doc-RedBlackTreeMap-303070.svg)](./map/red_black_tree_map/struct.RedBlackTreeMap.html)
//!
//! A map implemented with a [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree).
//!
//! #### Example
//!
//! ```rust
//! use rpds::RedBlackTreeMap;
//!
//! let map_en = RedBlackTreeMap::new()
//!     .insert(0, "zero")
//!     .insert(1, "one");
//!
//! assert_eq!(map_en.get(&1), Some(&"one"));
//!
//! let map_pt = map_en
//!     .insert(1, "um")
//!     .insert(2, "dois");
//!
//! assert_eq!(map_pt.get(&2), Some(&"dois"));
//!
//! let map_pt_binary = map_pt.remove(&2);
//!
//! assert_eq!(map_pt_binary.get(&2), None);
//!
//! assert_eq!(map_pt_binary.first(), Some((&0, &"zero")));
//! ```
//!
//! ### `RedBlackTreeSet`
//! [![`RedBlackTreeSet` documentation](https://img.shields.io/badge/doc-RedBlackTreeSet-303070.svg)](./set/red_black_tree_set/struct.RedBlackTreeSet.html)
//!
//! A set implemented with a [`RedBlackTreeMap`](#redblacktreemap).
//!
//! #### Example
//!
//! ```rust
//! use rpds::RedBlackTreeSet;
//!
//! let set = RedBlackTreeSet::new()
//!     .insert("zero")
//!     .insert("one");
//!
//! assert!(set.contains(&"one"));
//!
//! let set_extended = set.insert("two");
//!
//! assert!(set_extended.contains(&"two"));
//!
//! let set_positive = set_extended.remove(&"zero");
//!
//! assert!(!set_positive.contains(&"zero"));
//!
//! assert_eq!(set_positive.first(), Some(&"one"));
//! ```
//!
//! ## Other Features
//!
//! ### Mutable Methods
//!
//! When you change a data structure you often do not need its previous versions.  For those cases
//! rpds offers you mutable methods which are generally faster:
//!
//! ```rust
//! use rpds::HashTrieSet;
//!
//! let mut set = HashTrieSet::new();
//!
//! set.insert_mut("zero");
//! set.insert_mut("one");
//!
//! let set_0_1 = set.clone();
//! let set_0_1_2 = set.insert("two");
//! ```
//!
//! ### Initialization Macros
//!
//! There are convenient initialization macros for all data structures:
//!
//! ```rust
//! use rpds::*;
//!
//! let vector = vector![3, 1, 4, 1, 5];
//! let map = ht_map!["orange" => "orange", "banana" => "yellow"];
//! ```
//!
//! Check the documentation for initialization macros of other data structures.
//!
//! ### Serialization
//!
//! We support serialization through [serde](https://crates.io/crates/serde).  To use it
//! enable the `serde` feature.  To do so change the rpds dependency in your `Cargo.toml` to
//!
//! ```toml
//! [dependencies]
//! rpds = { version = "<version>", features = ["serde"] }
//! ```

mod utils;
#[macro_use]
pub mod list;
pub mod map;
pub mod queue;
pub mod set;
pub mod stack;
pub mod vector;

pub use crate::list::List;
pub use crate::map::hash_trie_map::HashTrieMap;
pub use crate::map::red_black_tree_map::RedBlackTreeMap;
pub use crate::queue::Queue;
pub use crate::set::hash_trie_set::HashTrieSet;
pub use crate::set::red_black_tree_set::RedBlackTreeSet;
pub use crate::stack::Stack;
pub use crate::vector::Vector;
<|endoftext|>


<commit_before>//! # Dynamic parsing within trees 🌲 🌳 🌴
//!
//! Target of this library is to provide a flexible approach in parsing data. This will mainly be
//! done within [arena](https://en.wikipedia.org/wiki/Region-based_memory_management) based
//! [parser trees](https://en.wikipedia.org/wiki/Parse_tree) which can be modified during runtime.
//! Every parser is using the [nom](https://github.com/Geal/nom) framework for the actual parsing
//! work. A complete source code example can be found within the
//! [`src/example`](https://github.com/saschagrunert/peel/tree/master/src/example) directory of the
//! crate.
#![deny(missing_docs)]

#[macro_use]
extern crate nom;

#[macro_use]
extern crate log;
extern crate indextree;
extern crate mowl;

#[macro_use]
pub mod error;
pub mod parser;
pub mod example;

use std::{iter, fmt};
use std::collections::HashMap;
use log::LogLevel;
use indextree::{Arena, NodeId};
use nom::{IResult, generate_colors, prepare_errors, print_codes, print_offsets};

use prelude::*;
use parser::ParserBox;

/// Provides sensible imports at all
pub mod prelude {
    pub use super::Peel;
    pub use error::{PeelResult, PeelError, ErrorType};
    pub use parser::{Parser, ParserNode, ParserArena, ParserState};
}

/// The main peeling structure
pub struct Peel<R, V> {
    /// The memory arena of the tree
    pub arena: Arena<ParserBox<R, V>>,

    /// The first node added will be the root
    pub root: Option<NodeId>,
}

impl<R, V> Peel<R, V> where V: fmt::Display {
    /// Create a new empty `Peel` instance
    pub fn new() -> Self {
        Peel {
            arena: Arena::new(),
            root: None,
        }
    }

    /// Set the global log level for reporting
    pub fn set_log_level(&mut self, level: LogLevel) {
        // Setup the logger if not already set
        if mowl::init_with_level(level).is_err() {
            warn!("Logger already set.");
        };
        info!("Log level set to: {:?}", level);
    }

    /// Create a new boxed Parser and return a corresponding Node
    pub fn new_parser<T>(&mut self, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_node = self.arena.new_node(Box::new(parser));

        // Check if the root node is already set. If not, then this will be the root
        if let None = self.root {
            self.root = Some(new_node);
        }

        // Return the shiny new node
        new_node
    }

    /// Append the second node to the first one within the current tree structure
    pub fn link(&mut self, left: NodeId, right: NodeId) {
        left.append(right, &mut self.arena);
    }

    /// Create a new parser and link it with the provided node
    pub fn link_new_parser<T>(&mut self, left: NodeId, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_parser = self.new_parser(parser);

        // Append the node to the given node
        left.append(new_parser, &mut self.arena);

        // Return the parser
        new_parser
    }

    /// Convenient function for recursive traversal with the root as starting point
    ///
    /// # Errors
    /// When no tree root was found or the first parser already fails.
    pub fn traverse(&self, input: &[u8], result: Vec<R>) -> PeelResult<Vec<R>> {
        match self.root {
            Some(node) => self.traverse_recursive(node, input, result),
            None => bail!(ErrorType::NoTreeRoot, "No tree root found"),
        }
    }

    /// Do parsing until all possible paths failed. This is equivalent in finding the deepest
    /// possible parsing result within the tree. The result will be assembled together in the
    /// given result vector, which will be returned at the end.
    ///
    /// # Errors
    /// When the first parser already fails.
    pub fn traverse_recursive(&self, start_node: NodeId, input: &[u8], mut result: Vec<R>)
        -> PeelResult<Vec<R>> {

        for node_id in start_node.following_siblings(&self.arena) {
            // Get the initial values from the arena
            let ref node = self.arena[node_id];
            let ref parser = node.data;

            // Do the actual parsing work
            match parser.parse(input, Some(node), Some(&self.arena), Some(&result)) {
                IResult::Done(input_left, (parser_result, state)) => {
                    // Adapt the result
                    debug!("{} parsing succeed, left input length: {}",
                           parser.variant(), input_left.len());
                    result.push(parser_result);

                    // Check the parser state
                    let next_node = match state {
                        // Continue with the first child parser
                         ParserState::ContinueWithFirstChild => {
                            debug!("Continue traversal to first child of the parser");
                            node.first_child()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithNextSibling => {
                            debug!("Continue traversal to next sibling of the parser");
                            node.next_sibling()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithCurrent => {
                            debug!("Trying the current parser again");
                            Some(node_id)
                         },

                         // Immediately stop the parser here
                         ParserState::Stop => None,
                    };

                    // Continue traversal if needed
                    if let Some(node) = next_node {
                        result = self.traverse_recursive(node, input_left, result)?;
                    }

                    // Stop here since we already succeed
                    break;
                }
                error @ _ => if log_enabled!(log::LogLevel::Trace) {
                    if Some(start_node) == self.root {
                        bail!(ErrorType::RootParserFailed, "No parser succeed at all");
                    }
                    debug!("Failed parser: {}", parser.variant());
                    self.display_error(input, error);
                },
            }
        }
        Ok(result)
    }

    /// Display the trees children by recursive iteration
    fn display_children(&self, f: &mut fmt::Formatter, node: NodeId, mut level: usize)
        -> fmt::Result {
        level += 2;
        for child in node.children(&self.arena) {
            let indent = iter::repeat(' ').take(level).collect::<String>();
            let ref parser = self.arena[child].data;
            write!(f, "{}- {}: {:?}", indent, parser.variant(), child)?;
            writeln!(f, " ({})", self.arena[child])?;
            self.display_children(f, child, level)?;
        }
        Ok(())
    }

    /// Display an error from a parser
    pub fn display_error(&self, input: &[u8], res: IResult<&[u8], (R, ParserState)>) {
        let mut h: HashMap<u32, &str> = HashMap::new();
        let parsers = ["Custom", "Tag", "MapRes", "MapOpt", "Alt", "IsNot", "IsA", "SeparatedList",
                       "SeparatedNonEmptyList", "Many1", "Count", "TakeUntilAndConsume",
                       "TakeUntil", "TakeUntilEitherAndConsume", "TakeUntilEither", "LengthValue",
                       "TagClosure", "Alpha", "Digit", "AlphaNumeric", "Space", "MultiSpace",
                       "LengthValueFn", "Eof", "ExprOpt", "ExprRes", "CondReduce", "Switch",
                       "TagBits", "OneOf", "NoneOf", "Char", "CrLf", "RegexpMatch",
                       "RegexpMatches", "RegexpFind", "RegexpCapture", "RegexpCaptures",
                       "TakeWhile1", "Complete", "Fix", "Escaped", "EscapedTransform", "TagStr",
                       "IsNotStr", "IsAStr", "TakeWhile1Str", "NonEmpty", "ManyMN",
                       "TakeUntilAndConsumeStr", "HexDigit", "TakeUntilStr", "OctDigit", "Many0",
                       "Not", "Permutation", "ManyTill"];

        for (i, literal) in parsers.iter().enumerate() {
            h.insert(i as u32, literal);
        }

        if let Some(v) = prepare_errors(input, res) {
            let colors = generate_colors(&v);
            println!("Colors: {}", print_codes(colors, h));
            println!("Dump: \n{}",   print_offsets(input, 0, &v));
        }
    }
}

impl<K, V> fmt::Display for Peel<K, V> where V: fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.root {
            Some(node) => {
                write!(f, "- {}: {:?}", self.arena[node].data.variant(), node)?;
                writeln!(f, " ({})", self.arena[node])?;
                self.display_children(f, node, 0)
            }
            None => write!(f, "(no tree root available)"),
        }
    }
}
<commit_msg>Fixed clippy reportings<commit_after>//! # Dynamic parsing within trees 🌲 🌳 🌴
//!
//! Target of this library is to provide a flexible approach in parsing data. This will mainly be
//! done within [arena](https://en.wikipedia.org/wiki/Region-based_memory_management) based
//! [parser trees](https://en.wikipedia.org/wiki/Parse_tree) which can be modified during runtime.
//! Every parser is using the [nom](https://github.com/Geal/nom) framework for the actual parsing
//! work. A complete source code example can be found within the
//! [`src/example`](https://github.com/saschagrunert/peel/tree/master/src/example) directory of the
//! crate.
#![deny(missing_docs)]

#[macro_use]
extern crate nom;

#[macro_use]
extern crate log;
extern crate indextree;
extern crate mowl;

#[macro_use]
pub mod error;
pub mod parser;
pub mod example;

use std::{iter, fmt};
use std::collections::HashMap;
use log::LogLevel;
use indextree::{Arena, NodeId};
use nom::{IResult, generate_colors, prepare_errors, print_codes, print_offsets};

use prelude::*;
use parser::ParserBox;

/// Provides sensible imports at all
pub mod prelude {
    pub use super::Peel;
    pub use error::{PeelResult, PeelError, ErrorType};
    pub use parser::{Parser, ParserNode, ParserArena, ParserState};
}

/// The main peeling structure
pub struct Peel<R, V> {
    /// The memory arena of the tree
    pub arena: Arena<ParserBox<R, V>>,

    /// The first node added will be the root
    pub root: Option<NodeId>,
}

impl<R, V> Peel<R, V> where V: fmt::Display {
    /// Create a new empty `Peel` instance
    pub fn new() -> Self {
        Peel {
            arena: Arena::new(),
            root: None,
        }
    }

    /// Set the global log level for reporting
    pub fn set_log_level(&mut self, level: LogLevel) {
        // Setup the logger if not already set
        if mowl::init_with_level(level).is_err() {
            warn!("Logger already set.");
        };
        info!("Log level set to: {:?}", level);
    }

    /// Create a new boxed Parser and return a corresponding Node
    pub fn new_parser<T>(&mut self, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_node = self.arena.new_node(Box::new(parser));

        // Check if the root node is already set. If not, then this will be the root
        if self.root.is_none() {
            self.root = Some(new_node);
        }

        // Return the shiny new node
        new_node
    }

    /// Append the second node to the first one within the current tree structure
    pub fn link(&mut self, left: NodeId, right: NodeId) {
        left.append(right, &mut self.arena);
    }

    /// Create a new parser and link it with the provided node
    pub fn link_new_parser<T>(&mut self, left: NodeId, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_parser = self.new_parser(parser);

        // Append the node to the given node
        left.append(new_parser, &mut self.arena);

        // Return the parser
        new_parser
    }

    /// Convenient function for recursive traversal with the root as starting point
    ///
    /// # Errors
    /// When no tree root was found or the first parser already fails.
    pub fn traverse(&self, input: &[u8], result: Vec<R>) -> PeelResult<Vec<R>> {
        match self.root {
            Some(node) => self.traverse_recursive(node, input, result),
            None => bail!(ErrorType::NoTreeRoot, "No tree root found"),
        }
    }

    /// Do parsing until all possible paths failed. This is equivalent in finding the deepest
    /// possible parsing result within the tree. The result will be assembled together in the
    /// given result vector, which will be returned at the end.
    ///
    /// # Errors
    /// When the first parser already fails.
    pub fn traverse_recursive(&self, start_node: NodeId, input: &[u8], mut result: Vec<R>)
        -> PeelResult<Vec<R>> {

        for node_id in start_node.following_siblings(&self.arena) {
            // Get the initial values from the arena
            let node = &self.arena[node_id];
            let parser = &node.data;

            // Do the actual parsing work
            match parser.parse(input, Some(node), Some(&self.arena), Some(&result)) {
                IResult::Done(input_left, (parser_result, state)) => {
                    // Adapt the result
                    debug!("{} parsing succeed, left input length: {}",
                           parser.variant(), input_left.len());
                    result.push(parser_result);

                    // Check the parser state
                    let next_node = match state {
                        // Continue with the first child parser
                         ParserState::ContinueWithFirstChild => {
                            debug!("Continue traversal to first child of the parser");
                            node.first_child()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithNextSibling => {
                            debug!("Continue traversal to next sibling of the parser");
                            node.next_sibling()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithCurrent => {
                            debug!("Trying the current parser again");
                            Some(node_id)
                         },

                         // Immediately stop the parser here
                         ParserState::Stop => None,
                    };

                    // Continue traversal if needed
                    if let Some(node) = next_node {
                        result = self.traverse_recursive(node, input_left, result)?;
                    }

                    // Stop here since we already succeed
                    break;
                }
                error => if log_enabled!(log::LogLevel::Trace) {
                    if Some(start_node) == self.root {
                        bail!(ErrorType::RootParserFailed, "No parser succeed at all");
                    }
                    debug!("Failed parser: {}", parser.variant());
                    self.display_error(input, error);
                },
            }
        }
        Ok(result)
    }

    /// Display the trees children by recursive iteration
    fn display_children(&self, f: &mut fmt::Formatter, node: NodeId, mut level: usize)
        -> fmt::Result {
        level += 2;
        for child in node.children(&self.arena) {
            let indent = iter::repeat(' ').take(level).collect::<String>();
            let parser = &self.arena[child].data;
            write!(f, "{}- {}: {:?}", indent, parser.variant(), child)?;
            writeln!(f, " ({})", self.arena[child])?;
            self.display_children(f, child, level)?;
        }
        Ok(())
    }

    /// Display an error from a parser
    pub fn display_error(&self, input: &[u8], res: IResult<&[u8], (R, ParserState)>) {
        let mut h: HashMap<u32, &str> = HashMap::new();
        let parsers = ["Custom", "Tag", "MapRes", "MapOpt", "Alt", "IsNot", "IsA", "SeparatedList",
                       "SeparatedNonEmptyList", "Many1", "Count", "TakeUntilAndConsume",
                       "TakeUntil", "TakeUntilEitherAndConsume", "TakeUntilEither", "LengthValue",
                       "TagClosure", "Alpha", "Digit", "AlphaNumeric", "Space", "MultiSpace",
                       "LengthValueFn", "Eof", "ExprOpt", "ExprRes", "CondReduce", "Switch",
                       "TagBits", "OneOf", "NoneOf", "Char", "CrLf", "RegexpMatch",
                       "RegexpMatches", "RegexpFind", "RegexpCapture", "RegexpCaptures",
                       "TakeWhile1", "Complete", "Fix", "Escaped", "EscapedTransform", "TagStr",
                       "IsNotStr", "IsAStr", "TakeWhile1Str", "NonEmpty", "ManyMN",
                       "TakeUntilAndConsumeStr", "HexDigit", "TakeUntilStr", "OctDigit", "Many0",
                       "Not", "Permutation", "ManyTill"];

        for (i, literal) in parsers.iter().enumerate() {
            h.insert(i as u32, literal);
        }

        if let Some(v) = prepare_errors(input, res) {
            let colors = generate_colors(&v);
            println!("Colors: {}", print_codes(colors, h));
            println!("Dump: \n{}",   print_offsets(input, 0, &v));
        }
    }
}

impl<K, V> fmt::Display for Peel<K, V> where V: fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.root {
            Some(node) => {
                write!(f, "- {}: {:?}", self.arena[node].data.variant(), node)?;
                writeln!(f, " ({})", self.arena[node])?;
                self.display_children(f, node, 0)
            }
            None => write!(f, "(no tree root available)"),
        }
    }
}
<|endoftext|>


<commit_before>extern crate atom;

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::mem;
use atom::Atom;

pub use select::Select;
mod select;

/// Drop rules
/// This may be freed iff state is Pulsed | Dropped
/// and Waiting is Dropped
struct Inner {
    state: AtomicUsize,
    waiting: Atom<Waiting, Box<Waiting>>
}

const PULSED: usize = 1;
const TX_DROPPED: usize = 2;
const RX_DROPPED: usize = 4;

pub enum Waiting {
    Thread(thread::Thread),
    Select(select::Handle)
}

impl Waiting {
    fn trigger(s: Box<Self>, id: usize) {
        match *s {
            Waiting::Thread(thread) => thread.unpark(),
            Waiting::Select(select) => {
                let trigger = {
                    let mut guard = select.0.lock().unwrap();
                    guard.ready.push(id);
                    guard.trigger.take()
                };
                trigger.map(|x| x.pulse());
            }
        }
    }

    pub fn thread() -> Waiting {
        Waiting::Thread(thread::current())
    }
}

pub struct Trigger {
    pulsed: bool,
    inner: Arc<Inner>
}

impl Drop for Trigger {
    fn drop(&mut self) {
        self.set(TX_DROPPED);
        if !self.pulsed {
            self.wake();
        }
    }
}

impl Trigger {
    fn set(&self, state: usize) {
        self.inner.state.fetch_or(state, Ordering::Relaxed);
    }

    fn wake(&self) {
        let id = unsafe { mem::transmute_copy(&self.inner) };
        match self.inner.waiting.take(Ordering::Acquire) {
            None => (),
            Some(v) => Waiting::trigger(v, id)
        }
    }

    /// Trigger an pulse, this can only occure once
    /// Returns true if this triggering triggered the pulse
    pub fn pulse(mut self) {
        self.set(PULSED);
        self.wake();
        self.pulsed = true;
    }
}

pub struct Pulse(Arc<Inner>);

impl Pulse {
    pub fn new() -> (Pulse, Trigger) {
        let inner = Arc::new(Inner {
            state: AtomicUsize::new(0),
            waiting: Atom::empty()
        });

        (Pulse(inner.clone()),
         Trigger {
            inner: inner,
            pulsed: false
        })
    }

    // Read out the state of the Pulse
    fn state(&self) -> usize {
        self.0.state.load(Ordering::Relaxed)
    }

    // Check to see if the pulse is pending or not
    pub fn is_pending(&self) -> bool {
        self.state() == 0
    }

    /// Arm a pulse to wake
    pub fn arm(&self, waiter: Box<Waiting>) {
        let old = self.0.waiting.swap(
            waiter,
            Ordering::AcqRel
        );

        if old.is_some() {
            panic!("Pulse cannot be waited on by multiple clients");
        }
    }

    /// Disarm a pulse
    pub fn disarm(&self) {
        self.0.waiting.take(Ordering::Acquire);
    }

    /// Wait for an pulse to be triggered
    ///
    /// Panics if something else is waiting on this already
    pub fn wait(&self) -> Result<(), WaitError> {
        loop {
            if !self.is_pending() {
                let state = self.state();
                return if (state & PULSED) == PULSED {
                    Ok(())
                } else {
                    Err(WaitError(state))
                };
            }

            self.arm(Box::new(Waiting::thread()));

            if self.is_pending() {
                // wait for wake
                thread::park();
            }
            self.disarm();
        }
    }

    pub fn id(&self) -> usize {
        unsafe { mem::transmute_copy(&self.0) }
    }

    pub fn recycle(&self) -> Trigger {
        if self.is_pending() {
            panic!("Attempted to recycle pending Pulse")
        }
        self.0.state.store(0, Ordering::Relaxed);
        Trigger {
            pulsed: false,
            inner: self.0.clone()
        }
    }
}

#[derive(Debug)]
pub struct WaitError(usize);<commit_msg>Remove Arc to speed up set/clear speed<commit_after>extern crate atom;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::mem;
use std::ptr;
use atom::Atom;

pub use select::Select;
mod select;

/// Drop rules
/// This may be freed iff state is Pulsed | Dropped
/// and Waiting is Dropped
struct Inner {
    state: AtomicUsize,
    waiting: Atom<Waiting, Box<Waiting>>
}

const PULSED: usize = 1;
const TX_DROPPED: usize = 2;
const RX_DROPPED: usize = 4;

pub enum Waiting {
    Thread(thread::Thread),
    Select(select::Handle)
}

impl Waiting {
    fn trigger(s: Box<Self>, id: usize) {
        match *s {
            Waiting::Thread(thread) => thread.unpark(),
            Waiting::Select(select) => {
                let trigger = {
                    let mut guard = select.0.lock().unwrap();
                    guard.ready.push(id);
                    guard.trigger.take()
                };
                trigger.map(|x| x.pulse());
            }
        }
    }

    pub fn thread() -> Waiting {
        Waiting::Thread(thread::current())
    }
}

unsafe impl Send for Trigger {}

pub struct Trigger(*mut Inner);

impl Drop for Trigger {
    fn drop(&mut self) {
        let flag = self.set(TX_DROPPED);
        self.wake();
        self.free(flag);
    }
}

impl Trigger {
    fn free(&mut self, flag: usize) {
        if (flag & RX_DROPPED) == RX_DROPPED {
            let inner: Box<Inner> = unsafe {
                mem::transmute(self.0)
            };
            drop(inner);
        }
        self.0 = ptr::null_mut();
    }

    fn inner(&self) -> &Inner {
        unsafe { mem::transmute(self.0) }
    }

    fn set(&self, state: usize) -> usize {
        self.inner().state.fetch_or(state, Ordering::Relaxed)
    }

    fn wake(&self) {
        let id = unsafe { mem::transmute(self.0) };
        match self.inner().waiting.take(Ordering::Acquire) {
            None => (),
            Some(v) => Waiting::trigger(v, id)
        }
    }

    /// Trigger an pulse, this can only occure once
    /// Returns true if this triggering triggered the pulse
    pub fn pulse(mut self) {
        let flag = self.set(PULSED | TX_DROPPED);
        self.wake();

        // We manually dropped ourselves to avoid to overhead
        // of a third atomic operation
        self.free(flag);
        unsafe {mem::forget(self)};
    }
}

unsafe impl Send for Pulse {}

pub struct Pulse(*mut Inner);

impl Drop for Pulse {
    fn drop(&mut self) {
        let flag = self.inner().state.fetch_or(RX_DROPPED, Ordering::Relaxed);
        if (flag & TX_DROPPED) == TX_DROPPED {
            let inner: Box<Inner> = unsafe {
                mem::transmute(self.0)
            };
            drop(inner);
        }
    }
}

impl Pulse {
    pub fn new() -> (Pulse, Trigger) {
        let inner = Box::new(Inner {
            state: AtomicUsize::new(0),
            waiting: Atom::empty()
        });

        let inner = unsafe {mem::transmute(inner)};

        (Pulse(inner), Trigger(inner))
    }

    fn inner(&self) -> &Inner {
        unsafe { mem::transmute(self.0) }
    }

    // Read out the state of the Pulse
    fn state(&self) -> usize {
        self.inner().state.load(Ordering::Relaxed)
    }

    // Check to see if the pulse is pending or not
    pub fn is_pending(&self) -> bool {
        self.state() == 0
    }

    /// Arm a pulse to wake
    pub fn arm(&self, waiter: Box<Waiting>) {
        let old = self.inner().waiting.swap(
            waiter,
            Ordering::AcqRel
        );

        if old.is_some() {
            panic!("Pulse cannot be waited on by multiple clients");
        }
    }

    /// Disarm a pulse
    pub fn disarm(&self) {
        self.inner().waiting.take(Ordering::Acquire);
    }

    /// Wait for an pulse to be triggered
    ///
    /// Panics if something else is waiting on this already
    pub fn wait(&self) -> Result<(), WaitError> {
        loop {
            if !self.is_pending() {
                let state = self.state();
                return if (state & PULSED) == PULSED {
                    Ok(())
                } else {
                    Err(WaitError(state))
                };
            }

            self.arm(Box::new(Waiting::thread()));

            if self.is_pending() {
                // wait for wake
                thread::park();
            }
            self.disarm();
        }
    }

    pub fn id(&self) -> usize {
        unsafe { mem::transmute_copy(&self.0) }
    }

    pub fn recycle(&self) -> Trigger {
        if self.is_pending() {
            panic!("Attempted to recycle pending Pulse")
        }
        self.inner().state.store(0, Ordering::Relaxed);
        Trigger(self.0)
    }
}

#[derive(Debug)]
pub struct WaitError(usize);<|endoftext|>


<commit_before>extern crate libc;
/*
 * seccomp actions
 */
#[allow(non_camel_case_types)]
pub type scmp_filter_ctx = libc::c_void;

/**
 * Kill the process
 */
pub const SCMP_ACT_KILL: u32  = 0x00000000;
/**
 * Throw a SIGSYS signal
 */
pub const SCMP_ACT_TRAP: u32 = 0x00030000;
/**
 * Return the specified error code
 */
#[allow(non_snake_case)]
pub fn SCMP_ACT_ERRNO(x: u32) -> u32 { 0x00050000 | ((x) & 0x0000ffff) }
/**
 * Notify a tracing process with the specified value
 */
#[allow(non_snake_case)]
pub fn SCMP_ACT_TRACE(x: u32) -> u32 { 0x7ff00000 | ((x) & 0x0000ffff) }
/**
 * Allow the syscall to be executed
 */
pub const SCMP_ACT_ALLOW: u32 = 0x7fff0000;

/**
 * Filter attributes
 */
#[derive(Debug)]
#[repr(C)]
pub enum scmp_filter_attr {
    _SCMP_FLTATR_MIN,
    SCMP_FLTATR_ACT_DEFAULT, /** default filter action */
    SCMP_FLTATR_ACT_BADARCH, /** bad architecture action */
    SCMP_FLTATR_CTL_NNP, /** set NO_NEW_PRIVS on filter load */
    _SCMP_FLTATR_MAX,
}

/**
 * Comparison operators
 */
#[derive(Debug)]
#[repr(C)]
pub enum scmp_compare {
        _SCMP_CMP_MIN = 0,
        SCMP_CMP_NE = 1,                /** not equal */
        SCMP_CMP_LT = 2,                /** less than */
        SCMP_CMP_LE = 3,                /** less than or equal */
        SCMP_CMP_EQ = 4,                /** equal */
        SCMP_CMP_GE = 5,                /** greater than or equal */
        SCMP_CMP_GT = 6,                /** greater than */
        SCMP_CMP_MASKED_EQ = 7,         /** masked equality */
        _SCMP_CMP_MAX,
}

/**
 * Argument datum
 */
#[allow(non_camel_case_types)]
pub type scmp_datum_t = libc::uint64_t;

/**
 * Argument / Value comparison definition
 */
#[derive(Debug)]
#[repr(C)]
pub struct scmp_arg_cmp {
        pub arg: libc::c_uint,        /** argument number, starting at 0 */
        pub op: scmp_compare,       /** the comparison op, e.g. SCMP_CMP_* */
        pub datum_a: scmp_datum_t,
        pub datum_b: scmp_datum_t,
}

#[link(name = "seccomp")]
extern {
    /**
     * Initialize the filter state
     *
     * @param def_action the default filter action
     *
     * This function initializes the internal seccomp filter state and should
     * be called before any other functions in this library to ensure the filter
     * state is initialized.  Returns a filter context on success, NULL on failure.
     *
     */
    pub fn seccomp_init(def_action: libc::uint32_t) -> *mut scmp_filter_ctx;
    /**
     * Reset the filter state
     *
     * @param ctx the filter context
     * @param def_action the default filter action
     *
     * This function resets the given seccomp filter state and ensures the
     * filter state is reinitialized.  This function does not reset any seccomp
     * filters already loaded into the kernel.  Returns zero on success, negative
     * values on failure.
     *
     */
    pub fn seccomp_reset(ctx: *mut scmp_filter_ctx, def_action: libc::uint32_t) -> libc::c_int;
    /**
     * Destroys the filter state and releases any resources
     *
     * @param ctx the filter context
     *
     * This functions destroys the given seccomp filter state and releases any
     * resources, including memory, associated with the filter state.  This
     * function does not reset any seccomp filters already loaded into the kernel.
     * The filter context can no longer be used after calling this function.
     *
     */
    pub fn seccomp_release(ctx: *mut scmp_filter_ctx);
    /**
     * Loads the filter into the kernel
     *
     * @param ctx the filter context
     *
     * This function loads the given seccomp filter context into the kernel.  If
     * the filter was loaded correctly, the kernel will be enforcing the filter
     * when this function returns.  Returns zero on success, negative values on
     * error.
     *
     */
    pub fn seccomp_load(ctx: *const scmp_filter_ctx) -> libc::c_int;

    /**
     * Get the value of a filter attribute
     *
     * @param ctx the filter context
     * @param attr the filter attribute name
     * @param value the filter attribute value
     *
     * This function fetches the value of the given attribute name and returns it
     * via @value.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_attr_get(ctx: *const scmp_filter_ctx,
                         attr: scmp_filter_attr, value: *mut libc::uint32_t) -> libc::c_int;

    /**
     * Set the value of a filter attribute
     *
     * @param ctx the filter context
     * @param attr the filter attribute name
     * @param value the filter attribute value
     *
     * This function sets the value of the given attribute.  Returns zero on
     * success, negative values on failure.
     *
     */
    pub fn seccomp_attr_set(ctx: *mut scmp_filter_ctx,
                         attr: scmp_filter_attr, value: libc::uint32_t) -> libc::c_int;

    /**
     * Set the priority of a given syscall
     *
     * @param ctx the filter context
     * @param syscall the syscall number
     * @param priority priority value, higher value == higher priority
     *
     * This function sets the priority of the given syscall; this value is used
     * when generating the seccomp filter code such that higher priority syscalls
     * will incur less filter code overhead than the lower priority syscalls in the
     * filter.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_syscall_priority(ctx: *mut scmp_filter_ctx,
                                 syscall: libc::c_int, priority: libc::uint8_t) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt the number of argument filters in the argument filter chain
     * @param ... scmp_arg_cmp structs (use of SCMP_ARG_CMP() recommended)
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule needs to be adjusted due to architecture specifics it
     * will be adjusted without notification.  Returns zero on success, negative
     * values on failure.
     *
     */
    pub fn seccomp_rule_add(ctx: *mut scmp_filter_ctx,
                         action: libc::uint32_t, syscall: libc::c_int, arg_cnt: libc::c_uint, ...) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt the number of elements in the arg_array parameter
     * @param arg_array array of scmp_arg_cmp structs
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule needs to be adjusted due to architecture specifics it
     * will be adjusted without notification.  Returns zero on success, negative
     * values on failure.
     *
     */
    pub fn seccomp_rule_add_array(ctx: *mut scmp_filter_ctx,
        action: libc::uint32_t, syscall: libc::c_int, arg_cnt: libc::c_uint,
        arg_array: *const scmp_arg_cmp) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt the number of argument filters in the argument filter chain
     * @param ... scmp_arg_cmp structs (use of SCMP_ARG_CMP() recommended)
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule can not be represented on the architecture the
     * function will fail.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_rule_add_exact(ctx: *mut scmp_filter_ctx, action: libc::uint32_t,
                               syscall: libc::c_int, arg_cnt: libc::c_uint, ...) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt  the number of elements in the arg_array parameter
     * @param arg_array array of scmp_arg_cmp structs
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule can not be represented on the architecture the
     * function will fail.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_rule_add_exact_array(ctx: *mut scmp_filter_ctx,
                                        action: libc::uint32_t, syscall: libc::c_int, arg_cnt: libc::c_uint,
                                        arg_array: *const scmp_arg_cmp) -> libc::c_int;

    /**
     * Generate seccomp Pseudo Filter Code (PFC) and export it to a file
     *
     * @param ctx the filter context
     * @param fd the destination fd
     *
     * This function generates seccomp Pseudo Filter Code (PFC) and writes it to
     * the given fd.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_export_pfc(ctx: *const scmp_filter_ctx, fd: libc::c_int) -> libc::c_int;

    /**
     * Generate seccomp Berkley Packet Filter (BPF) code and export it to a file
     *
     * @param ctx the filter context
     * @param fd the destination fd
     *
     * This function generates seccomp Berkley Packer Filter (BPF) code and writes
     * it to the given fd.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_export_bpf(ctx: *const scmp_filter_ctx, fd: libc::c_int) -> libc::c_int;
}

#[test]
fn does_it_even_work() {
    unsafe {
        let context = seccomp_init(SCMP_ACT_ALLOW);
        let comparator = scmp_arg_cmp {
            arg: 0,
            op: scmp_compare::SCMP_CMP_EQ,
            datum_a: 1000,
            datum_b: 0,
        };
        assert!(seccomp_rule_add(context, SCMP_ACT_KILL, 105, 1, comparator) == 0);
        assert!(seccomp_load(context) == 0);
        //assert!(libc::setuid(1000) == 0);
    }
}
<commit_msg>add seccomp_syscall_resolve_name<commit_after>extern crate libc;
/*
 * seccomp actions
 */
#[allow(non_camel_case_types)]
pub type scmp_filter_ctx = libc::c_void;

/**
 * Error retern value
 */
pub const __NR_SCMP_ERROR: libc::c_int = -1;

/**
 * Kill the process
 */
pub const SCMP_ACT_KILL: u32  = 0x00000000;
/**
 * Throw a SIGSYS signal
 */
pub const SCMP_ACT_TRAP: u32 = 0x00030000;
/**
 * Return the specified error code
 */
#[allow(non_snake_case)]
pub fn SCMP_ACT_ERRNO(x: u32) -> u32 { 0x00050000 | ((x) & 0x0000ffff) }
/**
 * Notify a tracing process with the specified value
 */
#[allow(non_snake_case)]
pub fn SCMP_ACT_TRACE(x: u32) -> u32 { 0x7ff00000 | ((x) & 0x0000ffff) }
/**
 * Allow the syscall to be executed
 */
pub const SCMP_ACT_ALLOW: u32 = 0x7fff0000;

/**
 * Filter attributes
 */
#[derive(Debug)]
#[repr(C)]
pub enum scmp_filter_attr {
    _SCMP_FLTATR_MIN,
    SCMP_FLTATR_ACT_DEFAULT, /** default filter action */
    SCMP_FLTATR_ACT_BADARCH, /** bad architecture action */
    SCMP_FLTATR_CTL_NNP, /** set NO_NEW_PRIVS on filter load */
    _SCMP_FLTATR_MAX,
}

/**
 * Comparison operators
 */
#[derive(Debug)]
#[repr(C)]
pub enum scmp_compare {
        _SCMP_CMP_MIN = 0,
        SCMP_CMP_NE = 1,                /** not equal */
        SCMP_CMP_LT = 2,                /** less than */
        SCMP_CMP_LE = 3,                /** less than or equal */
        SCMP_CMP_EQ = 4,                /** equal */
        SCMP_CMP_GE = 5,                /** greater than or equal */
        SCMP_CMP_GT = 6,                /** greater than */
        SCMP_CMP_MASKED_EQ = 7,         /** masked equality */
        _SCMP_CMP_MAX,
}

/**
 * Argument datum
 */
#[allow(non_camel_case_types)]
pub type scmp_datum_t = libc::uint64_t;

/**
 * Argument / Value comparison definition
 */
#[derive(Debug)]
#[repr(C)]
pub struct scmp_arg_cmp {
        pub arg: libc::c_uint,        /** argument number, starting at 0 */
        pub op: scmp_compare,       /** the comparison op, e.g. SCMP_CMP_* */
        pub datum_a: scmp_datum_t,
        pub datum_b: scmp_datum_t,
}

#[link(name = "seccomp")]
extern {
    /**
     * Initialize the filter state
     *
     * @param def_action the default filter action
     *
     * This function initializes the internal seccomp filter state and should
     * be called before any other functions in this library to ensure the filter
     * state is initialized.  Returns a filter context on success, NULL on failure.
     *
     */
    pub fn seccomp_init(def_action: libc::uint32_t) -> *mut scmp_filter_ctx;
    /**
     * Reset the filter state
     *
     * @param ctx the filter context
     * @param def_action the default filter action
     *
     * This function resets the given seccomp filter state and ensures the
     * filter state is reinitialized.  This function does not reset any seccomp
     * filters already loaded into the kernel.  Returns zero on success, negative
     * values on failure.
     *
     */
    pub fn seccomp_reset(ctx: *mut scmp_filter_ctx, def_action: libc::uint32_t) -> libc::c_int;
    /**
     * Destroys the filter state and releases any resources
     *
     * @param ctx the filter context
     *
     * This functions destroys the given seccomp filter state and releases any
     * resources, including memory, associated with the filter state.  This
     * function does not reset any seccomp filters already loaded into the kernel.
     * The filter context can no longer be used after calling this function.
     *
     */
    pub fn seccomp_release(ctx: *mut scmp_filter_ctx);
    /**
     * Loads the filter into the kernel
     *
     * @param ctx the filter context
     *
     * This function loads the given seccomp filter context into the kernel.  If
     * the filter was loaded correctly, the kernel will be enforcing the filter
     * when this function returns.  Returns zero on success, negative values on
     * error.
     *
     */
    pub fn seccomp_load(ctx: *const scmp_filter_ctx) -> libc::c_int;

    /**
     * Get the value of a filter attribute
     *
     * @param ctx the filter context
     * @param attr the filter attribute name
     * @param value the filter attribute value
     *
     * This function fetches the value of the given attribute name and returns it
     * via @value.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_attr_get(ctx: *const scmp_filter_ctx,
                         attr: scmp_filter_attr, value: *mut libc::uint32_t) -> libc::c_int;

    /**
     * Set the value of a filter attribute
     *
     * @param ctx the filter context
     * @param attr the filter attribute name
     * @param value the filter attribute value
     *
     * This function sets the value of the given attribute.  Returns zero on
     * success, negative values on failure.
     *
     */
    pub fn seccomp_attr_set(ctx: *mut scmp_filter_ctx,
                         attr: scmp_filter_attr, value: libc::uint32_t) -> libc::c_int;

    /**
     * Resolve a syscall name to a number
     * @param name the syscall name
     *
     * Resolve the given syscall name to the syscall number.  Returns the syscall
     * number on success, including negative pseudo syscall numbers (e.g. __PNR_*);
     * returns __NR_SCMP_ERROR on failure.
     *
     */
    pub fn seccomp_syscall_resolve_name(name: *const libc::c_char) -> libc::c_int;

    /**
     * Set the priority of a given syscall
     *
     * @param ctx the filter context
     * @param syscall the syscall number
     * @param priority priority value, higher value == higher priority
     *
     * This function sets the priority of the given syscall; this value is used
     * when generating the seccomp filter code such that higher priority syscalls
     * will incur less filter code overhead than the lower priority syscalls in the
     * filter.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_syscall_priority(ctx: *mut scmp_filter_ctx,
                                 syscall: libc::c_int, priority: libc::uint8_t) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt the number of argument filters in the argument filter chain
     * @param ... scmp_arg_cmp structs (use of SCMP_ARG_CMP() recommended)
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule needs to be adjusted due to architecture specifics it
     * will be adjusted without notification.  Returns zero on success, negative
     * values on failure.
     *
     */
    pub fn seccomp_rule_add(ctx: *mut scmp_filter_ctx,
                         action: libc::uint32_t, syscall: libc::c_int, arg_cnt: libc::c_uint, ...) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt the number of elements in the arg_array parameter
     * @param arg_array array of scmp_arg_cmp structs
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule needs to be adjusted due to architecture specifics it
     * will be adjusted without notification.  Returns zero on success, negative
     * values on failure.
     *
     */
    pub fn seccomp_rule_add_array(ctx: *mut scmp_filter_ctx,
        action: libc::uint32_t, syscall: libc::c_int, arg_cnt: libc::c_uint,
        arg_array: *const scmp_arg_cmp) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt the number of argument filters in the argument filter chain
     * @param ... scmp_arg_cmp structs (use of SCMP_ARG_CMP() recommended)
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule can not be represented on the architecture the
     * function will fail.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_rule_add_exact(ctx: *mut scmp_filter_ctx, action: libc::uint32_t,
                               syscall: libc::c_int, arg_cnt: libc::c_uint, ...) -> libc::c_int;

    /**
     * Add a new rule to the filter
     *
     * @param ctx the filter context
     * @param action the filter action
     * @param syscall the syscall number
     * @param arg_cnt  the number of elements in the arg_array parameter
     * @param arg_array array of scmp_arg_cmp structs
     *
     * This function adds a series of new argument/value checks to the seccomp
     * filter for the given syscall; multiple argument/value checks can be
     * specified and they will be chained together (AND'd together) in the filter.
     * If the specified rule can not be represented on the architecture the
     * function will fail.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_rule_add_exact_array(ctx: *mut scmp_filter_ctx,
                                        action: libc::uint32_t, syscall: libc::c_int, arg_cnt: libc::c_uint,
                                        arg_array: *const scmp_arg_cmp) -> libc::c_int;

    /**
     * Generate seccomp Pseudo Filter Code (PFC) and export it to a file
     *
     * @param ctx the filter context
     * @param fd the destination fd
     *
     * This function generates seccomp Pseudo Filter Code (PFC) and writes it to
     * the given fd.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_export_pfc(ctx: *const scmp_filter_ctx, fd: libc::c_int) -> libc::c_int;

    /**
     * Generate seccomp Berkley Packet Filter (BPF) code and export it to a file
     *
     * @param ctx the filter context
     * @param fd the destination fd
     *
     * This function generates seccomp Berkley Packer Filter (BPF) code and writes
     * it to the given fd.  Returns zero on success, negative values on failure.
     *
     */
    pub fn seccomp_export_bpf(ctx: *const scmp_filter_ctx, fd: libc::c_int) -> libc::c_int;
}

#[test]
fn does_it_even_work() {
    unsafe {
        let context = seccomp_init(SCMP_ACT_ALLOW);
        let comparator = scmp_arg_cmp {
            arg: 0,
            op: scmp_compare::SCMP_CMP_EQ,
            datum_a: 1000,
            datum_b: 0,
        };
        assert!(seccomp_rule_add(context, SCMP_ACT_KILL, 105, 1, comparator) == 0);
        assert!(seccomp_load(context) == 0);
        //assert!(libc::setuid(1000) == 0);
    }
}
<|endoftext|>


<commit_before>//! An implementation of [FLAC](https://xiph.org/flac), free lossless audio
//! codec, written in Rust.
//!
//! The code is available on [GitHub](https://github.com/sourrust/flac).
//!
//! # Examples
//!
//! Basic decoding from a file.
//!
//! ```
//! use flac::{Stream, ReadStream};
//! use std::fs::File;
//!
//! match Stream::<ReadStream<File>>::from_file("path/to/file.flac") {
//!   Ok(mut stream) => {
//!     // Copy of `StreamInfo` to help convert to a different audio format.
//!     let info = stream.info();
//!
//!     for sample in stream.iter() {
//!       // Iterate over each decoded sample
//!     }
//!   }
//!   Err(error)     => println!("{}", error),
//! }
//! ```

#[macro_use]
extern crate nom;

#[macro_use]
mod utility;
mod frame;
mod subframe;
pub mod metadata;
pub mod stream;

pub use metadata::Metadata;
pub use stream::Stream;
pub use utility::{StreamProducer, ReadStream, ByteStream};
<commit_msg>Re-export these aliases in top module<commit_after>//! An implementation of [FLAC](https://xiph.org/flac), free lossless audio
//! codec, written in Rust.
//!
//! The code is available on [GitHub](https://github.com/sourrust/flac).
//!
//! # Examples
//!
//! Basic decoding from a file.
//!
//! ```
//! use flac::{Stream, ReadStream};
//! use std::fs::File;
//!
//! match Stream::<ReadStream<File>>::from_file("path/to/file.flac") {
//!   Ok(mut stream) => {
//!     // Copy of `StreamInfo` to help convert to a different audio format.
//!     let info = stream.info();
//!
//!     for sample in stream.iter() {
//!       // Iterate over each decoded sample
//!     }
//!   }
//!   Err(error)     => println!("{}", error),
//! }
//! ```

#[macro_use]
extern crate nom;

#[macro_use]
mod utility;
mod frame;
mod subframe;
pub mod metadata;
pub mod stream;

pub use metadata::Metadata;
pub use stream::{Stream, StreamBuffer, StreamReader};
pub use utility::{StreamProducer, ReadStream, ByteStream};
<|endoftext|>


<commit_before>//! `harfbuzz_rs` is a high-level interface to HarfBuzz, exposing its most important functionality
//! in a safe manner using Rust.
//!
//! # What is HarfBuzz?
//! HarfBuzz is a library for performing complex text layout. It does not perform any drawing. This
//! is quite a low-level operation. If you want to simply draw some text on the screen choose
//! another library. However if you want to build a library for drawing text on some canvas or
//! need a lot of control on advanced text layout then this is the right library to use.
//!
//! # Getting Started
//!
//! To shape a simple string of text you just create a `Font` from a font file, fill a `Buffer`
//! with some text and call the `shape` function.
//!
//! ```
//! # extern crate harfbuzz_rs;
//! use harfbuzz_rs::*;
//!
//! # fn try_main() -> Result<(), std::io::Error> {
//!
//! let path = "path/to/some/font_file.otf";
//! let index = 0; //< face index in the font file
//! # let path = "testfiles/SourceSansVariable-Roman.ttf";
//! let face = Face::from_file(path, index)?;
//! let mut font = Font::new(face);
//!
//! let buffer = UnicodeBuffer::new().add_str("Hello World!");
//! let output = shape(&font, buffer, &[]);
//!
//! // The results of the shaping operation are stored in the `output` buffer.
//!
//! let positions = output.get_glyph_positions();
//! let infos = output.get_glyph_infos();
//!
//! # assert_eq!(positions.len(), 12);
//! assert_eq!(positions.len(), infos.len());
//!
//! // iterate over the shaped glyphs
//! for (position, info) in positions.iter().zip(infos) {
//!     let gid = info.codepoint;
//!     let cluster = info.cluster;
//!     let x_advance = position.x_advance;
//!     let x_offset = position.x_offset;
//!     let y_offset = position.y_offset;
//!
//!     // Here you would usually draw the glyphs.
//!     println!("gid{:?}={:?}@{:?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset);
//! }
//!
//! # Ok(())
//! # }
//! #
//! # fn main() {
//! #   try_main().unwrap();
//! # }
//! ```
//! This should print out something similar to the following:
//!
//! ```text
//! gid41=0@741,0+0
//! gid70=1@421,0+0
//! gid77=2@258,0+0
//! gid77=3@253,0+0
//! gid80=4@510,0+0
//! gid1=5@227,0+0
//! gid56=6@874,0+0
//! gid80=7@498,0+0
//! gid83=8@367,0+0
//! gid77=9@253,0+0
//! gid69=10@528,0+0
//! gid2=11@276,0+0
//! ```
#![deny(missing_debug_implementations)]

use harfbuzz_sys as hb;
#[macro_use]
extern crate bitflags;

mod blob;
mod buffer;
mod common;
mod face;
mod font;
pub mod font_funcs;

#[cfg(feature = "rusttype")]
pub mod rusttype;

pub use crate::blob::*;
pub use crate::buffer::*;
pub use crate::common::*;
pub use crate::face::*;
pub use crate::font::*;

pub type Feature = hb::hb_feature_t;

/// Shape the contents of the buffer using the provided font and activating all
/// OpenType features given in `features`.
///
/// This function consumes the `buffer` and returns a `GlyphBuffer` containing
/// the resulting glyph indices and the corresponding positioning information.
///
/// # Arguments
/// - `font` – a reference to the harfbuzz font used to shape the text.
/// - `buffer` – a `UnicodeBuffer` that is filled with the text to be shaped and
/// also contains metadata about the text in the form of segment properties.
/// - `features` – a slice
pub fn shape(font: &Font<'_>, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer {
    let buffer = buffer.guess_segment_properties();
    unsafe {
        hb::hb_shape(
            font.as_raw(),
            buffer.0.as_raw(),
            features.as_ptr(),
            features.len() as u32,
        )
    };
    GlyphBuffer(buffer.0)
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<commit_msg>Add a new struct wrapper `Feature`<commit_after>//! `harfbuzz_rs` is a high-level interface to HarfBuzz, exposing its most important functionality
//! in a safe manner using Rust.
//!
//! # What is HarfBuzz?
//! HarfBuzz is a library for performing complex text layout. It does not perform any drawing. This
//! is quite a low-level operation. If you want to simply draw some text on the screen choose
//! another library. However if you want to build a library for drawing text on some canvas or
//! need a lot of control on advanced text layout then this is the right library to use.
//!
//! # Getting Started
//!
//! To shape a simple string of text you just create a `Font` from a font file, fill a `Buffer`
//! with some text and call the `shape` function.
//!
//! ```
//! # extern crate harfbuzz_rs;
//! use harfbuzz_rs::*;
//!
//! # fn try_main() -> Result<(), std::io::Error> {
//!
//! let path = "path/to/some/font_file.otf";
//! let index = 0; //< face index in the font file
//! # let path = "testfiles/SourceSansVariable-Roman.ttf";
//! let face = Face::from_file(path, index)?;
//! let mut font = Font::new(face);
//!
//! let buffer = UnicodeBuffer::new().add_str("Hello World!");
//! let output = shape(&font, buffer, &[]);
//!
//! // The results of the shaping operation are stored in the `output` buffer.
//!
//! let positions = output.get_glyph_positions();
//! let infos = output.get_glyph_infos();
//!
//! # assert_eq!(positions.len(), 12);
//! assert_eq!(positions.len(), infos.len());
//!
//! // iterate over the shaped glyphs
//! for (position, info) in positions.iter().zip(infos) {
//!     let gid = info.codepoint;
//!     let cluster = info.cluster;
//!     let x_advance = position.x_advance;
//!     let x_offset = position.x_offset;
//!     let y_offset = position.y_offset;
//!
//!     // Here you would usually draw the glyphs.
//!     println!("gid{:?}={:?}@{:?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset);
//! }
//!
//! # Ok(())
//! # }
//! #
//! # fn main() {
//! #   try_main().unwrap();
//! # }
//! ```
//! This should print out something similar to the following:
//!
//! ```text
//! gid41=0@741,0+0
//! gid70=1@421,0+0
//! gid77=2@258,0+0
//! gid77=3@253,0+0
//! gid80=4@510,0+0
//! gid1=5@227,0+0
//! gid56=6@874,0+0
//! gid80=7@498,0+0
//! gid83=8@367,0+0
//! gid77=9@253,0+0
//! gid69=10@528,0+0
//! gid2=11@276,0+0
//! ```
#![deny(missing_debug_implementations)]

use harfbuzz_sys as hb;
#[macro_use]
extern crate bitflags;

mod blob;
mod buffer;
mod common;
mod face;
mod font;
pub mod font_funcs;

#[cfg(feature = "rusttype")]
pub mod rusttype;

pub use crate::blob::*;
pub use crate::buffer::*;
pub use crate::common::*;
pub use crate::face::*;
pub use crate::font::*;

use std::ops::{Bound, RangeBounds};
use std::os::raw::c_uint;

/// A feature tag with an according length were it should be applied.
///
/// You can pass a slice of `Feature`s to `shape` that will be activated for the
/// corresponding slices of input.
///
/// # Examples
///
/// Shape some text using the `calt` (Contextual Alternatives) feature.
///
/// ```
/// use harfbuzz_rs::{Face, Font, UnicodeBuffer, shape, Feature, Tag};
///
/// let path = "testfiles/SourceSansVariable-Roman.ttf";
/// let face = Face::from_file(path, 0).expect("could not load face");
/// let font = Font::new(face);
///
/// let buffer = UnicodeBuffer::new().add_str("Hello World!");
///
/// // contextual alternatives feature
/// let feature_tag = Tag::new('c', 'a', 'l', 't');
///
/// // use the feature on the entire input
/// let feature_range = 0..;
/// let feature = Feature::new(feature_tag, 0, feature_range);
///
/// let output = shape(&font, buffer, &[feature]);
/// ```
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Feature(hb::hb_feature_t);

impl Feature {
    /// Create a new `Feature` struct.
    ///
    /// # Arguments
    ///
    /// - `tag`: The OpenType feature tag to use.
    /// - `value`: Some OpenType features accept different values to change
    ///   their behaviour.
    /// - `range`: The character range that should be affected by this feature.
    pub fn new(tag: Tag, value: u32, range: impl RangeBounds<usize>) -> Feature {
        // We have to do careful bounds checking since c_uint may be of
        // different sizes on different platforms. We do assume that
        // sizeof(usize) >= sizeof(c_uint).
        const MAX_UINT: usize = c_uint::max_value() as usize;
        let start = match range.start_bound() {
            Bound::Included(&included) => included.min(MAX_UINT) as c_uint,
            Bound::Excluded(&excluded) => excluded.min(MAX_UINT - 1) as c_uint + 1,
            Bound::Unbounded => 0,
        };
        let end = match range.end_bound() {
            Bound::Included(&included) => included.min(MAX_UINT) as c_uint,
            Bound::Excluded(&excluded) => excluded.saturating_sub(1).min(MAX_UINT) as c_uint,
            Bound::Unbounded => c_uint::max_value(),
        };

        Feature(hb::hb_feature_t {
            tag: tag.0,
            value,
            start,
            end,
        })
    }

    pub fn tag(&self) -> Tag {
        Tag(self.0.tag)
    }

    pub fn value(&self) -> u32 {
        self.0.value
    }

    pub fn start(&self) -> usize {
        self.0.start as usize
    }

    pub fn end(&self) -> usize {
        self.0.end as usize
    }
}

/// Shape the contents of the buffer using the provided font and activating all
/// OpenType features given in `features`.
///
/// This function consumes the `buffer` and returns a `GlyphBuffer` containing
/// the resulting glyph indices and the corresponding positioning information.
/// Once all the information from the `GlyphBuffer` has been processed as
/// necessary you can reuse the `GlyphBuffer` as an `UnicodeBuffer` (using
/// `GlyphBuffer::clear_contents`) and use that to call `shape` again with new
/// data.
///
/// By default some basic OpenType features are enabled according to the
/// language and the script set in the buffer.
///
/// # Arguments
/// - `font` – a reference to the harfbuzz font used to shape the text.
/// - `buffer` – a `UnicodeBuffer` that is filled with the text to be shaped and
/// also contains metadata about the text in the form of segment properties.
/// - `features` – a slice of additional features to activate
pub fn shape(font: &Font<'_>, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer {
    let buffer = buffer.guess_segment_properties();
    unsafe {
        hb::hb_shape(
            font.as_raw(),
            buffer.0.as_raw(),
            features.as_ptr() as *mut _,
            features.len() as u32,
        )
    };
    GlyphBuffer(buffer.0)
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}

    fn assert_feature(feat: Feature, tag: Tag, value: u32, start: usize, end: usize) {
        assert_eq!(feat.tag(), tag);
        assert_eq!(feat.value(), value);
        assert_eq!(feat.start(), start);
        assert_eq!(feat.end(), end);
    }

    use super::{Feature, Tag};
    #[test]
    fn feature_new() {
        let tag = Tag::new('a', 'b', 'c', 'd');
        const UINT_MAX: usize = std::os::raw::c_uint::max_value() as usize;

        let feature = Feature::new(tag, 100, 2..100);
        assert_feature(feature, tag, 100, 2, 99);

        let feature = Feature::new(tag, 100, 2..=100);
        assert_feature(feature, tag, 100, 2, 100);

        let feature = Feature::new(tag, 100, 2..);
        assert_feature(feature, tag, 100, 2, UINT_MAX);

        let feature = Feature::new(tag, 100, ..100);
        assert_feature(feature, tag, 100, 0, 99);

        let feature = Feature::new(tag, 100, ..=100);
        assert_feature(feature, tag, 100, 0, 100);

        let feature = Feature::new(tag, 100, ..);
        assert_feature(feature, tag, 100, 0, UINT_MAX);
    }
}
<|endoftext|>