<commit_before>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! extern crate "sha1-hasher" as sha1;
//! use std::hash::Writer;
//!
//! # fn main() {
//!
//! let mut m = sha1::Sha1::new();
//! m.write("Hello World!".as_bytes());
//! assert_eq!(&*m.hexdigest(), "2ef7bde608ce5404e97d5f042f95f89f1c232871");
//! # }
//! ```

#![feature(slicing_syntax)]
#![feature(collections)]
#![feature(core)]
#![feature(hash)]
#![feature(io)]

#![allow(unused_features)]
#![feature(test)]

#[cfg(test)] extern crate test;

use std::old_io::{Writer, BufWriter};
use std::default::Default;
use std::hash::{self, Hasher};

/// Represents a Sha1 hash object in memory.
#[derive(Clone)]
pub struct Sha1 {
    state: [u32; 5],
    data: Vec<u8>,
    len: u64,
}

const DEFAULT_STATE : [u32; 5] =
    [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

fn to_hex(input: &[u8]) -> String {
    let mut s = String::new();
    for b in input.iter() {
        s.push_str(&*format!("{:02x}", *b));
    }
    return s;
}

impl hash::Hasher for Sha1 {
    type Output = Vec<u8>;
    fn reset(&mut self) {
        self.state = DEFAULT_STATE;
        self.data.clear();
        self.len = 0;
    }
    fn finish(&self) -> Vec<u8> {
        let mut buf = [0u8; 20].to_vec();
        self.output(&mut *buf);
        buf
    }
}

impl Default for Sha1 {
    #[inline]
    fn default() -> Sha1 {
        Sha1::new()
    }
}

impl hash::Writer for Sha1 {
    fn write(&mut self, bytes: &[u8]) {
        let mut d = self.data.clone();
        self.data.clear();

        d.push_all(bytes);

        for chunk in d[].chunks(64) {
            if chunk.len() == 64 {
                self.len += 64;
                self.process_block(chunk);
            } else {
                self.data.push_all(chunk);
            }
        }
    }
}

impl Sha1 {

    /// Creates an fresh sha1 hash object.
    pub fn new() -> Sha1 {
        Sha1 {
            state: DEFAULT_STATE,
            data: Vec::new(),
            len: 0,
        }
    }

    fn process_block(&mut self, block: &[u8]) {
        assert_eq!(block.len(), 64);

        let mut words = [0u32; 80];
        for (i, chunk) in block.chunks(4).enumerate() {
            words[i] = (chunk[3] as u32) |
                       ((chunk[2] as u32) << 8) |
                       ((chunk[1] as u32) << 16) |
                       ((chunk[0] as u32) << 24);
        }

        fn ff(b: u32, c: u32, d: u32) -> u32 { d ^ (b & (c ^ d)) }
        fn gg(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }
        fn hh(b: u32, c: u32, d: u32) -> u32 { (b & c) | (d & (b | c)) }
        fn ii(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }

        fn left_rotate(x: u32, n: u32) -> u32 { (x << n) | (x >> (32 - n)) }

        for i in range(16, 80) {
            let n = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
            words[i] = left_rotate(n, 1);
        }

        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];

        for i in range(0, 80) {
            let (f, k) = match i {
                0 ... 19 => (ff(b, c, d), 0x5a827999),
                20 ... 39 => (gg(b, c, d), 0x6ed9eba1),
                40 ... 59 => (hh(b, c, d), 0x8f1bbcdc),
                60 ... 79 => (ii(b, c, d), 0xca62c1d6),
                _ => (0, 0),
            };

            let tmp = left_rotate(a, 5) + f + e + k + words[i];
            e = d;
            d = c;
            c = left_rotate(b, 30);
            b = a;
            a = tmp;
        }

        self.state[0] += a;
        self.state[1] += b;
        self.state[2] += c;
        self.state[3] += d;
        self.state[4] += e;
    }

    /// Retrieve digest result.  The output must be large enough to
    /// contain result (20 bytes).
    pub fn output(&self, out: &mut [u8]) {
        // these are unlikely to fail, since we're writing to memory
        #![allow(unused_must_use)]

        let mut m = Sha1 {
            state: self.state.clone(),
            data: Vec::new(),
            len: 0,
        };

        let mut w = Vec::<u8>::new();
        w.write_all(&*self.data);
        w.write_u8(0x80u8);
        let padding = 64 - ((self.data.len() + 9) % 64);
        for _ in range(0, padding) {
            w.write_u8(0u8);
        }
        w.write_be_u64((self.data.len() as u64 + self.len) * 8);
        for chunk in w[].chunks(64) {
            m.process_block(chunk);
        }

        let mut w = BufWriter::new(out);
        for &n in m.state.iter() {
            w.write_be_u32(n);
        }
    }

    pub fn hexdigest(&self) -> String {
        to_hex(&*self.finish())
    }
}

#[cfg(test)]
mod tests {
    use test::Bencher;
    use super::{Sha1, to_hex};
    use std::hash::{Writer, Hasher};

    #[test]
    fn test_simple() {
        let mut m = Sha1::new();

        let tests = [
            ("The quick brown fox jumps over the lazy dog",
            "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
            ("The quick brown fox jumps over the lazy cog",
            "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
            ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
            ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
            ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
        ];

        for &(s, ref h) in tests.iter() {
            let data = s.as_bytes();

            m.reset();
            m.write(data);
            let hh = m.hexdigest();

            assert_eq!(hh.len(), h.len());
            assert_eq!(&*hh, *h);
        }
    }

    #[test]
    fn test_dirty_run() {
        let mut m = Sha1::new();

        m.write(b"123");
        let out1 = m.finish();

        m.write(b"123");
        let out2 = m.finish();

        assert!(out1 != out2);
        assert_eq!(&*to_hex(&*out1), "40bd001563085fc35165329ea1ff5c5ecbdbbeef");
        assert_eq!(&*to_hex(&*out2), "601f1889667efaebb33b8c12572835da3f027f78");
    }

    #[bench]
    fn test_sha1_speed(b: &mut Bencher) {
        let mut m = Sha1::new();
        let s = "The quick brown fox jumps over the lazy dog.";
        let n = 1000u64;

        b.bytes = n * s.len() as u64;
        b.iter(|| {
            m.reset();
            for _ in range(0, n) {
                m.write(s.as_bytes());
            }
            assert_eq!(m.hexdigest(), "7ca27655f67fceaa78ed2e645a81c7f1d6e249d2");
        });
    }
}
<commit_msg>update to latest rust<commit_after>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! extern crate "sha1-hasher" as sha1;
//!
//! # fn main() {
//!
//! let mut m = sha1::Sha1::new();
//! m.write("Hello World!".as_bytes());
//! assert_eq!(&*m.hexdigest(), "2ef7bde608ce5404e97d5f042f95f89f1c232871");
//! # }
//! ```

#![feature(collections)]
#![feature(core)]
#![feature(hash)]
#![feature(old_io)]

#![allow(unused_features)]
#![feature(test)]

#[cfg(test)] extern crate test;

use std::old_io::{Writer, BufWriter};
use std::old_io::IoResult;
use std::default::Default;

/// Represents a Sha1 hash object in memory.
#[derive(Clone)]
pub struct Sha1 {
    state: [u32; 5],
    data: Vec<u8>,
    len: u64,
}

const DEFAULT_STATE : [u32; 5] =
    [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

fn to_hex(input: &[u8]) -> String {
    let mut s = String::new();
    for b in input.iter() {
        s.push_str(&*format!("{:02x}", *b));
    }
    return s;
}

impl Default for Sha1 {
    #[inline]
    fn default() -> Sha1 {
        Sha1::new()
    }
}

impl Writer for Sha1 {
    fn write_all(&mut self, bytes: &[u8]) -> IoResult<()> {
        let mut d = self.data.clone();
        self.data.clear();

        d.push_all(bytes);

        for chunk in d[..].chunks(64) {
            if chunk.len() == 64 {
                self.len += 64;
                self.process_block(chunk);
            } else {
                self.data.push_all(chunk);
            }
        }

        Ok(())
        //Ok(bytes.len())
    }

    fn flush(&mut self) -> IoResult<()> {
        Ok(())
    }
}

impl Sha1 {

    /// Creates an fresh sha1 hash object.
    pub fn new() -> Sha1 {
        Sha1 {
            state: DEFAULT_STATE,
            data: Vec::new(),
            len: 0,
        }
    }

    pub fn reset(&mut self) {
        self.state = DEFAULT_STATE;
        self.data.clear();
        self.len = 0;
    }

    pub fn finish(&self) -> Vec<u8> {
        let mut buf = [0u8; 20].to_vec();
        self.output(&mut *buf);
        buf
    }

    fn process_block(&mut self, block: &[u8]) {
        assert_eq!(block.len(), 64);

        let mut words = [0u32; 80];
        for (i, chunk) in block.chunks(4).enumerate() {
            words[i] = (chunk[3] as u32) |
                       ((chunk[2] as u32) << 8) |
                       ((chunk[1] as u32) << 16) |
                       ((chunk[0] as u32) << 24);
        }

        fn ff(b: u32, c: u32, d: u32) -> u32 { d ^ (b & (c ^ d)) }
        fn gg(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }
        fn hh(b: u32, c: u32, d: u32) -> u32 { (b & c) | (d & (b | c)) }
        fn ii(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }

        fn left_rotate(x: u32, n: u32) -> u32 { (x << n) | (x >> (32 - n)) }

        for i in range(16, 80) {
            let n = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
            words[i] = left_rotate(n, 1);
        }

        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];

        for i in range(0, 80) {
            let (f, k) = match i {
                0 ... 19 => (ff(b, c, d), 0x5a827999),
                20 ... 39 => (gg(b, c, d), 0x6ed9eba1),
                40 ... 59 => (hh(b, c, d), 0x8f1bbcdc),
                60 ... 79 => (ii(b, c, d), 0xca62c1d6),
                _ => (0, 0),
            };

            let tmp = left_rotate(a, 5) + f + e + k + words[i];
            e = d;
            d = c;
            c = left_rotate(b, 30);
            b = a;
            a = tmp;
        }

        self.state[0] += a;
        self.state[1] += b;
        self.state[2] += c;
        self.state[3] += d;
        self.state[4] += e;
    }

    /// Retrieve digest result.  The output must be large enough to
    /// contain result (20 bytes).
    pub fn output(&self, out: &mut [u8]) {
        // these are unlikely to fail, since we're writing to memory
        #![allow(unused_must_use)]

        let mut m = Sha1 {
            state: self.state.clone(),
            data: Vec::new(),
            len: 0,
        };

        let mut w = Vec::<u8>::new();
        w.write_all(&*self.data);
        w.write_u8(0x80u8);
        let padding = 64 - ((self.data.len() + 9) % 64);
        for _ in range(0, padding) {
            w.write_u8(0u8);
        }
        w.write_be_u64((self.data.len() as u64 + self.len) * 8);
        for chunk in w[..].chunks(64) {
            m.process_block(chunk);
        }

        let mut w = BufWriter::new(out);
        for &n in m.state.iter() {
            w.write_be_u32(n);
        }
    }

    pub fn hexdigest(&self) -> String {
        to_hex(&*self.finish())
    }
}

#[cfg(test)]
mod tests {
    use test::Bencher;
    use super::{Sha1, to_hex};

    #[test]
    fn test_simple() {
        let mut m = Sha1::new();

        let tests = [
            ("The quick brown fox jumps over the lazy dog",
            "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
            ("The quick brown fox jumps over the lazy cog",
            "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
            ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
            ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
            ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
        ];

        for &(s, ref h) in tests.iter() {
            let data = s.as_bytes();

            m.reset();
            m.write_all(data).unwrap();
            let hh = m.hexdigest();

            assert_eq!(hh.len(), h.len());
            assert_eq!(&*hh, *h);
        }
    }

    #[test]
    fn test_dirty_run() {
        let mut m = Sha1::new();

        m.write_all(b"123").unwrap();
        let out1 = m.finish();

        m.write_all(b"123").unwrap();
        let out2 = m.finish();

        assert!(out1 != out2);
        assert_eq!(&*to_hex(&*out1), "40bd001563085fc35165329ea1ff5c5ecbdbbeef");
        assert_eq!(&*to_hex(&*out2), "601f1889667efaebb33b8c12572835da3f027f78");
    }

    #[bench]
    fn test_sha1_speed(b: &mut Bencher) {
        let mut m = Sha1::new();
        let s = "The quick brown fox jumps over the lazy dog.";
        let n = 1000u64;

        b.bytes = n * s.len() as u64;
        b.iter(|| {
            m.reset();
            for _ in range(0, n) {
                m.write_all(s.as_bytes()).unwrap();
            }
            assert_eq!(m.hexdigest(), "7ca27655f67fceaa78ed2e645a81c7f1d6e249d2");
        });
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate csv;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate safe_unwrap;
extern crate term_painter;
extern crate term_size;
extern crate textwrap;
extern crate try_from;

pub mod err;
mod regexes;

use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use err::{ColumnConstraintsError, ColumnTypeError, ErrorLoc, ErrorAtLocation, Location, ResultLoc,
          SchemaLoadError, ValidationError, ValueError};
use std::{fmt, path, slice};
use safe_unwrap::SafeUnwrap;
use regexes::{IDENT_UNDERSCORE_RE, ENUM_EXPR_RE, CONSTRAINT_RE, DECIMAL_RE, DATE_RE, DATETIME_RE,
              FN_RE, TIME_RE};
use try_from::TryFrom;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CsvxMetadata {
    pub table_name: String,
    pub date: NaiveDate,
    pub schema: String,
}

impl CsvxMetadata {
    pub fn is_schema(&self) -> bool {
        self.schema.starts_with("csvx-schema-")
    }
}

#[derive(Clone, Debug)]
pub enum ColumnType {
    String,
    Bool,
    Integer,
    Enum(Vec<String>),
    Decimal,
    Date,
    DateTime,
    Time,
}

impl fmt::Display for ColumnType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ColumnType::String => write!(f, "STRING"),
            ColumnType::Bool => write!(f, "BOOL"),
            ColumnType::Integer => write!(f, "INTEGER"),
            ColumnType::Enum(ref variants) => write!(f, "ENUM({})", variants.join(",")),
            ColumnType::Decimal => write!(f, "DECIMAL"),
            ColumnType::Date => write!(f, "DATE"),
            ColumnType::DateTime => write!(f, "DATETIME"),
            ColumnType::Time => write!(f, "TIME"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ColumnConstraints {
    nullable: bool,
    unique: bool,
}

impl Default for ColumnConstraints {
    fn default() -> ColumnConstraints {
        ColumnConstraints {
            nullable: false,
            unique: false,
        }
    }
}

impl fmt::Display for ColumnConstraints {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut parts = Vec::new();
        if self.nullable {
            parts.push("NULLABLE");
        }
        if self.unique {
            parts.push("UNIQUE");
        }
        write!(f, "{}", parts.join(","))
    }
}

impl<S> TryFrom<S> for ColumnConstraints
    where S: AsRef<str>
{
    type Err = ColumnConstraintsError;

    fn try_from(s: S) -> Result<ColumnConstraints, Self::Err> {
        if !CONSTRAINT_RE.is_match(s.as_ref()) {
            return Err(ColumnConstraintsError::MalformedConstraints(s.as_ref().to_string()));
        }

        let mut ccs = ColumnConstraints::default();

        if s.as_ref() == "" {
            return Ok(ccs);
        }

        for fragment in s.as_ref().split(',') {
            match fragment.as_ref() {
                "NULLABLE" => {
                    ccs.nullable = true;
                }
                "UNIQUE" => {
                    ccs.unique = true;
                }
                _ => return Err(ColumnConstraintsError::UnknownConstraint(s.as_ref().to_string())),
            }

        }

        Ok(ccs)
    }
}

impl<S> TryFrom<S> for ColumnType
    where S: AsRef<str>
{
    type Err = ColumnTypeError;

    fn try_from(s: S) -> Result<ColumnType, Self::Err> {
        match s.as_ref() {
            "STRING" => Ok(ColumnType::String),
            "BOOL" => Ok(ColumnType::Bool),
            "INTEGER" => Ok(ColumnType::Integer),
            "DECIMAL" => Ok(ColumnType::Decimal),
            "DATE" => Ok(ColumnType::Date),
            "DATETIME" => Ok(ColumnType::DateTime),
            "TIME" => Ok(ColumnType::Time),
            _ if ENUM_EXPR_RE.is_match(s.as_ref()) => {
                let variants: Vec<_> = ENUM_EXPR_RE
                    .captures(s.as_ref())
                    .safe_unwrap("match already exists")
                    .get(1)
                    .safe_unwrap("group 1 exists in regex")
                    .as_str()
                    .split(',')
                    .map(|s| s.to_owned())
                    .collect();

                Ok(ColumnType::Enum(variants))
            }
            _ => {
                if s.as_ref().starts_with("ENUM") {
                    return Err(ColumnTypeError::BadEnum(s.as_ref().to_owned()));
                }
                return Err(ColumnTypeError::UnknownType(s.as_ref().to_owned()));
            }

        }
    }
}

#[derive(Clone, Debug)]
pub struct CsvxColumnType {
    pub id: String,
    pub ty: ColumnType,
    pub constraints: ColumnConstraints,
    pub description: String,
}

#[derive(Debug)]
pub enum Value {
    String(String),
    Bool(bool),
    Integer(i64),
    Enum(String),
    Decimal(String),
    Date(NaiveDate),
    DateTime(NaiveDateTime),
    Time(NaiveTime),
}

impl CsvxColumnType {
    pub fn validate_value<S: AsRef<str>>(&self, s: &S) -> Result<Option<Value>, ValueError> {
        // FIXME: check UNIQUE

        // null check
        if s.as_ref() == "" {
            if self.constraints.nullable {
                return Ok(None);
            } else {
                return Err(ValueError::NonNullable);
            }
        }

        match self.ty {
            ColumnType::String => Ok(Some(Value::String(s.as_ref().to_string()))),
            ColumnType::Bool => {
                match s.as_ref() {
                    "TRUE" => Ok(Some(Value::Bool(true))),
                    "FALSE" => Ok(Some(Value::Bool(false))),
                    _ => Err(ValueError::InvalidBool(s.as_ref().to_owned())),
                }
            }
            ColumnType::Integer => {
                // FIXME: check for leading zeros
                Ok(Some(Value::Integer(s.as_ref()
                                           .parse()
                                           .map_err(|_| {
                                                        ValueError::InvalidInt(s.as_ref()
                                                                                .to_owned())
                                                    })?)))
            }
            ColumnType::Enum(ref variants) => {
                let v = s.as_ref().to_owned();
                if variants.contains(&v) {
                    Ok(Some(Value::Enum(v)))
                } else {
                    Err(ValueError::InvalidEnum(s.as_ref().to_owned(), variants.clone()))
                }
            }
            ColumnType::Decimal => {
                if DECIMAL_RE.is_match(s.as_ref()) {
                    Ok(Some(Value::Decimal(s.as_ref().to_owned())))
                } else {
                    Err(ValueError::InvalidDecimal(s.as_ref().to_owned()))
                }
            }
            ColumnType::Date => {
                match DATE_RE.captures(s.as_ref()) {
                    Some(ref c) => {
                        Ok(Some(Value::Date(NaiveDate::from_ymd_opt(cap(c, 1),
                                                                    cap(c, 2),
                                                                    cap(c, 3))
              .ok_or_else(||{
                      ValueError::InvalidDate(s.as_ref().to_owned ()) }
                      )?)))
                    }
                    None => Err(ValueError::InvalidDate(s.as_ref().to_owned())),
                }
            }
            ColumnType::DateTime => {
                match DATETIME_RE.captures(s.as_ref()) {
                    Some(ref c) => {
                        let dt =
                            NaiveDate::from_ymd_opt(cap(c, 1), cap(c, 2), cap(c, 3))
                                .ok_or_else(|| ValueError::InvalidDate(s.as_ref().to_string()))?;
                        Ok(Some(Value::DateTime(dt.and_hms_opt(cap(c, 4), cap(c, 5), cap(c, 6))
                                                    .ok_or_else(|| {
                                                                    ValueError::InvalidTime (s.as_ref ().to_string ()) })?)))
                    }
                    None => Err(ValueError::InvalidDateTime(s.as_ref().to_string())),
                }
            }
            ColumnType::Time => {
                match TIME_RE.captures(s.as_ref()) {
                    Some(ref c) => {
                        Ok(Some(Value::Time(NaiveTime::from_hms_opt(cap(c, 1),
                                                                    cap(c, 2),
                                                                    cap(c, 3))
                                                    .ok_or_else(||
                                                        ValueError::InvalidTime (s.as_ref ().to_string ()))?)))
                    }
                    None => Err(ValueError::InvalidTime(s.as_ref().to_string())),
                }
            }
        }
    }
}

#[derive(Clone, Debug)]
pub struct CsvxSchema {
    columns: Vec<CsvxColumnType>,
}

impl CsvxSchema {
    pub fn iter_columns(&self) -> slice::Iter<CsvxColumnType> {
        self.columns.iter()
    }

    pub fn from_file<P: AsRef<path::Path>>
        (filename: P)
         -> Result<CsvxSchema, ErrorAtLocation<SchemaLoadError, Location>> {

        // have a copy of the filename as a string ready for error locations
        let filename_s = filename.as_ref().to_string_lossy().to_string();

        let mut rdr = csv::Reader::from_file(filename)
            .err_at(|| Location::File(filename_s.clone()))?
            .has_headers(false);

        let mut it = rdr.decode();
        let header: Option<Result<(String, String, String, String), _>> = it.next();

        let mut columns = Vec::new();

        match header {
            None => {
                return Err(SchemaLoadError::MissingHeader.at(Location::FileLine(filename_s, 1)))
            }
            Some(res) => {
                let fields = res.err_at(|| Location::File(filename_s.clone()))?;
                if fields.0 != "id" || fields.1 != "type" || fields.2 != "constraints" ||
                   fields.3 != "description" {

                    return Err(SchemaLoadError::BadHeader.at(Location::FileLine(filename_s, 1)));
                }

                for (recno, rec) in it.enumerate() {
                    let (id, ty, constraints, desc) =
                        rec.err_at(|| Location::FileLine(filename_s.clone(), 1))?;
                    let lineno = recno + 2;

                    // check identifier
                    if !IDENT_UNDERSCORE_RE.is_match(&id.as_str()) {
                        return Err(SchemaLoadError::BadIdentifier(id)
                                       .at(Location::FileLineField(filename_s, lineno, 1)));
                    }

                    // create type
                    let col_type = match ColumnType::try_from(ty.as_str()) {
                        Ok(v) => v,
                        Err(e) => {
                            return Err(SchemaLoadError::BadType(e)
                                           .at(Location::FileLineField(filename_s, lineno, 1)))
                        }
                    };

                    // create constraints
                    let col_constraints = match ColumnConstraints::try_from(constraints.as_str()) {
                        Ok(v) => v,
                        // FIXME: location
                        Err(e) => {
                            return Err(SchemaLoadError::BadConstraints(e)
                                           .at(Location::FileLine(filename_s, lineno)))
                        }
                    };

                    let col = CsvxColumnType {
                        id: id,
                        ty: col_type,
                        constraints: col_constraints,
                        description: desc,
                    };

                    columns.push(col)
                }

                Ok(CsvxSchema { columns: columns })
            }
        }
    }

    pub fn validate_file<P: AsRef<path::Path>>
        (&self,
         filename: P)
         -> Result<(), Vec<ErrorAtLocation<ValidationError, Location>>> {
        let filename_s = filename.as_ref().to_string_lossy().to_string();

        let mut rdr = csv::Reader::from_file(filename)
            .map_err(|e| vec![e.at(Location::File(filename_s.clone()))])?
            .has_headers(true);

        let headers = rdr.headers()
            .map_err(|e| vec![e.at(Location::FileLine(filename_s.clone(), 1))])?;

        if headers.len() != self.columns.len() {
            return Err(vec![ValidationError::MissingHeaders
                                .at(Location::FileLine(filename_s.clone(), 1))]);
        }

        let mut errs = Vec::new();

        for (idx, (spec, actual)) in self.columns.iter().zip(headers.iter()).enumerate() {
            if spec.id.as_str() != actual {
                errs.push(ValidationError::HeaderMismatch(actual.to_string())
                              .at(Location::FileLineField(filename_s.clone(), 1, idx + 1)));
            }
        }

        // bail if headers are incorrect
        if errs.len() != 0 {
            return Err(errs);
        }

        for (rowid, row) in rdr.records().enumerate() {
            let lineno = rowid + 2;

            // bail early if we cannot read the fields, this is probably a
            // major csv issue
            let fields = row.map_err(|e| vec![e.at(Location::FileLine(filename_s.clone(), 1))])?;

            for (idx, (col, value)) in self.columns.iter().zip(fields.iter()).enumerate() {
                if let Err(e) = col.validate_value(value) {
                    let col_idx = idx + 1;

                    errs.push(ValidationError::ValueError(e)
                                  .at(Location::FileLineField(filename_s.clone(),
                                                              lineno,
                                                              col_idx)));
                    continue;
                }
            }
        }

        if errs.len() != 0 {
            return Err(errs);
        } else {
            Ok(())
        }
    }

    pub fn parse_row(&self, fields: &Vec<String>)
                 -> Result<Vec<Option<Value>>, ErrorAtLocation<ValidationError, usize>> {
        let mut rv = Vec::with_capacity(self.columns.len());
        for (idx, (col, value)) in self.columns.iter().zip(fields.iter()).enumerate() {
            match col.validate_value(value) {
                Err(e) => {
                    let col_idx = idx + 1;

                    return Err(ValidationError::ValueError(e).at(col_idx));
                }
                Ok(v) => rv.push(v),
            }
        }
        Ok(rv)
    }
}

#[inline]
fn cap<T>(c: &regex::Captures, idx: usize) -> T
    where T: std::str::FromStr,
          T::Err: std::fmt::Debug
{
    c.get(idx)
        .safe_unwrap("valid group")
        .as_str()
        .parse()
        .safe_unwrap("already validated through regex")

}

pub fn parse_filename<S: AsRef<str>>(filename: S) -> Option<CsvxMetadata> {
    match FN_RE.captures(filename.as_ref()) {
        Some(caps) => {
            let table_name = caps.get(1)
                .safe_unwrap("known group")
                .as_str()
                .to_string();
            let year = cap(&caps, 2);
            let month = cap(&caps, 3);
            let day = cap(&caps, 4);
            let schema = caps.get(5)
                .safe_unwrap("known group")
                .as_str()
                .to_string();

            Some(CsvxMetadata {
                     table_name: table_name,
                     date: match NaiveDate::from_ymd_opt(year, month, day) {
                         Some(d) => d,
                         None => return None,
                     },
                     schema: schema,
                 })
        }
        None => None,
    }
}

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

    use chrono::NaiveDate;

    #[test]
    fn filename_parsing_rejects_invalid() {
        assert_eq!(parse_filename("asdf"), None);
        assert_eq!(parse_filename(""), None);
        assert_eq!(parse_filename("test.csv"), None);
        assert_eq!(parse_filename("test.csv"), None);
    }

    #[test]
    fn filename_parsing_parses_valid() {
        assert_eq!(parse_filename("zoo-nyc_20170401_animals-2.csv").unwrap(),
                   CsvxMetadata {
                       table_name: "zoo-nyc".to_owned(),
                       date: NaiveDate::from_ymd(2017, 04, 01),
                       schema: "animals-2".to_owned(),
                   });
    }

}
<commit_msg>Allow any slice ref.<commit_after>extern crate chrono;
extern crate csv;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate safe_unwrap;
extern crate term_painter;
extern crate term_size;
extern crate textwrap;
extern crate try_from;

pub mod err;
mod regexes;

use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use err::{ColumnConstraintsError, ColumnTypeError, ErrorLoc, ErrorAtLocation, Location, ResultLoc,
          SchemaLoadError, ValidationError, ValueError};
use std::{fmt, path, slice};
use safe_unwrap::SafeUnwrap;
use regexes::{IDENT_UNDERSCORE_RE, ENUM_EXPR_RE, CONSTRAINT_RE, DECIMAL_RE, DATE_RE, DATETIME_RE,
              FN_RE, TIME_RE};
use try_from::TryFrom;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CsvxMetadata {
    pub table_name: String,
    pub date: NaiveDate,
    pub schema: String,
}

impl CsvxMetadata {
    pub fn is_schema(&self) -> bool {
        self.schema.starts_with("csvx-schema-")
    }
}

#[derive(Clone, Debug)]
pub enum ColumnType {
    String,
    Bool,
    Integer,
    Enum(Vec<String>),
    Decimal,
    Date,
    DateTime,
    Time,
}

impl fmt::Display for ColumnType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ColumnType::String => write!(f, "STRING"),
            ColumnType::Bool => write!(f, "BOOL"),
            ColumnType::Integer => write!(f, "INTEGER"),
            ColumnType::Enum(ref variants) => write!(f, "ENUM({})", variants.join(",")),
            ColumnType::Decimal => write!(f, "DECIMAL"),
            ColumnType::Date => write!(f, "DATE"),
            ColumnType::DateTime => write!(f, "DATETIME"),
            ColumnType::Time => write!(f, "TIME"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ColumnConstraints {
    nullable: bool,
    unique: bool,
}

impl Default for ColumnConstraints {
    fn default() -> ColumnConstraints {
        ColumnConstraints {
            nullable: false,
            unique: false,
        }
    }
}

impl fmt::Display for ColumnConstraints {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut parts = Vec::new();
        if self.nullable {
            parts.push("NULLABLE");
        }
        if self.unique {
            parts.push("UNIQUE");
        }
        write!(f, "{}", parts.join(","))
    }
}

impl<S> TryFrom<S> for ColumnConstraints
    where S: AsRef<str>
{
    type Err = ColumnConstraintsError;

    fn try_from(s: S) -> Result<ColumnConstraints, Self::Err> {
        if !CONSTRAINT_RE.is_match(s.as_ref()) {
            return Err(ColumnConstraintsError::MalformedConstraints(s.as_ref().to_string()));
        }

        let mut ccs = ColumnConstraints::default();

        if s.as_ref() == "" {
            return Ok(ccs);
        }

        for fragment in s.as_ref().split(',') {
            match fragment.as_ref() {
                "NULLABLE" => {
                    ccs.nullable = true;
                }
                "UNIQUE" => {
                    ccs.unique = true;
                }
                _ => return Err(ColumnConstraintsError::UnknownConstraint(s.as_ref().to_string())),
            }

        }

        Ok(ccs)
    }
}

impl<S> TryFrom<S> for ColumnType
    where S: AsRef<str>
{
    type Err = ColumnTypeError;

    fn try_from(s: S) -> Result<ColumnType, Self::Err> {
        match s.as_ref() {
            "STRING" => Ok(ColumnType::String),
            "BOOL" => Ok(ColumnType::Bool),
            "INTEGER" => Ok(ColumnType::Integer),
            "DECIMAL" => Ok(ColumnType::Decimal),
            "DATE" => Ok(ColumnType::Date),
            "DATETIME" => Ok(ColumnType::DateTime),
            "TIME" => Ok(ColumnType::Time),
            _ if ENUM_EXPR_RE.is_match(s.as_ref()) => {
                let variants: Vec<_> = ENUM_EXPR_RE
                    .captures(s.as_ref())
                    .safe_unwrap("match already exists")
                    .get(1)
                    .safe_unwrap("group 1 exists in regex")
                    .as_str()
                    .split(',')
                    .map(|s| s.to_owned())
                    .collect();

                Ok(ColumnType::Enum(variants))
            }
            _ => {
                if s.as_ref().starts_with("ENUM") {
                    return Err(ColumnTypeError::BadEnum(s.as_ref().to_owned()));
                }
                return Err(ColumnTypeError::UnknownType(s.as_ref().to_owned()));
            }

        }
    }
}

#[derive(Clone, Debug)]
pub struct CsvxColumnType {
    pub id: String,
    pub ty: ColumnType,
    pub constraints: ColumnConstraints,
    pub description: String,
}

#[derive(Debug)]
pub enum Value {
    String(String),
    Bool(bool),
    Integer(i64),
    Enum(String),
    Decimal(String),
    Date(NaiveDate),
    DateTime(NaiveDateTime),
    Time(NaiveTime),
}

impl CsvxColumnType {
    pub fn validate_value<S: AsRef<str>>(&self, s: &S) -> Result<Option<Value>, ValueError> {
        // FIXME: check UNIQUE

        // null check
        if s.as_ref() == "" {
            if self.constraints.nullable {
                return Ok(None);
            } else {
                return Err(ValueError::NonNullable);
            }
        }

        match self.ty {
            ColumnType::String => Ok(Some(Value::String(s.as_ref().to_string()))),
            ColumnType::Bool => {
                match s.as_ref() {
                    "TRUE" => Ok(Some(Value::Bool(true))),
                    "FALSE" => Ok(Some(Value::Bool(false))),
                    _ => Err(ValueError::InvalidBool(s.as_ref().to_owned())),
                }
            }
            ColumnType::Integer => {
                // FIXME: check for leading zeros
                Ok(Some(Value::Integer(s.as_ref()
                                           .parse()
                                           .map_err(|_| {
                                                        ValueError::InvalidInt(s.as_ref()
                                                                                .to_owned())
                                                    })?)))
            }
            ColumnType::Enum(ref variants) => {
                let v = s.as_ref().to_owned();
                if variants.contains(&v) {
                    Ok(Some(Value::Enum(v)))
                } else {
                    Err(ValueError::InvalidEnum(s.as_ref().to_owned(), variants.clone()))
                }
            }
            ColumnType::Decimal => {
                if DECIMAL_RE.is_match(s.as_ref()) {
                    Ok(Some(Value::Decimal(s.as_ref().to_owned())))
                } else {
                    Err(ValueError::InvalidDecimal(s.as_ref().to_owned()))
                }
            }
            ColumnType::Date => {
                match DATE_RE.captures(s.as_ref()) {
                    Some(ref c) => {
                        Ok(Some(Value::Date(NaiveDate::from_ymd_opt(cap(c, 1),
                                                                    cap(c, 2),
                                                                    cap(c, 3))
              .ok_or_else(||{
                      ValueError::InvalidDate(s.as_ref().to_owned ()) }
                      )?)))
                    }
                    None => Err(ValueError::InvalidDate(s.as_ref().to_owned())),
                }
            }
            ColumnType::DateTime => {
                match DATETIME_RE.captures(s.as_ref()) {
                    Some(ref c) => {
                        let dt =
                            NaiveDate::from_ymd_opt(cap(c, 1), cap(c, 2), cap(c, 3))
                                .ok_or_else(|| ValueError::InvalidDate(s.as_ref().to_string()))?;
                        Ok(Some(Value::DateTime(dt.and_hms_opt(cap(c, 4), cap(c, 5), cap(c, 6))
                                                    .ok_or_else(|| {
                                                                    ValueError::InvalidTime (s.as_ref ().to_string ()) })?)))
                    }
                    None => Err(ValueError::InvalidDateTime(s.as_ref().to_string())),
                }
            }
            ColumnType::Time => {
                match TIME_RE.captures(s.as_ref()) {
                    Some(ref c) => {
                        Ok(Some(Value::Time(NaiveTime::from_hms_opt(cap(c, 1),
                                                                    cap(c, 2),
                                                                    cap(c, 3))
                                                    .ok_or_else(||
                                                        ValueError::InvalidTime (s.as_ref ().to_string ()))?)))
                    }
                    None => Err(ValueError::InvalidTime(s.as_ref().to_string())),
                }
            }
        }
    }
}

#[derive(Clone, Debug)]
pub struct CsvxSchema {
    columns: Vec<CsvxColumnType>,
}

impl CsvxSchema {
    pub fn iter_columns(&self) -> slice::Iter<CsvxColumnType> {
        self.columns.iter()
    }

    pub fn from_file<P: AsRef<path::Path>>
        (filename: P)
         -> Result<CsvxSchema, ErrorAtLocation<SchemaLoadError, Location>> {

        // have a copy of the filename as a string ready for error locations
        let filename_s = filename.as_ref().to_string_lossy().to_string();

        let mut rdr = csv::Reader::from_file(filename)
            .err_at(|| Location::File(filename_s.clone()))?
            .has_headers(false);

        let mut it = rdr.decode();
        let header: Option<Result<(String, String, String, String), _>> = it.next();

        let mut columns = Vec::new();

        match header {
            None => {
                return Err(SchemaLoadError::MissingHeader.at(Location::FileLine(filename_s, 1)))
            }
            Some(res) => {
                let fields = res.err_at(|| Location::File(filename_s.clone()))?;
                if fields.0 != "id" || fields.1 != "type" || fields.2 != "constraints" ||
                   fields.3 != "description" {

                    return Err(SchemaLoadError::BadHeader.at(Location::FileLine(filename_s, 1)));
                }

                for (recno, rec) in it.enumerate() {
                    let (id, ty, constraints, desc) =
                        rec.err_at(|| Location::FileLine(filename_s.clone(), 1))?;
                    let lineno = recno + 2;

                    // check identifier
                    if !IDENT_UNDERSCORE_RE.is_match(&id.as_str()) {
                        return Err(SchemaLoadError::BadIdentifier(id)
                                       .at(Location::FileLineField(filename_s, lineno, 1)));
                    }

                    // create type
                    let col_type = match ColumnType::try_from(ty.as_str()) {
                        Ok(v) => v,
                        Err(e) => {
                            return Err(SchemaLoadError::BadType(e)
                                           .at(Location::FileLineField(filename_s, lineno, 1)))
                        }
                    };

                    // create constraints
                    let col_constraints = match ColumnConstraints::try_from(constraints.as_str()) {
                        Ok(v) => v,
                        // FIXME: location
                        Err(e) => {
                            return Err(SchemaLoadError::BadConstraints(e)
                                           .at(Location::FileLine(filename_s, lineno)))
                        }
                    };

                    let col = CsvxColumnType {
                        id: id,
                        ty: col_type,
                        constraints: col_constraints,
                        description: desc,
                    };

                    columns.push(col)
                }

                Ok(CsvxSchema { columns: columns })
            }
        }
    }

    pub fn validate_file<P: AsRef<path::Path>>
        (&self,
         filename: P)
         -> Result<(), Vec<ErrorAtLocation<ValidationError, Location>>> {
        let filename_s = filename.as_ref().to_string_lossy().to_string();

        let mut rdr = csv::Reader::from_file(filename)
            .map_err(|e| vec![e.at(Location::File(filename_s.clone()))])?
            .has_headers(true);

        let headers = rdr.headers()
            .map_err(|e| vec![e.at(Location::FileLine(filename_s.clone(), 1))])?;

        if headers.len() != self.columns.len() {
            return Err(vec![ValidationError::MissingHeaders
                                .at(Location::FileLine(filename_s.clone(), 1))]);
        }

        let mut errs = Vec::new();

        for (idx, (spec, actual)) in self.columns.iter().zip(headers.iter()).enumerate() {
            if spec.id.as_str() != actual {
                errs.push(ValidationError::HeaderMismatch(actual.to_string())
                              .at(Location::FileLineField(filename_s.clone(), 1, idx + 1)));
            }
        }

        // bail if headers are incorrect
        if errs.len() != 0 {
            return Err(errs);
        }

        for (rowid, row) in rdr.records().enumerate() {
            let lineno = rowid + 2;

            // bail early if we cannot read the fields, this is probably a
            // major csv issue
            let fields = row.map_err(|e| vec![e.at(Location::FileLine(filename_s.clone(), 1))])?;

            for (idx, (col, value)) in self.columns.iter().zip(fields.iter()).enumerate() {
                if let Err(e) = col.validate_value(value) {
                    let col_idx = idx + 1;

                    errs.push(ValidationError::ValueError(e)
                                  .at(Location::FileLineField(filename_s.clone(),
                                                              lineno,
                                                              col_idx)));
                    continue;
                }
            }
        }

        if errs.len() != 0 {
            return Err(errs);
        } else {
            Ok(())
        }
    }

    pub fn parse_row<T: AsRef<[String]>>(&self, fields: &T)
                 -> Result<Vec<Option<Value>>, ErrorAtLocation<ValidationError, usize>> {
        let mut rv = Vec::with_capacity(self.columns.len());
        let fields = fields.as_ref();
        for (idx, (col, value)) in self.columns.iter().zip(fields.iter()).enumerate() {
            match col.validate_value(value) {
                Err(e) => {
                    let col_idx = idx + 1;

                    return Err(ValidationError::ValueError(e).at(col_idx));
                }
                Ok(v) => rv.push(v),
            }
        }
        Ok(rv)
    }
}

#[inline]
fn cap<T>(c: &regex::Captures, idx: usize) -> T
    where T: std::str::FromStr,
          T::Err: std::fmt::Debug
{
    c.get(idx)
        .safe_unwrap("valid group")
        .as_str()
        .parse()
        .safe_unwrap("already validated through regex")

}

pub fn parse_filename<S: AsRef<str>>(filename: S) -> Option<CsvxMetadata> {
    match FN_RE.captures(filename.as_ref()) {
        Some(caps) => {
            let table_name = caps.get(1)
                .safe_unwrap("known group")
                .as_str()
                .to_string();
            let year = cap(&caps, 2);
            let month = cap(&caps, 3);
            let day = cap(&caps, 4);
            let schema = caps.get(5)
                .safe_unwrap("known group")
                .as_str()
                .to_string();

            Some(CsvxMetadata {
                     table_name: table_name,
                     date: match NaiveDate::from_ymd_opt(year, month, day) {
                         Some(d) => d,
                         None => return None,
                     },
                     schema: schema,
                 })
        }
        None => None,
    }
}

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

    use chrono::NaiveDate;

    #[test]
    fn filename_parsing_rejects_invalid() {
        assert_eq!(parse_filename("asdf"), None);
        assert_eq!(parse_filename(""), None);
        assert_eq!(parse_filename("test.csv"), None);
        assert_eq!(parse_filename("test.csv"), None);
    }

    #[test]
    fn filename_parsing_parses_valid() {
        assert_eq!(parse_filename("zoo-nyc_20170401_animals-2.csv").unwrap(),
                   CsvxMetadata {
                       table_name: "zoo-nyc".to_owned(),
                       date: NaiveDate::from_ymd(2017, 04, 01),
                       schema: "animals-2".to_owned(),
                   });
    }

}
<|endoftext|>


<commit_before>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! extern crate sha1;
//! # fn main() {
//!
//! let mut m = sha1::Sha1::new();
//! m.update("Hello World!".as_bytes());
//! assert_eq!(m.hexdigest().as_slice(),
//!            "2ef7bde608ce5404e97d5f042f95f89f1c232871");
//! # }
//! ```

#![feature(core, collections, old_io)]
#![unstable]

use std::old_io::BufWriter;
use std::num::Int;

/// Represents a Sha1 hash object in memory.
#[derive(Clone)]
pub struct Sha1 {
    state: [u32; 5],
    data: Vec<u8>,
    len: u64,
}

const DEFAULT_STATE : [u32; 5] =
    [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

fn to_hex(input: &[u8]) -> String {
    let mut s = String::new();
    for b in input.iter() {
        s.push_str(&format!("{:02x}", *b));
    }
    return s;
}

impl Sha1 {

    /// Creates an fresh sha1 hash object.
    pub fn new() -> Sha1 {
        Sha1 {
            state: DEFAULT_STATE,
            data: Vec::new(),
            len: 0,
        }
    }

    fn process_block(&mut self, block: &[u8]) {
        assert_eq!(block.len(), 64);

        let mut words = [0u32; 80];
        for (i, chunk) in block.chunks(4).enumerate() {
            words[i] = (chunk[3] as u32) |
                       ((chunk[2] as u32) << 8) |
                       ((chunk[1] as u32) << 16) |
                       ((chunk[0] as u32) << 24);
        }

        fn ff(b: u32, c: u32, d: u32) -> u32 { d ^ (b & (c ^ d)) }
        fn gg(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }
        fn hh(b: u32, c: u32, d: u32) -> u32 { (b & c) | (d & (b | c)) }
        fn ii(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }

        for i in range(16, 80) {
            let n = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
            words[i] = n.rotate_left(1);
        }

        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];

        for i in range(0, 80) {
            let (f, k) = match i {
                0 ... 19 => (ff(b, c, d), 0x5a827999),
                20 ... 39 => (gg(b, c, d), 0x6ed9eba1),
                40 ... 59 => (hh(b, c, d), 0x8f1bbcdc),
                60 ... 79 => (ii(b, c, d), 0xca62c1d6),
                _ => (0, 0),
            };

            let tmp = a.rotate_left(5)
                .wrapping_add(f)
                .wrapping_add(e)
                .wrapping_add(k)
                .wrapping_add(words[i]);
            e = d;
            d = c;
            c = b.rotate_left(30);
            b = a;
            a = tmp;
        }

        self.state[0] = self.state[0].wrapping_add(a);
        self.state[1] = self.state[1].wrapping_add(b);
        self.state[2] = self.state[2].wrapping_add(c);
        self.state[3] = self.state[3].wrapping_add(d);
        self.state[4] = self.state[4].wrapping_add(e);
    }

    /// Resets the hash object to it's initial state.
    pub fn reset(&mut self) {
        self.state = DEFAULT_STATE;
        self.data.clear();
        self.len = 0;
    }

    /// Update hash with input data.
    pub fn update(&mut self, data: &[u8]) {
        let mut d = self.data.clone();
        self.data.clear();

        d.push_all(data);

        for chunk in d.chunks(64) {
            if chunk.len() == 64 {
                self.len += chunk.len() as u64;
                self.process_block(chunk);
            } else {
                self.data.push_all(chunk);
            }
        }
    }

    /// Retrieve digest result.  The output must be large enough to
    /// contain result (20 bytes).
    pub fn output(&self, out: &mut [u8]) {
        // these are unlikely to fail, since we're writing to memory
        #![allow(unused_must_use)]

        let mut m = Sha1 {
            state: self.state,
            data: Vec::new(),
            len: 0,
        };

        let mut w : Vec<u8> = Vec::new();
        w.write_all(&self.data);
        w.write_u8(0x80 as u8);
        let padding = 64 - ((self.data.len() + 9) % 64);
        for _ in range(0, padding) {
            w.write_u8(0u8);
        }

        w.write_be_u64((self.data.len() as u64 + self.len) * 8);
        for chunk in w.chunks(64) {
            m.process_block(chunk);
        }

        let mut w = BufWriter::new(out);
        for &n in m.state.iter() {
            w.write_be_u32(n);
        }
    }

    /// Shortcut for getting `output` into a new vector.
    pub fn digest(&self) -> Vec<u8> {
        let mut buf = [0u8; 20].to_vec();
        self.output(&mut buf);
        buf
    }

    /// Shortcut for getting a hex output of the vector.
    pub fn hexdigest(&self) -> String {
        to_hex(&self.digest())
    }
}

#[test]
fn test_simple() {
    let mut m = Sha1::new();

    let tests = [
        ("The quick brown fox jumps over the lazy dog",
         "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
        ("The quick brown fox jumps over the lazy cog",
         "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
        ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
        ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
        ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
         "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
    ];

    for &(s, ref h) in tests.iter() {
        let data = s.as_bytes();

        m.reset();
        m.update(data);
        let hh = m.hexdigest();

        assert_eq!(hh.len(), h.len());
		assert_eq!(hh, *h);
    }
}
<commit_msg>Test for multiple updates<commit_after>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! extern crate sha1;
//! # fn main() {
//!
//! let mut m = sha1::Sha1::new();
//! m.update("Hello World!".as_bytes());
//! assert_eq!(m.hexdigest().as_slice(),
//!            "2ef7bde608ce5404e97d5f042f95f89f1c232871");
//! # }
//! ```

#![feature(core, collections, old_io)]
#![unstable]

use std::old_io::BufWriter;
use std::num::Int;

/// Represents a Sha1 hash object in memory.
#[derive(Clone)]
pub struct Sha1 {
    state: [u32; 5],
    data: Vec<u8>,
    len: u64,
}

const DEFAULT_STATE : [u32; 5] =
    [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

fn to_hex(input: &[u8]) -> String {
    let mut s = String::new();
    for b in input.iter() {
        s.push_str(&format!("{:02x}", *b));
    }
    return s;
}

impl Sha1 {

    /// Creates an fresh sha1 hash object.
    pub fn new() -> Sha1 {
        Sha1 {
            state: DEFAULT_STATE,
            data: Vec::new(),
            len: 0,
        }
    }

    fn process_block(&mut self, block: &[u8]) {
        assert_eq!(block.len(), 64);

        let mut words = [0u32; 80];
        for (i, chunk) in block.chunks(4).enumerate() {
            words[i] = (chunk[3] as u32) |
                       ((chunk[2] as u32) << 8) |
                       ((chunk[1] as u32) << 16) |
                       ((chunk[0] as u32) << 24);
        }

        fn ff(b: u32, c: u32, d: u32) -> u32 { d ^ (b & (c ^ d)) }
        fn gg(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }
        fn hh(b: u32, c: u32, d: u32) -> u32 { (b & c) | (d & (b | c)) }
        fn ii(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }

        for i in range(16, 80) {
            let n = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
            words[i] = n.rotate_left(1);
        }

        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];

        for i in range(0, 80) {
            let (f, k) = match i {
                0 ... 19 => (ff(b, c, d), 0x5a827999),
                20 ... 39 => (gg(b, c, d), 0x6ed9eba1),
                40 ... 59 => (hh(b, c, d), 0x8f1bbcdc),
                60 ... 79 => (ii(b, c, d), 0xca62c1d6),
                _ => (0, 0),
            };

            let tmp = a.rotate_left(5)
                .wrapping_add(f)
                .wrapping_add(e)
                .wrapping_add(k)
                .wrapping_add(words[i]);
            e = d;
            d = c;
            c = b.rotate_left(30);
            b = a;
            a = tmp;
        }

        self.state[0] = self.state[0].wrapping_add(a);
        self.state[1] = self.state[1].wrapping_add(b);
        self.state[2] = self.state[2].wrapping_add(c);
        self.state[3] = self.state[3].wrapping_add(d);
        self.state[4] = self.state[4].wrapping_add(e);
    }

    /// Resets the hash object to it's initial state.
    pub fn reset(&mut self) {
        self.state = DEFAULT_STATE;
        self.data.clear();
        self.len = 0;
    }

    /// Update hash with input data.
    pub fn update(&mut self, data: &[u8]) {
        let mut d = self.data.clone();
        self.data.clear();

        d.push_all(data);

        for chunk in d.chunks(64) {
            if chunk.len() == 64 {
                self.len += chunk.len() as u64;
                self.process_block(chunk);
            } else {
                self.data.push_all(chunk);
            }
        }
    }

    /// Retrieve digest result.  The output must be large enough to
    /// contain result (20 bytes).
    pub fn output(&self, out: &mut [u8]) {
        // these are unlikely to fail, since we're writing to memory
        #![allow(unused_must_use)]

        let mut m = Sha1 {
            state: self.state,
            data: Vec::new(),
            len: 0,
        };

        let mut w : Vec<u8> = Vec::new();
        w.write_all(&self.data);
        w.write_u8(0x80 as u8);
        let padding = 64 - ((self.data.len() + 9) % 64);
        for _ in range(0, padding) {
            w.write_u8(0u8);
        }

        w.write_be_u64((self.data.len() as u64 + self.len) * 8);
        for chunk in w.chunks(64) {
            m.process_block(chunk);
        }

        let mut w = BufWriter::new(out);
        for &n in m.state.iter() {
            w.write_be_u32(n);
        }
    }

    /// Shortcut for getting `output` into a new vector.
    pub fn digest(&self) -> Vec<u8> {
        let mut buf = [0u8; 20].to_vec();
        self.output(&mut buf);
        buf
    }

    /// Shortcut for getting a hex output of the vector.
    pub fn hexdigest(&self) -> String {
        to_hex(&self.digest())
    }
}

#[test]
fn test_simple() {
    let mut m = Sha1::new();

    let tests = [
        ("The quick brown fox jumps over the lazy dog",
         "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
        ("The quick brown fox jumps over the lazy cog",
         "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
        ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
        ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
        ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
         "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
    ];

    for &(s, ref h) in tests.iter() {
        let data = s.as_bytes();

        m.reset();
        m.update(data);
        let hh = m.hexdigest();

        assert_eq!(hh.len(), h.len());
		assert_eq!(hh, *h);
    }
}

#[test]
fn test_multiple_updates() {
    let mut m = Sha1::new();

    m.reset();
    m.update("The quick brown ".as_bytes());
    m.update("fox jumps over ".as_bytes());
    m.update("the lazy dog".as_bytes());
    let hh = m.hexdigest();

    let h = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12";
    assert_eq!(hh.len(), h.len());
    assert_eq!(hh, &*h);
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display};

/// a simple recursive type which is able to render its
/// components in a tree-like format
#[derive(Debug)]
pub struct Tree<D: Display>(D, Vec<Tree<D>>);

impl<D: Display> Tree<D> {
    pub fn new(
        root: D,
        leaves: Vec<Tree<D>>,
    ) -> Tree<D> {
        Tree(root, leaves)
    }

    pub fn root(root: D) -> Tree<D> {
        Tree(root, Vec::new())
    }

    pub fn push(
        &mut self,
        leaf: Tree<D>,
    ) -> &mut Self {
        self.1.push(leaf);
        self
    }

    fn display_leaves(
        f: &mut fmt::Formatter,
        leaves: &[Tree<D>],
        spaces: Vec<bool>,
    ) -> fmt::Result {
        for (i, leaf) in leaves.iter().enumerate() {
            let last = i >= leaves.len() - 1;
            // print single line
            for s in &spaces {
                if *s {
                    write!(f, "    ")?;
                } else {
                    write!(f, "|   ")?;
                }
            }
            if last {
                writeln!(f, "└── {}", leaf.0)?;
            } else {
                writeln!(f, "├── {}", leaf.0)?;
            }

            // recurse
            if !leaf.1.is_empty() {
                let mut clone = spaces.clone();
                clone.push(last);
                Self::display_leaves(f, &leaf.1, clone)?;
            }
        }
        write!(f, "")
    }
}

impl<D: Display> Display for Tree<D> {
    fn fmt(
        &self,
        f: &mut fmt::Formatter,
    ) -> fmt::Result {
        writeln!(f, "{}", self.0)?;
        Self::display_leaves(f, &self.1, Vec::new())
    }
}

#[cfg(test)]
mod tests {
    use super::Tree;
    #[test]
    fn render_tree_root() {
        let tree = Tree::root("foo");
        assert_eq!(format!("{}", tree), "foo\n")
    }

    #[test]
    fn render_tree_with_leaves() {
        let tree = Tree::new("foo", vec![Tree::new("bar", vec![Tree::root("baz")])]);
        assert_eq!(
            format!("{}", tree),
            r#"foo
└── bar
    └── baz
"#
        )
    }
}
<commit_msg>fix: use correct character for vertical line<commit_after>use std::fmt::{self, Display};

/// a simple recursive type which is able to render its
/// components in a tree-like format
#[derive(Debug)]
pub struct Tree<D: Display>(D, Vec<Tree<D>>);

impl<D: Display> Tree<D> {
    pub fn new(
        root: D,
        leaves: Vec<Tree<D>>,
    ) -> Tree<D> {
        Tree(root, leaves)
    }

    pub fn root(root: D) -> Tree<D> {
        Tree(root, Vec::new())
    }

    pub fn push(
        &mut self,
        leaf: Tree<D>,
    ) -> &mut Self {
        self.1.push(leaf);
        self
    }

    fn display_leaves(
        f: &mut fmt::Formatter,
        leaves: &[Tree<D>],
        spaces: Vec<bool>,
    ) -> fmt::Result {
        for (i, leaf) in leaves.iter().enumerate() {
            let last = i >= leaves.len() - 1;
            // print single line
            for s in &spaces {
                if *s {
                    write!(f, "    ")?;
                } else {
                    write!(f, "│   ")?;
                }
            }
            if last {
                writeln!(f, "└── {}", leaf.0)?;
            } else {
                writeln!(f, "├── {}", leaf.0)?;
            }

            // recurse
            if !leaf.1.is_empty() {
                let mut clone = spaces.clone();
                clone.push(last);
                Self::display_leaves(f, &leaf.1, clone)?;
            }
        }
        write!(f, "")
    }
}

impl<D: Display> Display for Tree<D> {
    fn fmt(
        &self,
        f: &mut fmt::Formatter,
    ) -> fmt::Result {
        writeln!(f, "{}", self.0)?;
        Self::display_leaves(f, &self.1, Vec::new())
    }
}

#[cfg(test)]
mod tests {
    use super::Tree;
    #[test]
    fn render_tree_root() {
        let tree = Tree::root("foo");
        assert_eq!(format!("{}", tree), "foo\n")
    }

    #[test]
    fn render_tree_with_leaves() {
        let tree = Tree::new("foo", vec![Tree::new("bar", vec![Tree::root("baz")])]);
        assert_eq!(
            format!("{}", tree),
            r#"foo
└── bar
    └── baz
"#
        )
    }
}
<|endoftext|>


<commit_before>//! A simple logger for the [`log`](https://crates.io/crates/log) facade. One
//! log message is written per line. Each line also includes the time it was
//! logged, the logging level and the ID of the thread. See
//! [`SimpleLogger`](struct.SimpleLogger.html) for more details.
//!
//! # Examples
//!
//! Most users will simply need to call [`log_to_file()`](fn.log_to_file.html)
//! with the path to the log file and minimum log level:
//!
//! ```rust
//! # extern crate log;
//! # extern crate simple_logging;
//! use log::LogLevelFilter;
//!
//! # fn main() {
//! simple_logging::log_to_file("test.log", LogLevelFilter::Info);
//! # }
//! ```
//!
//! Or use [`log_to_stderr()`](fn.log_to_stderr.html) if simply logging to
//! `stderr`:
//!
//! ```rust
//! # extern crate log;
//! # extern crate simple_logging;
//! use log::LogLevelFilter;
//!
//! # fn main() {
//! simple_logging::log_to_stderr(LogLevelFilter::Info);
//! # }
//! ```
//!
//! For more control, [`log_to()`](fn.log_to.html) can be used with an
//! arbitrary sink implementing
//! [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html):
//!
//! ```rust
//! # extern crate log;
//! # extern crate simple_logging;
//! use log::LogLevelFilter;
//! use std::io;
//!
//! # fn main() {
//! simple_logging::log_to(io::sink(), LogLevelFilter::Info);
//! # }
//! ```
//!
//! # Performance
//!
//! The logger relies on a global lock to serialize access to the user supplied
//! sink.

#[cfg(not(test))]
extern crate log;
extern crate thread_id;

#[cfg(test)]
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate regex;

use log::{Log, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::Path;
use std::sync::Mutex;
use std::time::Instant;

/// A simple logger for the [`log`](https://crates.io/crates/log) facade.
///
/// Each and every log message obeys the following fixed and easily-parsable
/// format:
///
/// ```text
/// [<hh>:<mm>:<ss>.<SSS>] (<thread-id>) <level> <message>\n
/// ```
///
/// Where `<hh>` denotes hours zero-padded to at least two digits, `<mm>`
/// denotes minutes zero-padded to two digits, `<ss>` denotes seconds
/// zero-padded to two digits and `<SSS>` denotes miliseconds zero-padded to
/// three digits. `<thread-id>` is an implementation-specific alphanumeric ID.
/// `<level>` is the log level as defined by `log::LogLevel` and padded right
/// with spaces. `<message>` is the log message. Note that `<message>` is
/// written to the log as-is, including any embedded newlines.
///
/// # Examples
///
/// `SimpleLogger` implements
/// [`log::Log`](https://docs.rs/log/0.3/log/trait.Log.html), and as such may
/// be used with
/// [`log::set_logger`](https://docs.rs/log/0.3/log/fn.set_logger.html)
/// directly:
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
/// use simple_logging::SimpleLogger;
/// use std::io;
///
/// # fn main() {
/// log::set_logger(|max_log_level| {
///     max_log_level.set(LogLevelFilter::Info);
///
///     Box::new(SimpleLogger::new(io::sink()))
/// });
/// # }
/// ```
///
/// However, because this is the expected way to use `SimpleLogger`, there is a
/// shorthand function for the operation above: [`log_to()`](fn.log_to.html).
///
/// # Errors
///
/// Any errors returned by the sink when writing are ignored.
///
/// # See also
///
/// [`log_to_file()`](fn.log_to_file.html),
/// [`log_to_stderr()`](fn.log_to_stderr.html), [`log_to()`](fn.log_to.html)
pub struct SimpleLogger<T: Write> {
    start: Instant,
    sink:  Mutex<T>,
}

impl<T: Write> SimpleLogger<T> {
    /// Create a new `SimpleLogger`.
    pub fn new(sink: T) -> Self {
        SimpleLogger {
            start: Instant::now(),
            sink:  Mutex::new(sink),
        }
    }
}

impl<T: Write + Send + Sync> Log for SimpleLogger<T> {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        if !self.enabled(record.metadata()) {
            return;
        }

        let now         = self.start.elapsed();
        let seconds     = now.as_secs();
        let hours       = seconds / 3600;
        let minutes     = (seconds / 60) % 60;
        let seconds     = seconds % 60;
        let miliseconds = now.subsec_nanos() / 1000000;

        let mut sink = self.sink.lock().unwrap();
        let _        = write!(sink,
            "[{:02}:{:02}:{:02}.{:03}] ({}) {:6} {}\n", hours, minutes,
            seconds, miliseconds, thread_id::get(), record.level(),
            record.args());
    }
}

/// Configure the [`log`](https://crates.io/crates/log) facade to log to a file
/// through a [`SimpleLogger`](struct.SimpleLogger.html).
///
/// # Examples
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
///
/// # fn main() {
/// simple_logging::log_to_file("test.log", LogLevelFilter::Info);
/// # }
/// ```
pub fn log_to_file<T: AsRef<Path>>(path: T, max_log_level: LogLevelFilter)
        -> io::Result<()> {
    let file = File::create(path)?;

    log_to(file, max_log_level)
        // Wrap SetLoggerError into an io::Error just to avoid defining a new
        // error type
        .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}

/// Configure the [`log`](https://crates.io/crates/log) facade to log to
/// `stderr` through a [`SimpleLogger`](struct.SimpleLogger.html).
///
/// # Examples
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
///
/// # fn main() {
/// simple_logging::log_to_stderr(LogLevelFilter::Info);
/// # }
/// ```
pub fn log_to_stderr(max_log_level: LogLevelFilter)
        -> Result<(), SetLoggerError> {
    log_to(io::stderr(), max_log_level)
}

/// Configure the [`log`](https://crates.io/crates/log) facade to log to a
/// custom sink through a [`SimpleLogger`](struct.SimpleLogger.html).
///
/// # Examples
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
/// use std::io;
///
/// # fn main() {
/// simple_logging::log_to(io::sink(), LogLevelFilter::Info);
/// # }
/// ```
pub fn log_to<T: Write + Send + Sync + 'static>(sink: T,
        max_log_level: LogLevelFilter) -> Result<(), SetLoggerError> {
    log::set_logger(|log_max_log_level| {
        log_max_log_level.set(max_log_level);

        Box::new(SimpleLogger::new(sink))
    })
}

#[cfg(test)]
mod tests {
    use log_to;

    use log::LogLevelFilter::Info;
    use regex::Regex;
    use std::io;
    use std::io::Write;
    use std::str;
    use std::sync::{Arc, Mutex};

    struct VecProxy(Arc<Mutex<Vec<u8>>>);

    impl Write for VecProxy {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.0.lock().unwrap().write(buf)
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    // The `log` API forbids calling `set_logger()` more than once in the
    // lifetime of a single program (even after a `shutdown_logger()`), so
    // we stash all tests in a single function.
    // TODO: increase coverage by making `log_to*()` tests integration tests.
    #[test]
    fn test() {
        let buf   = Arc::new(Mutex::new(Vec::new()));
        let proxy = VecProxy(buf.clone());
        log_to(proxy, Info).unwrap();

        // Test filtering
        debug!("filtered");
        assert!(buf.lock().unwrap().is_empty());

        // Test message format
        let pat  = Regex::new(r"^\[\d\d:\d\d:\d\d.\d\d\d] \([0-9a-zA-Z]+\) INFO   test\n$").unwrap();
        info!("test");
        let line = str::from_utf8(&buf.lock().unwrap()).unwrap().to_owned();
        assert!(pat.is_match(&line));
    }
}
<commit_msg>Rustfmt pass<commit_after>//! A simple logger for the [`log`](https://crates.io/crates/log) facade. One
//! log message is written per line. Each line also includes the time it was
//! logged, the logging level and the ID of the thread. See
//! [`SimpleLogger`](struct.SimpleLogger.html) for more details.
//!
//! # Examples
//!
//! Most users will simply need to call [`log_to_file()`](fn.log_to_file.html)
//! with the path to the log file and minimum log level:
//!
//! ```rust
//! # extern crate log;
//! # extern crate simple_logging;
//! use log::LogLevelFilter;
//!
//! # fn main() {
//! simple_logging::log_to_file("test.log", LogLevelFilter::Info);
//! # }
//! ```
//!
//! Or use [`log_to_stderr()`](fn.log_to_stderr.html) if simply logging to
//! `stderr`:
//!
//! ```rust
//! # extern crate log;
//! # extern crate simple_logging;
//! use log::LogLevelFilter;
//!
//! # fn main() {
//! simple_logging::log_to_stderr(LogLevelFilter::Info);
//! # }
//! ```
//!
//! For more control, [`log_to()`](fn.log_to.html) can be used with an
//! arbitrary sink implementing
//! [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html):
//!
//! ```rust
//! # extern crate log;
//! # extern crate simple_logging;
//! use log::LogLevelFilter;
//! use std::io;
//!
//! # fn main() {
//! simple_logging::log_to(io::sink(), LogLevelFilter::Info);
//! # }
//! ```
//!
//! # Performance
//!
//! The logger relies on a global lock to serialize access to the user supplied
//! sink.

#[cfg(not(test))]
extern crate log;
extern crate thread_id;

#[cfg(test)]
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate regex;

use log::{Log, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::Path;
use std::sync::Mutex;
use std::time::Instant;

/// A simple logger for the [`log`](https://crates.io/crates/log) facade.
///
/// Each and every log message obeys the following fixed and easily-parsable
/// format:
///
/// ```text
/// [<hh>:<mm>:<ss>.<SSS>] (<thread-id>) <level> <message>\n
/// ```
///
/// Where `<hh>` denotes hours zero-padded to at least two digits, `<mm>`
/// denotes minutes zero-padded to two digits, `<ss>` denotes seconds
/// zero-padded to two digits and `<SSS>` denotes miliseconds zero-padded to
/// three digits. `<thread-id>` is an implementation-specific alphanumeric ID.
/// `<level>` is the log level as defined by `log::LogLevel` and padded right
/// with spaces. `<message>` is the log message. Note that `<message>` is
/// written to the log as-is, including any embedded newlines.
///
/// # Examples
///
/// `SimpleLogger` implements
/// [`log::Log`](https://docs.rs/log/0.3/log/trait.Log.html), and as such may
/// be used with
/// [`log::set_logger`](https://docs.rs/log/0.3/log/fn.set_logger.html)
/// directly:
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
/// use simple_logging::SimpleLogger;
/// use std::io;
///
/// # fn main() {
/// log::set_logger(|max_log_level| {
///     max_log_level.set(LogLevelFilter::Info);
///
///     Box::new(SimpleLogger::new(io::sink()))
/// });
/// # }
/// ```
///
/// However, because this is the expected way to use `SimpleLogger`, there is a
/// shorthand function for the operation above: [`log_to()`](fn.log_to.html).
///
/// # Errors
///
/// Any errors returned by the sink when writing are ignored.
///
/// # See also
///
/// [`log_to_file()`](fn.log_to_file.html),
/// [`log_to_stderr()`](fn.log_to_stderr.html), [`log_to()`](fn.log_to.html)
pub struct SimpleLogger<T: Write> {
    start: Instant,
    sink:  Mutex<T>,
}

impl<T: Write> SimpleLogger<T> {
    /// Create a new `SimpleLogger`.
    pub fn new(sink: T) -> Self {
        SimpleLogger {
            start: Instant::now(),
            sink:  Mutex::new(sink),
        }
    }
}

impl<T: Write + Send + Sync> Log for SimpleLogger<T> {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        if !self.enabled(record.metadata()) {
            return;
        }

        let now         = self.start.elapsed();
        let seconds     = now.as_secs();
        let hours       = seconds / 3600;
        let minutes     = (seconds / 60) % 60;
        let seconds     = seconds % 60;
        let miliseconds = now.subsec_nanos() / 1000000;

        let mut sink = self.sink.lock().unwrap();
        let _        = write!(
            sink,
            "[{:02}:{:02}:{:02}.{:03}] ({}) {:6} {}\n",
            hours,
            minutes,
            seconds,
            miliseconds,
            thread_id::get(),
            record.level(),
            record.args()
        );
    }
}

/// Configure the [`log`](https://crates.io/crates/log) facade to log to a file
/// through a [`SimpleLogger`](struct.SimpleLogger.html).
///
/// # Examples
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
///
/// # fn main() {
/// simple_logging::log_to_file("test.log", LogLevelFilter::Info);
/// # }
/// ```
pub fn log_to_file<T: AsRef<Path>>(
    path: T,
    max_log_level: LogLevelFilter
) -> io::Result<()> {
    let file = File::create(path)?;

    log_to(file, max_log_level)
        // Wrap SetLoggerError into an io::Error just to avoid defining a new
        // error type
        .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}

/// Configure the [`log`](https://crates.io/crates/log) facade to log to
/// `stderr` through a [`SimpleLogger`](struct.SimpleLogger.html).
///
/// # Examples
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
///
/// # fn main() {
/// simple_logging::log_to_stderr(LogLevelFilter::Info);
/// # }
/// ```
pub fn log_to_stderr(
    max_log_level: LogLevelFilter
) -> Result<(), SetLoggerError> {
    log_to(io::stderr(), max_log_level)
}

/// Configure the [`log`](https://crates.io/crates/log) facade to log to a
/// custom sink through a [`SimpleLogger`](struct.SimpleLogger.html).
///
/// # Examples
///
/// ```rust
/// # extern crate log;
/// # extern crate simple_logging;
/// use log::LogLevelFilter;
/// use std::io;
///
/// # fn main() {
/// simple_logging::log_to(io::sink(), LogLevelFilter::Info);
/// # }
/// ```
pub fn log_to<T: Write + Send + Sync + 'static>(
    sink: T,
    max_log_level: LogLevelFilter
) -> Result<(), SetLoggerError> {
    log::set_logger(|log_max_log_level| {
        log_max_log_level.set(max_log_level);

        Box::new(SimpleLogger::new(sink))
    })
}

#[cfg(test)]
mod tests {
    use log_to;

    use log::LogLevelFilter::Info;
    use regex::Regex;
    use std::io;
    use std::io::Write;
    use std::str;
    use std::sync::{Arc, Mutex};

    struct VecProxy(Arc<Mutex<Vec<u8>>>);

    impl Write for VecProxy {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.0.lock().unwrap().write(buf)
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    // The `log` API forbids calling `set_logger()` more than once in the
    // lifetime of a single program (even after a `shutdown_logger()`), so
    // we stash all tests in a single function.
    // TODO: increase coverage by making `log_to*()` tests integration tests.
    #[test]
    fn test() {
        let buf   = Arc::new(Mutex::new(Vec::new()));
        let proxy = VecProxy(buf.clone());
        log_to(proxy, Info).unwrap();

        // Test filtering
        debug!("filtered");
        assert!(buf.lock().unwrap().is_empty());

        // Test message format
        let pat = Regex::new(r"^\[\d\d:\d\d:\d\d.\d\d\d] \([0-9a-zA-Z]+\) INFO   test\n$").unwrap();
        info!("test");
        let line = str::from_utf8(&buf.lock().unwrap()).unwrap().to_owned();
        assert!(pat.is_match(&line));
    }
}
<|endoftext|>


<commit_before>//! `sled` is a high-performance embedded database with
//! an API that is similar to a `BTreeMap<[u8], [u8]>`,
//! but with several additional capabilities for
//! assisting creators of stateful systems.
//!
//! It is fully thread-safe, and all operations are
//! atomic. Multiple `Tree`s are supported with the
//! [`Db::open_tree`](struct.Db.html#method.open_tree) method.
//!
//! ACID transactions involving reads and writes to
//! multiple items are supported with the
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! method. Transactions may also operate over
//! multiple `Tree`s (see
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! docs for more info).
//!
//! Users may also subscribe to updates on individual
//! `Tree`s by using the
//! [`Tree::watch_prefix`](struct.Tree.html#method.watch_prefix)
//! method, which returns a blocking `Iterator` over
//! updates to keys that begin with the provided
//! prefix. You may supply an empty prefix to subscribe
//! to everything.
//!
//! [Merge operators](https://github.com/spacejam/sled/wiki/merge-operators)
//! (aka read-modify-write operators) are supported. A
//! merge operator is a function that specifies
//! how new data can be merged into an existing value
//! without requiring both a read and a write.
//! Using the
//! [`Tree::merge`](struct.Tree.html#method.merge)
//! method, you may "push" data to a `Tree` value
//! and have the provided merge operator combine
//! it with the existing value, if there was one.
//! They are set on a per-`Tree` basis, and essentially
//! allow any sort of data structure to be built
//! using merges as an atomic high-level operation.
//!
//! `sled` is built by experienced database engineers
//! who think users should spend less time tuning and
//! working against high-friction APIs. Expect
//! significant ergonomic and performance improvements
//! over time. Most surprises are bugs, so please
//! [let us know](mailto:t@jujit.su?subject=sled%20sucks!!!) if something
//! is high friction.
//!
//! # Examples
//!
//! ```
//! use sled::Db;
//!
//! let t = Db::open("my_db").unwrap();
//!
//! // insert and get
//! t.insert(b"yo!", b"v1");
//! assert_eq!(&t.get(b"yo!").unwrap().unwrap(), b"v1");
//!
//! // Atomic compare-and-swap.
//! t.compare_and_swap(
//!     b"yo!",      // key
//!     Some(b"v1"), // old value, None for not present
//!     Some(b"v2"), // new value, None for delete
//! )
//! .unwrap();
//!
//! // Iterates over key-value pairs, starting at the given key.
//! let scan_key: &[u8] = b"a non-present key before yo!";
//! let mut iter = t.range(scan_key..);
//! assert_eq!(&iter.next().unwrap().unwrap().0, b"yo!");
//! assert_eq!(iter.next(), None);
//!
//! t.remove(b"yo!");
//! assert_eq!(t.get(b"yo!"), Ok(None));
//! ```
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/spacejam/sled/master/art/tree_face_anti-transphobia.png"
)]
#![cfg_attr(test, deny(warnings))]
#![deny(
    missing_docs,
    future_incompatible,
    nonstandard_style,
    rust_2018_idioms,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unused_qualifications
)]
#![deny(
    // over time, consider enabling the following commented-out lints:
    // clippy::missing_const_for_fn,
    // clippy::missing_docs_in_private_items,
    // clippy::module_name_repetitions,
    // clippy::multiple_crate_versions,
    // clippy::unimplemented,
    // clippy::wildcard_enum_match_arm,
    // clippy::else_if_without_else,
    // clippy::float_arithmetic,
    // clippy::indexing_slicing,
    clippy::cast_lossless,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::checked_conversions,
    clippy::decimal_literal_representation,
    clippy::doc_markdown,
    clippy::empty_enum,
    clippy::expl_impl_clone_on_copy,
    clippy::explicit_into_iter_loop,
    clippy::explicit_iter_loop,
    clippy::explicit_iter_loop,
    clippy::fallible_impl_from,
    clippy::filter_map,
    clippy::filter_map_next,
    clippy::find_map,
    clippy::get_unwrap,
    clippy::if_not_else,
    clippy::inline_always,
    clippy::invalid_upcast_comparisons,
    clippy::items_after_statements,
    clippy::map_flatten,
    clippy::match_same_arms,
    clippy::maybe_infinite_iter,
    clippy::mem_forget,
    clippy::multiple_inherent_impl,
    clippy::mut_mut,
    clippy::needless_borrow,
    clippy::needless_continue,
    clippy::needless_pass_by_value,
    clippy::non_ascii_literal,
    clippy::option_map_unwrap_or,
    clippy::option_map_unwrap_or_else,
    clippy::path_buf_push_overwrite,
    clippy::print_stdout,
    clippy::pub_enum_variant_names,
    clippy::redundant_closure_for_method_calls,
    clippy::replace_consts,
    clippy::result_map_unwrap_or_else,
    clippy::shadow_reuse,
    clippy::shadow_same,
    clippy::shadow_unrelated,
    clippy::single_match_else,
    clippy::string_add,
    clippy::string_add_assign,
    clippy::type_repetition_in_bounds,
    clippy::unicode_not_nfc,
    clippy::unseparated_literal_suffix,
    clippy::used_underscore_binding,
    clippy::wildcard_dependencies,
    clippy::wrong_pub_self_convention,
)]

#[cfg(feature = "failpoints")]
use fail::fail_point;

macro_rules! maybe_fail {
    ($e:expr) => {
        #[cfg(feature = "failpoints")]
        fail_point!($e, |_| Err(Error::FailPoint));
    };
}

mod batch;
mod binary_search;
mod config;
mod context;
mod db;
mod dll;
mod histogram;
mod iter;
mod ivec;
mod lazy;
mod lru;
mod meta;
mod metrics;
mod node;
mod oneshot;
mod pagecache;
mod prefix;
mod result;
mod subscription;
mod sys_limits;
mod threadpool;
mod transaction;
mod tree;
mod vecset;

#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
mod flusher;

#[cfg(feature = "event_log")]
/// The event log helps debug concurrency issues.
pub mod event_log;

#[cfg(feature = "event_log")]
mod stack;

#[cfg(feature = "measure_allocs")]
mod measure_allocs;

#[cfg(feature = "measure_allocs")]
#[global_allocator]
static ALLOCATOR: measure_allocs::TrackingAllocator =
    measure_allocs::TrackingAllocator;

const DEFAULT_TREE_ID: &[u8] = b"__sled__default";

/// hidden re-export of items for testing purposes
#[doc(hidden)]
pub use {
    self::{
        config::RunningConfig,
        lazy::Lazy,
        pagecache::{
            constants::{
                MAX_SPACE_AMPLIFICATION, MINIMUM_ITEMS_PER_SEGMENT,
                MSG_HEADER_LEN, SEG_HEADER_LEN,
            },
            DiskPtr, Log, LogKind, LogOffset, LogRead, Lsn, PageCache, PageId,
            SegmentMode,
        },
    },
    crossbeam_epoch::{pin, Atomic, Guard, Owned, Shared},
};

pub use self::{
    batch::Batch,
    config::Config,
    db::Db,
    iter::Iter,
    ivec::IVec,
    result::{Error, Result},
    subscription::{Event, Subscriber},
    transaction::{
        abort, ConflictableTransactionError, ConflictableTransactionResult,
        TransactionError, TransactionResult, Transactional, TransactionalTree,
    },
    tree::{CompareAndSwapError, CompareAndSwapResult, Tree},
};

use {
    self::{
        binary_search::binary_search_lub,
        context::Context,
        histogram::Histogram,
        lru::Lru,
        meta::Meta,
        metrics::{clock, measure, Measure, M},
        node::{Data, Node},
        oneshot::{OneShot, OneShotFiller},
        result::CasResult,
        subscription::Subscriptions,
        tree::TreeInner,
        vecset::VecSet,
    },
    bincode::{deserialize, serialize},
    crossbeam_utils::{Backoff, CachePadded},
    log::{debug, error, trace, warn},
    pagecache::RecoveryGuard,
    parking_lot::{Condvar, Mutex, RwLock},
    serde::{Deserialize, Serialize},
    std::{
        collections::BTreeMap,
        convert::TryFrom,
        fmt::{self, Debug},
        io::{Read, Write},
        sync::{
            atomic::{
                AtomicI64 as AtomicLsn, AtomicU64, AtomicUsize,
                Ordering::{Acquire, Relaxed, Release, SeqCst},
            },
            Arc,
        },
    },
};

fn crc32(buf: &[u8]) -> u32 {
    let mut hasher = crc32fast::Hasher::new();
    hasher.update(buf);
    hasher.finalize()
}

#[cfg(any(test, feature = "lock_free_delays"))]
mod debug_delay;

#[cfg(any(test, feature = "lock_free_delays"))]
use debug_delay::debug_delay;

/// This function is useful for inducing random jitter into our atomic
/// operations, shaking out more possible interleavings quickly. It gets
/// fully eliminated by the compiler in non-test code.
#[cfg(not(any(test, feature = "lock_free_delays")))]
const fn debug_delay() {}

/// Link denotes a tree node or its modification fragment such as
/// key addition or removal.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Link {
    /// A new value is set for a given key
    Set(IVec, IVec),
    /// The associated value is removed for a given key
    Del(IVec),
    /// A child of this Index node is marked as mergable
    ParentMergeIntention(PageId),
    /// The merging child has been completely merged into its left sibling
    ParentMergeConfirm,
    /// A Node is marked for being merged into its left sibling
    ChildMergeCap,
}

/// A fast map that is not resistant to collision attacks. Works
/// on 8 bytes at a time.
pub(crate) type FastMap8<K, V> = std::collections::HashMap<
    K,
    V,
    std::hash::BuildHasherDefault<fxhash::FxHasher64>,
>;

/// A fast set that is not resistant to collision attacks. Works
/// on 8 bytes at a time.
pub(crate) type FastSet8<V> = std::collections::HashSet<
    V,
    std::hash::BuildHasherDefault<fxhash::FxHasher64>,
>;

/// Allows arbitrary logic to be injected into mere operations of the
/// `PageCache`.
pub type MergeOperator = fn(
    key: &[u8],
    last_value: Option<&[u8]>,
    new_merge: &[u8],
) -> Option<Vec<u8>>;

fn is_sorted<T: PartialOrd>(xs: &[T]) -> bool {
    xs.windows(2).all(|pair| pair[0] <= pair[1])
}
<commit_msg>Remove redundant clippy check<commit_after>//! `sled` is a high-performance embedded database with
//! an API that is similar to a `BTreeMap<[u8], [u8]>`,
//! but with several additional capabilities for
//! assisting creators of stateful systems.
//!
//! It is fully thread-safe, and all operations are
//! atomic. Multiple `Tree`s are supported with the
//! [`Db::open_tree`](struct.Db.html#method.open_tree) method.
//!
//! ACID transactions involving reads and writes to
//! multiple items are supported with the
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! method. Transactions may also operate over
//! multiple `Tree`s (see
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! docs for more info).
//!
//! Users may also subscribe to updates on individual
//! `Tree`s by using the
//! [`Tree::watch_prefix`](struct.Tree.html#method.watch_prefix)
//! method, which returns a blocking `Iterator` over
//! updates to keys that begin with the provided
//! prefix. You may supply an empty prefix to subscribe
//! to everything.
//!
//! [Merge operators](https://github.com/spacejam/sled/wiki/merge-operators)
//! (aka read-modify-write operators) are supported. A
//! merge operator is a function that specifies
//! how new data can be merged into an existing value
//! without requiring both a read and a write.
//! Using the
//! [`Tree::merge`](struct.Tree.html#method.merge)
//! method, you may "push" data to a `Tree` value
//! and have the provided merge operator combine
//! it with the existing value, if there was one.
//! They are set on a per-`Tree` basis, and essentially
//! allow any sort of data structure to be built
//! using merges as an atomic high-level operation.
//!
//! `sled` is built by experienced database engineers
//! who think users should spend less time tuning and
//! working against high-friction APIs. Expect
//! significant ergonomic and performance improvements
//! over time. Most surprises are bugs, so please
//! [let us know](mailto:t@jujit.su?subject=sled%20sucks!!!) if something
//! is high friction.
//!
//! # Examples
//!
//! ```
//! use sled::Db;
//!
//! let t = Db::open("my_db").unwrap();
//!
//! // insert and get
//! t.insert(b"yo!", b"v1");
//! assert_eq!(&t.get(b"yo!").unwrap().unwrap(), b"v1");
//!
//! // Atomic compare-and-swap.
//! t.compare_and_swap(
//!     b"yo!",      // key
//!     Some(b"v1"), // old value, None for not present
//!     Some(b"v2"), // new value, None for delete
//! )
//! .unwrap();
//!
//! // Iterates over key-value pairs, starting at the given key.
//! let scan_key: &[u8] = b"a non-present key before yo!";
//! let mut iter = t.range(scan_key..);
//! assert_eq!(&iter.next().unwrap().unwrap().0, b"yo!");
//! assert_eq!(iter.next(), None);
//!
//! t.remove(b"yo!");
//! assert_eq!(t.get(b"yo!"), Ok(None));
//! ```
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/spacejam/sled/master/art/tree_face_anti-transphobia.png"
)]
#![cfg_attr(test, deny(warnings))]
#![deny(
    missing_docs,
    future_incompatible,
    nonstandard_style,
    rust_2018_idioms,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unused_qualifications
)]
#![deny(
    // over time, consider enabling the following commented-out lints:
    // clippy::missing_const_for_fn,
    // clippy::missing_docs_in_private_items,
    // clippy::module_name_repetitions,
    // clippy::multiple_crate_versions,
    // clippy::unimplemented,
    // clippy::wildcard_enum_match_arm,
    // clippy::else_if_without_else,
    // clippy::float_arithmetic,
    // clippy::indexing_slicing,
    clippy::cast_lossless,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::checked_conversions,
    clippy::decimal_literal_representation,
    clippy::doc_markdown,
    clippy::empty_enum,
    clippy::expl_impl_clone_on_copy,
    clippy::explicit_into_iter_loop,
    clippy::explicit_iter_loop,
    clippy::fallible_impl_from,
    clippy::filter_map,
    clippy::filter_map_next,
    clippy::find_map,
    clippy::get_unwrap,
    clippy::if_not_else,
    clippy::inline_always,
    clippy::invalid_upcast_comparisons,
    clippy::items_after_statements,
    clippy::map_flatten,
    clippy::match_same_arms,
    clippy::maybe_infinite_iter,
    clippy::mem_forget,
    clippy::multiple_inherent_impl,
    clippy::mut_mut,
    clippy::needless_borrow,
    clippy::needless_continue,
    clippy::needless_pass_by_value,
    clippy::non_ascii_literal,
    clippy::option_map_unwrap_or,
    clippy::option_map_unwrap_or_else,
    clippy::path_buf_push_overwrite,
    clippy::print_stdout,
    clippy::pub_enum_variant_names,
    clippy::redundant_closure_for_method_calls,
    clippy::replace_consts,
    clippy::result_map_unwrap_or_else,
    clippy::shadow_reuse,
    clippy::shadow_same,
    clippy::shadow_unrelated,
    clippy::single_match_else,
    clippy::string_add,
    clippy::string_add_assign,
    clippy::type_repetition_in_bounds,
    clippy::unicode_not_nfc,
    clippy::unseparated_literal_suffix,
    clippy::used_underscore_binding,
    clippy::wildcard_dependencies,
    clippy::wrong_pub_self_convention,
)]

#[cfg(feature = "failpoints")]
use fail::fail_point;

macro_rules! maybe_fail {
    ($e:expr) => {
        #[cfg(feature = "failpoints")]
        fail_point!($e, |_| Err(Error::FailPoint));
    };
}

mod batch;
mod binary_search;
mod config;
mod context;
mod db;
mod dll;
mod histogram;
mod iter;
mod ivec;
mod lazy;
mod lru;
mod meta;
mod metrics;
mod node;
mod oneshot;
mod pagecache;
mod prefix;
mod result;
mod subscription;
mod sys_limits;
mod threadpool;
mod transaction;
mod tree;
mod vecset;

#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
mod flusher;

#[cfg(feature = "event_log")]
/// The event log helps debug concurrency issues.
pub mod event_log;

#[cfg(feature = "event_log")]
mod stack;

#[cfg(feature = "measure_allocs")]
mod measure_allocs;

#[cfg(feature = "measure_allocs")]
#[global_allocator]
static ALLOCATOR: measure_allocs::TrackingAllocator =
    measure_allocs::TrackingAllocator;

const DEFAULT_TREE_ID: &[u8] = b"__sled__default";

/// hidden re-export of items for testing purposes
#[doc(hidden)]
pub use {
    self::{
        config::RunningConfig,
        lazy::Lazy,
        pagecache::{
            constants::{
                MAX_SPACE_AMPLIFICATION, MINIMUM_ITEMS_PER_SEGMENT,
                MSG_HEADER_LEN, SEG_HEADER_LEN,
            },
            DiskPtr, Log, LogKind, LogOffset, LogRead, Lsn, PageCache, PageId,
            SegmentMode,
        },
    },
    crossbeam_epoch::{pin, Atomic, Guard, Owned, Shared},
};

pub use self::{
    batch::Batch,
    config::Config,
    db::Db,
    iter::Iter,
    ivec::IVec,
    result::{Error, Result},
    subscription::{Event, Subscriber},
    transaction::{
        abort, ConflictableTransactionError, ConflictableTransactionResult,
        TransactionError, TransactionResult, Transactional, TransactionalTree,
    },
    tree::{CompareAndSwapError, CompareAndSwapResult, Tree},
};

use {
    self::{
        binary_search::binary_search_lub,
        context::Context,
        histogram::Histogram,
        lru::Lru,
        meta::Meta,
        metrics::{clock, measure, Measure, M},
        node::{Data, Node},
        oneshot::{OneShot, OneShotFiller},
        result::CasResult,
        subscription::Subscriptions,
        tree::TreeInner,
        vecset::VecSet,
    },
    bincode::{deserialize, serialize},
    crossbeam_utils::{Backoff, CachePadded},
    log::{debug, error, trace, warn},
    pagecache::RecoveryGuard,
    parking_lot::{Condvar, Mutex, RwLock},
    serde::{Deserialize, Serialize},
    std::{
        collections::BTreeMap,
        convert::TryFrom,
        fmt::{self, Debug},
        io::{Read, Write},
        sync::{
            atomic::{
                AtomicI64 as AtomicLsn, AtomicU64, AtomicUsize,
                Ordering::{Acquire, Relaxed, Release, SeqCst},
            },
            Arc,
        },
    },
};

fn crc32(buf: &[u8]) -> u32 {
    let mut hasher = crc32fast::Hasher::new();
    hasher.update(buf);
    hasher.finalize()
}

#[cfg(any(test, feature = "lock_free_delays"))]
mod debug_delay;

#[cfg(any(test, feature = "lock_free_delays"))]
use debug_delay::debug_delay;

/// This function is useful for inducing random jitter into our atomic
/// operations, shaking out more possible interleavings quickly. It gets
/// fully eliminated by the compiler in non-test code.
#[cfg(not(any(test, feature = "lock_free_delays")))]
const fn debug_delay() {}

/// Link denotes a tree node or its modification fragment such as
/// key addition or removal.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Link {
    /// A new value is set for a given key
    Set(IVec, IVec),
    /// The associated value is removed for a given key
    Del(IVec),
    /// A child of this Index node is marked as mergable
    ParentMergeIntention(PageId),
    /// The merging child has been completely merged into its left sibling
    ParentMergeConfirm,
    /// A Node is marked for being merged into its left sibling
    ChildMergeCap,
}

/// A fast map that is not resistant to collision attacks. Works
/// on 8 bytes at a time.
pub(crate) type FastMap8<K, V> = std::collections::HashMap<
    K,
    V,
    std::hash::BuildHasherDefault<fxhash::FxHasher64>,
>;

/// A fast set that is not resistant to collision attacks. Works
/// on 8 bytes at a time.
pub(crate) type FastSet8<V> = std::collections::HashSet<
    V,
    std::hash::BuildHasherDefault<fxhash::FxHasher64>,
>;

/// Allows arbitrary logic to be injected into mere operations of the
/// `PageCache`.
pub type MergeOperator = fn(
    key: &[u8],
    last_value: Option<&[u8]>,
    new_merge: &[u8],
) -> Option<Vec<u8>>;

fn is_sorted<T: PartialOrd>(xs: &[T]) -> bool {
    xs.windows(2).all(|pair| pair[0] <= pair[1])
}
<|endoftext|>