<commit_before>use std::io;

/// Circular buffer to store a stream. Read/write boundaries are not preserved.
pub struct StreamBuffer {
    buf: Box<[u8]>,
    head: usize,
    tail: usize,
}

impl StreamBuffer {
    pub fn new(capacity: usize) -> Self {
        Self {
            buf: vec![0; capacity + 1].into_boxed_slice(),
            head: 0,
            tail: 0,
        }
    }

    pub fn is_empty(&self) -> bool{
        self.head == self.tail
    }

    pub fn size(&self) -> usize {
        if self.head < self.tail {
            self.head + self.buf.len() - self.tail
        } else {
            self.head - self.tail
        }
    }

    pub fn capacity(&self) -> usize {
        self.buf.len() - 1
    }

    pub fn remaining(&self) -> usize {
        self.capacity() - self.size()
    }

    pub fn write_to<W: io::Write>(&mut self, destination: &mut W) -> io::Result<usize> {
        if self.head == self.tail {
            // buffer is empty, nothing to do
            Ok(0)
        } else {
            let w;
            if self.head > self.tail {
                let source_slice = &self.buf[self.tail..self.head];
                w = destination.write(source_slice)?;
                self.tail += w;
            } else { // self.head < self.tail
                let source_slice = &self.buf[self.tail..];
                w = destination.write(source_slice)?;
                self.tail = (self.tail + w) % self.buf.len();
            }
            self.optimize();
            Ok(w)
        }
    }

    pub fn read_from(&mut self, source: &[u8]) {
        assert!(source.len() <= self.remaining(), "StreamBuffer is full, check remaining() before calling read_from()");
        let source_len = source.len();
        let buf_len = self.buf.len();
        if source_len <= buf_len - self.head {
            let target_slice = &mut self.buf[self.head..self.head + source_len];
            target_slice.copy_from_slice(source);
        } else {
            {
                // fill until the right-end of the buffer
                let target_slice = &mut self.buf[self.head..];
                let source_slice = &source[..buf_len - self.head];
                target_slice.copy_from_slice(source_slice);
            }
            // fill the remaining from the left-end of the buffer
            let target_slice = &mut self.buf[..self.head + source_len - buf_len];
            let source_slice = &source[buf_len - self.head..];
            target_slice.copy_from_slice(source_slice);
        }
        self.head = (self.head + source_len) % buf_len;
    }

    /// To avoid unnecessary copies, StreamBuffer writes at most until the "end" of the circular
    /// buffer, which is suboptimal (it could have written more data if they have been contiguous).
    ///
    /// In order to minimize the occurrence of this event, reset the head and tail to 0 when the
    /// buffer is empty (no copy is involved).
    ///
    /// This is especially useful when the StreamBuffer is used to read/write one packet at a time,
    /// so the "end" of the buffer is guaranteed to never be reached.
    fn optimize(&mut self) {
        if self.is_empty() {
            self.head = 0;
            self.tail = 0;
        }
    }
}

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

    fn create_data() -> Vec<u8> {
        (0..6).collect()
    }

    #[test]
    fn bufferize_data() {
        let data = create_data();
        let mut stream_buffer = StreamBuffer::new(9);

        let mut cursor = io::Cursor::new(Vec::new());
        stream_buffer.read_from(&data).unwrap();
        stream_buffer.write_to(&mut cursor).unwrap();

        assert_eq!(cursor.get_ref(), &data);
    }

    #[test]
    fn circular() {
        let data = create_data();
        let mut stream_buffer = StreamBuffer::new(9);

        // put 6 bytes
        stream_buffer.read_from(&data).unwrap();
        // consume 3 bytes
        read_some(&mut stream_buffer, 3);

        // put test data
        stream_buffer.read_from(&data).unwrap();
        // consume 3 bytes (so that the first 6 bytes are totally consumed, and the "tail" position is 6)
        read_some(&mut stream_buffer, 3);

        // consume test data
        let result = read(&mut stream_buffer);

        // StreamBuffer is expected to break writes at circular buffer boundaries (capacity + 1)
        // This is not a requirement, but this verifies that the implementation works as expected
        assert_eq!([0u8, 1, 2, 3], &result[..]);

        // consume the remaining
        let result = read(&mut stream_buffer);
        assert_eq!([4u8, 5], &result[..]);
    }

    #[test]
    fn not_enough_space() {
        let data = create_data();
        let mut stream_buffer = StreamBuffer::new(9);

        // fill the buffer twice
        stream_buffer.read_from(&data).unwrap();
        assert_eq!(3, stream_buffer.remaining());

        // not enough space for another packet of 6 bytes
        assert!(stream_buffer.read_from(&data).is_err());

        // but enough space for a smaller one
        stream_buffer.read_from(&[0, 1, 2]).unwrap();
    }

    fn read_some(stream_buffer: &mut StreamBuffer, bytes: usize) -> Vec<u8> {
        let mut vec = vec![0u8; bytes];
        {
            let mut cursor = io::Cursor::new(&mut vec[..bytes]);
            stream_buffer.write_to(&mut cursor).unwrap();
        }
        vec
    }

    fn read(stream_buffer: &mut StreamBuffer) -> Vec<u8> {
        let mut cursor = io::Cursor::new(Vec::new());
        stream_buffer.write_to(&mut cursor).unwrap();
        cursor.into_inner()
    }
}
<commit_msg>Fix StreamBuffer tests<commit_after>use std::io;

/// Circular buffer to store a stream. Read/write boundaries are not preserved.
pub struct StreamBuffer {
    buf: Box<[u8]>,
    head: usize,
    tail: usize,
}

impl StreamBuffer {
    pub fn new(capacity: usize) -> Self {
        Self {
            buf: vec![0; capacity + 1].into_boxed_slice(),
            head: 0,
            tail: 0,
        }
    }

    pub fn is_empty(&self) -> bool{
        self.head == self.tail
    }

    pub fn size(&self) -> usize {
        if self.head < self.tail {
            self.head + self.buf.len() - self.tail
        } else {
            self.head - self.tail
        }
    }

    pub fn capacity(&self) -> usize {
        self.buf.len() - 1
    }

    pub fn remaining(&self) -> usize {
        self.capacity() - self.size()
    }

    pub fn write_to<W: io::Write>(&mut self, destination: &mut W) -> io::Result<usize> {
        if self.head == self.tail {
            // buffer is empty, nothing to do
            Ok(0)
        } else {
            let w;
            if self.head > self.tail {
                let source_slice = &self.buf[self.tail..self.head];
                w = destination.write(source_slice)?;
                self.tail += w;
            } else { // self.head < self.tail
                let source_slice = &self.buf[self.tail..];
                w = destination.write(source_slice)?;
                self.tail = (self.tail + w) % self.buf.len();
            }
            self.optimize();
            Ok(w)
        }
    }

    pub fn read_from(&mut self, source: &[u8]) {
        assert!(source.len() <= self.remaining(), "StreamBuffer is full, check remaining() before calling read_from()");
        let source_len = source.len();
        let buf_len = self.buf.len();
        if source_len <= buf_len - self.head {
            let target_slice = &mut self.buf[self.head..self.head + source_len];
            target_slice.copy_from_slice(source);
        } else {
            {
                // fill until the right-end of the buffer
                let target_slice = &mut self.buf[self.head..];
                let source_slice = &source[..buf_len - self.head];
                target_slice.copy_from_slice(source_slice);
            }
            // fill the remaining from the left-end of the buffer
            let target_slice = &mut self.buf[..self.head + source_len - buf_len];
            let source_slice = &source[buf_len - self.head..];
            target_slice.copy_from_slice(source_slice);
        }
        self.head = (self.head + source_len) % buf_len;
    }

    /// To avoid unnecessary copies, StreamBuffer writes at most until the "end" of the circular
    /// buffer, which is suboptimal (it could have written more data if they have been contiguous).
    ///
    /// In order to minimize the occurrence of this event, reset the head and tail to 0 when the
    /// buffer is empty (no copy is involved).
    ///
    /// This is especially useful when the StreamBuffer is used to read/write one packet at a time,
    /// so the "end" of the buffer is guaranteed to never be reached.
    fn optimize(&mut self) {
        if self.is_empty() {
            self.head = 0;
            self.tail = 0;
        }
    }
}

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

    fn create_data() -> Vec<u8> {
        (0..6).collect()
    }

    #[test]
    fn bufferize_data() {
        let data = create_data();
        let mut stream_buffer = StreamBuffer::new(9);

        let mut cursor = io::Cursor::new(Vec::new());
        stream_buffer.read_from(&data);
        stream_buffer.write_to(&mut cursor).unwrap();

        assert_eq!(cursor.get_ref(), &data);
    }

    #[test]
    fn circular() {
        let data = create_data();
        let mut stream_buffer = StreamBuffer::new(9);

        // put 6 bytes
        stream_buffer.read_from(&data);
        // consume 3 bytes
        read_some(&mut stream_buffer, 3);

        // put test data
        stream_buffer.read_from(&data);
        // consume 3 bytes (so that the first 6 bytes are totally consumed, and the "tail" position is 6)
        read_some(&mut stream_buffer, 3);

        // consume test data
        let result = read(&mut stream_buffer);

        // StreamBuffer is expected to break writes at circular buffer boundaries (capacity + 1)
        // This is not a requirement, but this verifies that the implementation works as expected
        assert_eq!([0u8, 1, 2, 3], &result[..]);

        // consume the remaining
        let result = read(&mut stream_buffer);
        assert_eq!([4u8, 5], &result[..]);
    }

    #[test]
    fn just_enough_space() {
        let data = create_data();
        let mut stream_buffer = StreamBuffer::new(9);

        // fill the buffer twice
        stream_buffer.read_from(&data);
        assert_eq!(3, stream_buffer.remaining());
        stream_buffer.read_from(&[0, 1, 2]);

        let result = read(&mut stream_buffer);
        assert_eq!([0, 1, 2, 3, 4, 5, 0, 1, 2], &result[..]);
    }

    fn read_some(stream_buffer: &mut StreamBuffer, bytes: usize) -> Vec<u8> {
        let mut vec = vec![0u8; bytes];
        {
            let mut cursor = io::Cursor::new(&mut vec[..bytes]);
            stream_buffer.write_to(&mut cursor).unwrap();
        }
        vec
    }

    fn read(stream_buffer: &mut StreamBuffer) -> Vec<u8> {
        let mut cursor = io::Cursor::new(Vec::new());
        stream_buffer.write_to(&mut cursor).unwrap();
        cursor.into_inner()
    }
}
<|endoftext|>


<commit_before>use js::value::{Value, VNumber, VFunction, VObject, ResultValue, to_value};
use js::function::{NativeFunction, NativeFunc};
use collections::treemap::TreeMap;
use rand::random;
use std::f64;
use std::gc::Gc;
use std::cell::RefCell;

/// Get the absolute value of a number
pub fn abs(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().abs()
	} else {
		f64::NAN
	})))
}
/// Get the arccos of a number
pub fn acos(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().acos()
	} else {
		f64::NAN
	})))
}
/// Get the arcsine of a number
pub fn asin(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().asin()
	} else {
		f64::NAN
	})))
}
/// Get the arctangent of a number
pub fn atan(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().atan()
	} else {
		f64::NAN
	})))
}
/// Get the arctangent of a numbers
pub fn atan2(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().atan2(&args.get(1).borrow().to_num())
	} else {
		f64::NAN
	})))
}
/// Get the cubic root of a number
pub fn cbrt(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().cbrt()
	} else {
		f64::NAN
	})))
}
/// Get lowest integer above a number
pub fn ceil(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().ceil()
	} else {
		f64::NAN
	})))
}
/// Get the cosine of a number
pub fn cos(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().cos()
	} else {
		f64::NAN
	})))
}
/// Get the power to raise the natural logarithm to get the number
pub fn exp(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().exp()
	} else {
		f64::NAN
	})))
}
/// Get the highest integer below a number
pub fn floor(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().floor()
	} else {
		f64::NAN
	})))
}
/// Get the natural logarithm of a number
pub fn log(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().log(&f64::consts::E)
	} else {
		f64::NAN
	})))
}
/// Get the maximum of several numbers
pub fn max(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let mut max = f64::NEG_INFINITY;
	for arg in args.iter() {
		let num = arg.borrow().to_num();
		max = max.max(num);
	}
	Ok(to_value(max))
}
/// Get the minimum of several numbers
pub fn min(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let mut max = f64::INFINITY;
	for arg in args.iter() {
		let num = arg.borrow().to_num();
		max = max.min(num);
	}
	Ok(to_value(max))
}
/// Raise a number to a power
pub fn pow(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 2 {
		let num = args.get(0);
		let power = args.get(1);
		num.borrow().to_num().powf(&power.borrow().to_num())
	} else {
		f64::NAN
	})))
}
/// Generate a random floating-point number between 0 and 1
pub fn _random(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(random())))
}
/// Round a number to the nearest integer
pub fn round(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().round()
	} else {
		f64::NAN
	})))
}
/// Get the sine of a number
pub fn sin(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().sin()
	} else {
		f64::NAN
	})))
}
/// Get the square root of a number
pub fn sqrt(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().sqrt()
	} else {
		f64::NAN
	})))
}
/// Get the tangent of a number
pub fn tan(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(Gc::new(VNumber(if args.len() >= 1 {
		args.get(0).borrow().to_num().tan()
	} else {
		f64::NAN
	})))
}
/// Create a new 'Math' object
pub fn _create() -> Value {
	let mut math = TreeMap::new();
	math.insert(~"E", to_value(f64::consts::E));
	math.insert(~"LN2", to_value(f64::consts::LN_2));
	math.insert(~"LN10", to_value(f64::consts::LN_10));
	math.insert(~"LOG2E", to_value(f64::consts::LOG2_E));
	math.insert(~"LOG10E", to_value(f64::consts::LOG10_E));
	math.insert(~"SQRT1_2", Gc::new(VNumber(0.5f64.sqrt())));
	math.insert(~"SQRT2", to_value(f64::consts::SQRT2));
	math.insert(~"PI", to_value(f64::consts::PI));
	math.insert(~"abs", to_value(abs));
	math.insert(~"acos", to_value(acos));
	math.insert(~"asin", to_value(asin));
	math.insert(~"atan", to_value(atan));
	math.insert(~"atan2", to_value(atan2));
	math.insert(~"cbrt", to_value(cbrt));
	math.insert(~"ceil", to_value(ceil));
	math.insert(~"cos", to_value(cos));
	math.insert(~"exp", to_value(exp));
	math.insert(~"floor", to_value(floor));
	math.insert(~"log", to_value(log));
	math.insert(~"max", to_value(max));
	math.insert(~"min", to_value(min));
	math.insert(~"pow", to_value(pow));
	math.insert(~"random", to_value(_random));
	math.insert(~"round", to_value(round));
	math.insert(~"sin", to_value(sin));
	math.insert(~"sqrt", to_value(sqrt));
	math.insert(~"tan", to_value(tan));
	to_value(math)
}<commit_msg>Use from_value and to_value in Math<commit_after>use js::value::{Value, VNumber, VFunction, VObject, ResultValue, to_value, from_value};
use js::function::{NativeFunction, NativeFunc};
use collections::treemap::TreeMap;
use rand::random;
use std::f64;
use std::gc::Gc;
use std::cell::RefCell;

/// Get the absolute value of a number
pub fn abs(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.abs()
	} else {
		f64::NAN
	}))
}
/// Get the arccos of a number
pub fn acos(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.acos()
	} else {
		f64::NAN
	}))
}
/// Get the arcsine of a number
pub fn asin(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.asin()
	} else {
		f64::NAN
	}))
}
/// Get the arctangent of a number
pub fn atan(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.atan()
	} else {
		f64::NAN
	}))
}
/// Get the arctangent of a numbers
pub fn atan2(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.atan2(&args.get(1).borrow().to_num())
	} else {
		f64::NAN
	}))
}
/// Get the cubic root of a number
pub fn cbrt(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.cbrt()
	} else {
		f64::NAN
	}))
}
/// Get lowest integer above a number
pub fn ceil(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.ceil()
	} else {
		f64::NAN
	}))
}
/// Get the cosine of a number
pub fn cos(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.cos()
	} else {
		f64::NAN
	}))
}
/// Get the power to raise the natural logarithm to get the number
pub fn exp(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.exp()
	} else {
		f64::NAN
	}))
}
/// Get the highest integer below a number
pub fn floor(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.floor()
	} else {
		f64::NAN
	}))
}
/// Get the natural logarithm of a number
pub fn log(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.log(&f64::consts::E)
	} else {
		f64::NAN
	}))
}
/// Get the maximum of several numbers
pub fn max(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let mut max = f64::NEG_INFINITY;
	for arg in args.iter() {
		let num = arg.borrow().to_num();
		max = max.max(num);
	}
	Ok(to_value(max))
}
/// Get the minimum of several numbers
pub fn min(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let mut max = f64::INFINITY;
	for arg in args.iter() {
		let num = arg.borrow().to_num();
		max = max.min(num);
	}
	Ok(to_value(max))
}
/// Raise a number to a power
pub fn pow(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 2 {
		let num : f64 = from_value(*args.get(0)).unwrap();
		let power : f64 = from_value(*args.get(1)).unwrap();
		num.powf(&power)
	} else {
		f64::NAN
	}))
}
/// Generate a random floating-point number between 0 and 1
pub fn _random(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(random::<f64>()))
}
/// Round a number to the nearest integer
pub fn round(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.round()
	} else {
		f64::NAN
	}))
}
/// Get the sine of a number
pub fn sin(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.sin()
	} else {
		f64::NAN
	}))
}
/// Get the square root of a number
pub fn sqrt(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.sqrt()
	} else {
		f64::NAN
	}))
}
/// Get the tangent of a number
pub fn tan(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	Ok(to_value(if args.len() >= 1 {
		let val : f64 = from_value(*args.get(0)).unwrap();
		val.tan()
	} else {
		f64::NAN
	}))
}
/// Create a new 'Math' object
pub fn _create() -> Value {
	let mut math = TreeMap::new();
	math.insert(~"E", to_value(f64::consts::E));
	math.insert(~"LN2", to_value(f64::consts::LN_2));
	math.insert(~"LN10", to_value(f64::consts::LN_10));
	math.insert(~"LOG2E", to_value(f64::consts::LOG2_E));
	math.insert(~"LOG10E", to_value(f64::consts::LOG10_E));
	math.insert(~"SQRT1_2", to_value(0.5f64.sqrt()));
	math.insert(~"SQRT2", to_value(f64::consts::SQRT2));
	math.insert(~"PI", to_value(f64::consts::PI));
	math.insert(~"abs", to_value(abs));
	math.insert(~"acos", to_value(acos));
	math.insert(~"asin", to_value(asin));
	math.insert(~"atan", to_value(atan));
	math.insert(~"atan2", to_value(atan2));
	math.insert(~"cbrt", to_value(cbrt));
	math.insert(~"ceil", to_value(ceil));
	math.insert(~"cos", to_value(cos));
	math.insert(~"exp", to_value(exp));
	math.insert(~"floor", to_value(floor));
	math.insert(~"log", to_value(log));
	math.insert(~"max", to_value(max));
	math.insert(~"min", to_value(min));
	math.insert(~"pow", to_value(pow));
	math.insert(~"random", to_value(_random));
	math.insert(~"round", to_value(round));
	math.insert(~"sin", to_value(sin));
	math.insert(~"sqrt", to_value(sqrt));
	math.insert(~"tan", to_value(tan));
	to_value(math)
}<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

//! COSE_Key functionality.

use crate::{
    iana,
    iana::EnumI128,
    util::{cbor_type_error, AsCborValue},
    Algorithm, Label,
};
use maplit::btreemap;
use serde::de::Unexpected;
use serde_cbor as cbor;
use std::collections::{btree_map::Entry, BTreeMap, BTreeSet};

#[cfg(test)]
mod tests;

/// Key type.
type KeyType = crate::RegisteredLabel<iana::KeyType>;

impl Default for KeyType {
    fn default() -> Self {
        KeyType::Assigned(iana::KeyType::Reserved)
    }
}

/// Key operation.
type KeyOperation = crate::RegisteredLabel<iana::KeyOperation>;

/// Structure representing a cryptographic key.
///
/// ```cddl
///  COSE_Key = {
///      1 => tstr / int,          ; kty
///      ? 2 => bstr,              ; kid
///      ? 3 => tstr / int,        ; alg
///      ? 4 => [+ (tstr / int) ], ; key_ops
///      ? 5 => bstr,              ; Base IV
///      * label => values
///  }
///  ```
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CoseKey {
    /// Key type identification.
    pub kty: KeyType,
    /// Key identification.
    pub key_id: Vec<u8>,
    /// Key use restriction to this algorithm.
    pub alg: Option<Algorithm>,
    /// Restrict set of possible operations.
    pub key_ops: BTreeSet<KeyOperation>,
    /// Base IV to be xor-ed with partial IVs.
    pub base_iv: Vec<u8>,
    /// Any additional parameter values.
    pub params: BTreeMap<Label, cbor::Value>,
}

/// A collection of [`CoseKey`] objects.
pub type CoseKeySet = Vec<CoseKey>;

impl crate::CborSerializable for CoseKey {}

const KTY: cbor::Value = cbor::Value::Integer(iana::KeyParameter::Kty as i128);
const KID: cbor::Value = cbor::Value::Integer(iana::KeyParameter::Kid as i128);
const ALG: cbor::Value = cbor::Value::Integer(iana::KeyParameter::Alg as i128);
const KEY_OPS: cbor::Value = cbor::Value::Integer(iana::KeyParameter::KeyOps as i128);
const BASE_IV: cbor::Value = cbor::Value::Integer(iana::KeyParameter::BaseIv as i128);

impl AsCborValue for CoseKey {
    fn from_cbor_value<E: serde::de::Error>(value: cbor::Value) -> Result<Self, E> {
        let m = match value {
            cbor::Value::Map(m) => m,
            v => return cbor_type_error(&v, &"map"),
        };

        let mut key = Self::default();
        for (label, value) in m.into_iter() {
            match label {
                x if x == KTY => key.kty = KeyType::from_cbor_value(value)?,

                x if x == KID => match value {
                    cbor::Value::Bytes(v) => {
                        if v.is_empty() {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::Bytes(&v),
                                &"non-empty bstr",
                            ));
                        }
                        key.key_id = v;
                    }
                    v => return cbor_type_error(&v, &"bstr value"),
                },

                x if x == ALG => key.alg = Some(Algorithm::from_cbor_value(value)?),

                x if x == KEY_OPS => match value {
                    cbor::Value::Array(key_ops) => {
                        for key_op in key_ops.into_iter() {
                            if !key.key_ops.insert(KeyOperation::from_cbor_value(key_op)?) {
                                return Err(serde::de::Error::invalid_value(
                                    Unexpected::TupleVariant,
                                    &"unique array label",
                                ));
                            }
                        }
                        if key.key_ops.is_empty() {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::TupleVariant,
                                &"non-empty array",
                            ));
                        }
                    }
                    v => return cbor_type_error(&v, &"array value"),
                },

                x if x == BASE_IV => match value {
                    cbor::Value::Bytes(v) => {
                        if v.is_empty() {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::Bytes(&v),
                                &"non-empty bstr",
                            ));
                        }
                        key.base_iv = v;
                    }
                    v => return cbor_type_error(&v, &"bstr value"),
                },

                l => {
                    let label = Label::from_cbor_value(l)?;
                    match key.params.entry(label) {
                        Entry::Occupied(_) => {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::StructVariant,
                                &"unique map label",
                            ));
                        }
                        Entry::Vacant(ve) => {
                            ve.insert(value);
                        }
                    }
                }
            }
        }
        // Check that key type has been set.
        if key.kty == KeyType::Assigned(iana::KeyType::Reserved) {
            return Err(serde::de::Error::invalid_value(
                Unexpected::StructVariant,
                &"mandatory kty label",
            ));
        }

        Ok(key)
    }

    fn to_cbor_value(&self) -> cbor::Value {
        let mut map = BTreeMap::<cbor::Value, cbor::Value>::new();
        map.insert(KTY, self.kty.to_cbor_value());
        if !self.key_id.is_empty() {
            map.insert(KID, cbor::Value::Bytes(self.key_id.to_vec()));
        }
        if let Some(alg) = &self.alg {
            map.insert(ALG, alg.to_cbor_value());
        }
        if !self.key_ops.is_empty() {
            map.insert(
                KEY_OPS,
                cbor::Value::Array(
                    self.key_ops
                        .iter()
                        .map(|op| match op {
                            KeyOperation::Assigned(i) => cbor::Value::Integer(*i as i128),
                            KeyOperation::Text(t) => cbor::Value::Text(t.to_owned()),
                        })
                        .collect(),
                ),
            );
        }
        if !self.base_iv.is_empty() {
            map.insert(BASE_IV, cbor::Value::Bytes(self.base_iv.to_vec()));
        }
        for (label, value) in &self.params {
            map.insert(label.to_cbor_value(), value.clone());
        }
        cbor::Value::Map(map)
    }
}

cbor_serialize!(CoseKey);

/// Builder for [`CoseKey`] objects.
#[derive(Default)]
pub struct CoseKeyBuilder(CoseKey);

impl CoseKeyBuilder {
    builder! {CoseKey}
    builder_set! {key_id: Vec<u8>}
    builder_set! {base_iv: Vec<u8>}

    /// Constructor for an elliptic curve public key specified by `x` and `y` coordinates.
    pub fn new_ec2_pub_key(curve: iana::EllipticCurve, x: Vec<u8>, y: Vec<u8>) -> Self {
        Self(CoseKey {
            kty: KeyType::Assigned(iana::KeyType::EC2),
            params: btreemap! {
                Label::Int(iana::Ec2KeyParameter::Crv as i128) => cbor::Value::Integer(curve as i128),
                Label::Int(iana::Ec2KeyParameter::X as i128) => cbor::Value::Bytes(x),
                Label::Int(iana::Ec2KeyParameter::Y as i128) => cbor::Value::Bytes(y),
            },
            ..Default::default()
        })
    }

    /// Constructor for an elliptic curve public key specified by `x` coordinate plus sign of `y`
    /// coordinate.
    pub fn new_ec2_pub_key_y_sign(curve: iana::EllipticCurve, x: Vec<u8>, y_sign: bool) -> Self {
        Self(CoseKey {
            kty: KeyType::Assigned(iana::KeyType::EC2),
            params: btreemap! {
                Label::Int(iana::Ec2KeyParameter::Crv as i128) => cbor::Value::Integer(curve as i128),
                Label::Int(iana::Ec2KeyParameter::X as i128) => cbor::Value::Bytes(x),
                Label::Int(iana::Ec2KeyParameter::Y as i128) => cbor::Value::Bool(y_sign),
            },
            ..Default::default()
        })
    }

    /// Constructor for an elliptic curve private key specified by `d`, together with public `x` and
    /// `y` coordinates.
    pub fn new_ec2_priv_key(
        curve: iana::EllipticCurve,
        x: Vec<u8>,
        y: Vec<u8>,
        d: Vec<u8>,
    ) -> Self {
        let mut builder = Self::new_ec2_pub_key(curve, x, y);
        builder.0.params.insert(
            Label::Int(iana::Ec2KeyParameter::D as i128),
            cbor::Value::Bytes(d),
        );
        builder
    }

    /// Constructor for a symmetric key specified by `k`.
    pub fn new_symmetric_key(k: Vec<u8>) -> Self {
        Self(CoseKey {
            kty: KeyType::Assigned(iana::KeyType::Symmetric),
            params: btreemap! {
                Label::Int(iana::SymmetricKeyParameter::K as i128) => cbor::Value::Bytes(k),
            },
            ..Default::default()
        })
    }

    /// Set the algorithm.
    pub fn algorithm(mut self, alg: iana::Algorithm) -> Self {
        self.0.alg = Some(Algorithm::Assigned(alg));
        self
    }

    /// Add a key operation.
    pub fn add_key_op(mut self, op: iana::KeyOperation) -> Self {
        self.0.key_ops.insert(KeyOperation::Assigned(op));
        self
    }

    /// Set a parameter value.
    ///
    /// # Panics
    ///
    /// This function will panic if it used to set a parameter label from the [`iana::KeyParameter`]
    /// range.
    pub fn param(mut self, label: i128, value: cbor::Value) -> Self {
        if iana::KeyParameter::from_i128(label).is_some() {
            panic!("param() method used to set KeyParameter"); // safe: invalid input
        }
        self.0.params.insert(Label::Int(label), value);
        self
    }
}
<commit_msg>key: export alias types<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

//! COSE_Key functionality.

use crate::{
    iana,
    iana::EnumI128,
    util::{cbor_type_error, AsCborValue},
    Algorithm, Label,
};
use maplit::btreemap;
use serde::de::Unexpected;
use serde_cbor as cbor;
use std::collections::{btree_map::Entry, BTreeMap, BTreeSet};

#[cfg(test)]
mod tests;

/// Key type.
pub type KeyType = crate::RegisteredLabel<iana::KeyType>;

impl Default for KeyType {
    fn default() -> Self {
        KeyType::Assigned(iana::KeyType::Reserved)
    }
}

/// Key operation.
pub type KeyOperation = crate::RegisteredLabel<iana::KeyOperation>;

/// Structure representing a cryptographic key.
///
/// ```cddl
///  COSE_Key = {
///      1 => tstr / int,          ; kty
///      ? 2 => bstr,              ; kid
///      ? 3 => tstr / int,        ; alg
///      ? 4 => [+ (tstr / int) ], ; key_ops
///      ? 5 => bstr,              ; Base IV
///      * label => values
///  }
///  ```
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CoseKey {
    /// Key type identification.
    pub kty: KeyType,
    /// Key identification.
    pub key_id: Vec<u8>,
    /// Key use restriction to this algorithm.
    pub alg: Option<Algorithm>,
    /// Restrict set of possible operations.
    pub key_ops: BTreeSet<KeyOperation>,
    /// Base IV to be xor-ed with partial IVs.
    pub base_iv: Vec<u8>,
    /// Any additional parameter values.
    pub params: BTreeMap<Label, cbor::Value>,
}

/// A collection of [`CoseKey`] objects.
pub type CoseKeySet = Vec<CoseKey>;

impl crate::CborSerializable for CoseKey {}

const KTY: cbor::Value = cbor::Value::Integer(iana::KeyParameter::Kty as i128);
const KID: cbor::Value = cbor::Value::Integer(iana::KeyParameter::Kid as i128);
const ALG: cbor::Value = cbor::Value::Integer(iana::KeyParameter::Alg as i128);
const KEY_OPS: cbor::Value = cbor::Value::Integer(iana::KeyParameter::KeyOps as i128);
const BASE_IV: cbor::Value = cbor::Value::Integer(iana::KeyParameter::BaseIv as i128);

impl AsCborValue for CoseKey {
    fn from_cbor_value<E: serde::de::Error>(value: cbor::Value) -> Result<Self, E> {
        let m = match value {
            cbor::Value::Map(m) => m,
            v => return cbor_type_error(&v, &"map"),
        };

        let mut key = Self::default();
        for (label, value) in m.into_iter() {
            match label {
                x if x == KTY => key.kty = KeyType::from_cbor_value(value)?,

                x if x == KID => match value {
                    cbor::Value::Bytes(v) => {
                        if v.is_empty() {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::Bytes(&v),
                                &"non-empty bstr",
                            ));
                        }
                        key.key_id = v;
                    }
                    v => return cbor_type_error(&v, &"bstr value"),
                },

                x if x == ALG => key.alg = Some(Algorithm::from_cbor_value(value)?),

                x if x == KEY_OPS => match value {
                    cbor::Value::Array(key_ops) => {
                        for key_op in key_ops.into_iter() {
                            if !key.key_ops.insert(KeyOperation::from_cbor_value(key_op)?) {
                                return Err(serde::de::Error::invalid_value(
                                    Unexpected::TupleVariant,
                                    &"unique array label",
                                ));
                            }
                        }
                        if key.key_ops.is_empty() {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::TupleVariant,
                                &"non-empty array",
                            ));
                        }
                    }
                    v => return cbor_type_error(&v, &"array value"),
                },

                x if x == BASE_IV => match value {
                    cbor::Value::Bytes(v) => {
                        if v.is_empty() {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::Bytes(&v),
                                &"non-empty bstr",
                            ));
                        }
                        key.base_iv = v;
                    }
                    v => return cbor_type_error(&v, &"bstr value"),
                },

                l => {
                    let label = Label::from_cbor_value(l)?;
                    match key.params.entry(label) {
                        Entry::Occupied(_) => {
                            return Err(serde::de::Error::invalid_value(
                                Unexpected::StructVariant,
                                &"unique map label",
                            ));
                        }
                        Entry::Vacant(ve) => {
                            ve.insert(value);
                        }
                    }
                }
            }
        }
        // Check that key type has been set.
        if key.kty == KeyType::Assigned(iana::KeyType::Reserved) {
            return Err(serde::de::Error::invalid_value(
                Unexpected::StructVariant,
                &"mandatory kty label",
            ));
        }

        Ok(key)
    }

    fn to_cbor_value(&self) -> cbor::Value {
        let mut map = BTreeMap::<cbor::Value, cbor::Value>::new();
        map.insert(KTY, self.kty.to_cbor_value());
        if !self.key_id.is_empty() {
            map.insert(KID, cbor::Value::Bytes(self.key_id.to_vec()));
        }
        if let Some(alg) = &self.alg {
            map.insert(ALG, alg.to_cbor_value());
        }
        if !self.key_ops.is_empty() {
            map.insert(
                KEY_OPS,
                cbor::Value::Array(
                    self.key_ops
                        .iter()
                        .map(|op| match op {
                            KeyOperation::Assigned(i) => cbor::Value::Integer(*i as i128),
                            KeyOperation::Text(t) => cbor::Value::Text(t.to_owned()),
                        })
                        .collect(),
                ),
            );
        }
        if !self.base_iv.is_empty() {
            map.insert(BASE_IV, cbor::Value::Bytes(self.base_iv.to_vec()));
        }
        for (label, value) in &self.params {
            map.insert(label.to_cbor_value(), value.clone());
        }
        cbor::Value::Map(map)
    }
}

cbor_serialize!(CoseKey);

/// Builder for [`CoseKey`] objects.
#[derive(Default)]
pub struct CoseKeyBuilder(CoseKey);

impl CoseKeyBuilder {
    builder! {CoseKey}
    builder_set! {key_id: Vec<u8>}
    builder_set! {base_iv: Vec<u8>}

    /// Constructor for an elliptic curve public key specified by `x` and `y` coordinates.
    pub fn new_ec2_pub_key(curve: iana::EllipticCurve, x: Vec<u8>, y: Vec<u8>) -> Self {
        Self(CoseKey {
            kty: KeyType::Assigned(iana::KeyType::EC2),
            params: btreemap! {
                Label::Int(iana::Ec2KeyParameter::Crv as i128) => cbor::Value::Integer(curve as i128),
                Label::Int(iana::Ec2KeyParameter::X as i128) => cbor::Value::Bytes(x),
                Label::Int(iana::Ec2KeyParameter::Y as i128) => cbor::Value::Bytes(y),
            },
            ..Default::default()
        })
    }

    /// Constructor for an elliptic curve public key specified by `x` coordinate plus sign of `y`
    /// coordinate.
    pub fn new_ec2_pub_key_y_sign(curve: iana::EllipticCurve, x: Vec<u8>, y_sign: bool) -> Self {
        Self(CoseKey {
            kty: KeyType::Assigned(iana::KeyType::EC2),
            params: btreemap! {
                Label::Int(iana::Ec2KeyParameter::Crv as i128) => cbor::Value::Integer(curve as i128),
                Label::Int(iana::Ec2KeyParameter::X as i128) => cbor::Value::Bytes(x),
                Label::Int(iana::Ec2KeyParameter::Y as i128) => cbor::Value::Bool(y_sign),
            },
            ..Default::default()
        })
    }

    /// Constructor for an elliptic curve private key specified by `d`, together with public `x` and
    /// `y` coordinates.
    pub fn new_ec2_priv_key(
        curve: iana::EllipticCurve,
        x: Vec<u8>,
        y: Vec<u8>,
        d: Vec<u8>,
    ) -> Self {
        let mut builder = Self::new_ec2_pub_key(curve, x, y);
        builder.0.params.insert(
            Label::Int(iana::Ec2KeyParameter::D as i128),
            cbor::Value::Bytes(d),
        );
        builder
    }

    /// Constructor for a symmetric key specified by `k`.
    pub fn new_symmetric_key(k: Vec<u8>) -> Self {
        Self(CoseKey {
            kty: KeyType::Assigned(iana::KeyType::Symmetric),
            params: btreemap! {
                Label::Int(iana::SymmetricKeyParameter::K as i128) => cbor::Value::Bytes(k),
            },
            ..Default::default()
        })
    }

    /// Set the algorithm.
    pub fn algorithm(mut self, alg: iana::Algorithm) -> Self {
        self.0.alg = Some(Algorithm::Assigned(alg));
        self
    }

    /// Add a key operation.
    pub fn add_key_op(mut self, op: iana::KeyOperation) -> Self {
        self.0.key_ops.insert(KeyOperation::Assigned(op));
        self
    }

    /// Set a parameter value.
    ///
    /// # Panics
    ///
    /// This function will panic if it used to set a parameter label from the [`iana::KeyParameter`]
    /// range.
    pub fn param(mut self, label: i128, value: cbor::Value) -> Self {
        if iana::KeyParameter::from_i128(label).is_some() {
            panic!("param() method used to set KeyParameter"); // safe: invalid input
        }
        self.0.params.insert(Label::Int(label), value);
        self
    }
}
<|endoftext|>


<commit_before>use crate::convert::*;
use crate::folded_multiply::*;
use core::hash::Hasher;
use crate::HasherExt;

///This constant come from Kunth's prng (Empirically it works better than those from splitmix32).
const MULTIPLE: u64 = crate::random_state::MULTIPLE;
const INCREMENT: u64 = crate::random_state::INCREMENT;
const ROT: u32 = 23; //17

/// A `Hasher` for hashing an arbitrary stream of bytes.
///
/// Instances of [`AHasher`] represent state that is updated while hashing data.
///
/// Each method updates the internal state based on the new data provided. Once
/// all of the data has been provided, the resulting hash can be obtained by calling
/// `finish()`
///
/// [Clone] is also provided in case you wish to calculate hashes for two different items that
/// start with the same data.
///
#[derive(Debug, Clone)]
pub struct AHasher {
    buffer: u64,
    pad: u64,
    extra_keys: [u64; 2]
}

impl AHasher {
    /// Creates a new hasher keyed to the provided key.
    #[inline]
    #[allow(dead_code)] // Is not called if non-fallback hash is used.
    pub fn new_with_keys(key1: u128, key2: u128) -> AHasher {
        let keys: [u64; 2] = key1.convert();
        AHasher {
            buffer: keys[0],
            pad: keys[1],
            extra_keys: key2.convert(),
        }
    }

    #[cfg(test)]
    pub(crate) fn test_with_keys(key1: u64, key2: u64) -> AHasher {
        use crate::random_state::scramble_keys;
        let (k1, k2, k3, k4) = scramble_keys(key1, key2);
        AHasher {
            buffer: k1,
            pad: k2,
            extra_keys: [k3, k4]
        }
    }

    /// This update function has the goal of updating the buffer with a single multiply
    /// FxHash does this but is vulnerable to attack. To avoid this input needs to be masked to with an
    /// unpredictable value. Other hashes such as murmurhash have taken this approach but were found vulnerable
    /// to attack. The attack was based on the idea of reversing the pre-mixing (Which is necessarily
    /// reversible otherwise bits would be lost) then placing a difference in the highest bit before the
    /// multiply used to mix the data. Because a multiply can never affect the bits to the right of it, a
    /// subsequent update that also differed in this bit could result in a predictable collision.
    ///
    /// This version avoids this vulnerability while still only using a single multiply. It takes advantage
    /// of the fact that when a 64 bit multiply is performed the upper 64 bits are usually computed and thrown
    /// away. Instead it creates two 128 bit values where the upper 64 bits are zeros and multiplies them.
    /// (The compiler is smart enough to turn this into a 64 bit multiplication in the assembly)
    /// Then the upper bits are added to the lower bits to produce a single 64 bit result.
    ///
    /// To understand why this is a good scrambling function it helps to understand multiply-with-carry PRNGs:
    /// https://en.wikipedia.org/wiki/Multiply-with-carry_pseudorandom_number_generator
    /// If the multiple is chosen well, this creates a long period, decent quality PRNG.
    /// Notice that this function is equivalent to this except the `buffer`/`state` is being xored with each
    /// new block of data. In the event that data is all zeros, it is exactly equivalent to a MWC PRNG.
    ///
    /// This is impervious to attack because every bit buffer at the end is dependent on every bit in
    /// `new_data ^ buffer`. For example suppose two inputs differed in only the 5th bit. Then when the
    /// multiplication is performed the `result` will differ in bits 5-69. More specifically it will differ by
    /// 2^5 * MULTIPLE. However in the next step bits 65-128 are turned into a separate 64 bit value. So the
    /// differing bits will be in the lower 6 bits of this value. The two intermediate values that differ in
    /// bits 5-63 and in bits 0-5 respectively get added together. Producing an output that differs in every
    /// bit. The addition carries in the multiplication and at the end additionally mean that the even if an
    /// attacker somehow knew part of (but not all) the contents of the buffer before hand,
    /// they would not be able to predict any of the bits in the buffer at the end.
    #[inline(always)]
    fn update(&mut self, new_data: u64) {
        self.buffer = (new_data ^ self.buffer).folded_multiply(MULTIPLE);
    }

    /// This update function updates the buffer with the new information in a way that can't be canceled
    /// with a subsequent update without knowledge of the content of the buffer prior to the update.
    ///
    /// To achieve this the input needs to be modified in an unpredictable (to an attacker) way before it is
    /// combined with the value in the buffer. This is done by xoring it with `key`.
    ///
    /// Other hashes such as murmurhash have taken that approach but were found vulnerable to attack.
    /// The attack was based on the idea of reversing any pre-mixing (Which is necessarily reversible otherwise
    /// bits would be lost) then placing a difference in the highest bit before the multiply. Because a multiply
    /// can never affect the bits to the right of it, a subsequent update that also only differed in the high
    /// order bit could cancel out the change to `buffer` from the first update. This allowed murmurhash to be
    /// attacked. In this update function aHash avoids this vulnerability by rotating and performing a second
    /// multiply.
    ///
    /// This makes it impossible for an attacker to place a single bit difference between
    /// two blocks so as to cancel each other. (While the transform is still reversible if you know the key)
    ///
    /// This is similar to the above update function but is designed to run in a loop
    /// that will be unrolled and vectorized. So instead of using the buffer, it uses a 'key' that it updates
    /// and returns. The buffer is only xored at the end. This structure is so that when the method is inlined,
    /// the compiler will unroll any loop this gets placed in and the loop can be automatically vectorized
    /// and the rotates, xors, and multiplies can be paralleled.
    ///
    /// The key needs to be incremented between consecutive calls to prevent (a,b) from hashing the same as (b,a).
    /// The adding of the increment is moved to the bottom rather than the top. This allows one less add to be
    /// performed overall, but more importantly, it follows the multiply, which is expensive. So the CPU can
    /// run another operation afterwords if does not depend on the output of the multiply operation.
    #[inline(always)]
    fn ordered_update(&mut self, new_data: u64, key: u64) -> u64 {
        self.buffer ^= (new_data ^ key)
            .wrapping_mul(MULTIPLE)
            .rotate_left(ROT)
            .wrapping_mul(MULTIPLE);
        key.wrapping_add(INCREMENT)
    }
}

impl HasherExt for AHasher {
    #[inline]
    fn short_finish(&self) -> u64 {
        self.buffer
    }
}

/// Provides methods to hash all of the primitive types.
impl Hasher for AHasher {
    #[inline]
    fn write_u8(&mut self, i: u8) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u16(&mut self, i: u16) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u32(&mut self, i: u32) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u64(&mut self, i: u64) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u128(&mut self, i: u128) {
        let data: [u64; 2] = i.convert();
        self.update(data[0]);
        self.update(data[1]);
    }

    #[inline]
    fn write_usize(&mut self, i: usize) {
        self.write_u64(i as u64);
    }

    #[inline]
    #[allow(clippy::collapsible_if)]
    fn write(&mut self, input: &[u8]) {
        let mut data = input;
        let length = data.len() as u64;
        //Needs to be an add rather than an xor because otherwise it could be canceled with carefully formed input.
        self.buffer = self.buffer.wrapping_add(length).wrapping_mul(MULTIPLE);
        //A 'binary search' on sizes reduces the number of comparisons.
        if data.len() > 8 {
            if data.len() > 16 {
                let tail = data.read_last_u64();
                let mut key: u64 = self.buffer;
                while data.len() > 8 {
                    let (val, rest) = data.read_u64();
                    key = self.ordered_update(val, key);
                    data = rest;
                }
                self.update(tail);
            } else {
                self.update(data.read_u64().0);
                self.update(data.read_last_u64());
            }
        } else {
            if data.len() >= 2 {
                if data.len() >= 4 {
                    let block: [u32; 2] = [data.read_u32().0, data.read_last_u32()];
                    self.update(block.convert());
                } else {
                    let value = [data.read_u16().0 as u32, data[data.len() - 1] as u32];
                    self.update(value.convert());
                }
            } else {
                let value = if data.len() > 0 {
                    data[0] //len 1
                } else {
                    0
                };
                self.update(value as u64);
            }
        }
    }
    #[inline]
    fn finish(&self) -> u64 {
        let rot = (self.buffer & 63) as u32;
        self.buffer.folded_multiply(self.pad).rotate_left(rot)
    }
}

#[cfg(test)]
mod tests {
    use crate::convert::Convert;
    use crate::fallback_hash::*;

    #[test]
    fn test_hash() {
        let mut hasher = AHasher::new_with_keys(0, 0);
        let value: u64 = 1 << 32;
        hasher.update(value);
        let result = hasher.buffer;
        let mut hasher = AHasher::new_with_keys(0, 0);
        let value2: u64 = 1;
        hasher.update(value2);
        let result2 = hasher.buffer;
        let result: [u8; 8] = result.convert();
        let result2: [u8; 8] = result2.convert();
        assert_ne!(hex::encode(result), hex::encode(result2));
    }

    #[test]
    fn test_conversion() {
        let input: &[u8] = "dddddddd".as_bytes();
        let bytes: u64 = as_array!(input, 8).convert();
        assert_eq!(bytes, 0x6464646464646464);
    }
}
<commit_msg>Speed up and strengthen fallback algorithm<commit_after>use crate::convert::*;
use crate::folded_multiply::*;
use core::hash::Hasher;
use crate::HasherExt;

///This constant come from Kunth's prng (Empirically it works better than those from splitmix32).
const MULTIPLE: u64 = crate::random_state::MULTIPLE;
const ROT: u32 = 23; //17

/// A `Hasher` for hashing an arbitrary stream of bytes.
///
/// Instances of [`AHasher`] represent state that is updated while hashing data.
///
/// Each method updates the internal state based on the new data provided. Once
/// all of the data has been provided, the resulting hash can be obtained by calling
/// `finish()`
///
/// [Clone] is also provided in case you wish to calculate hashes for two different items that
/// start with the same data.
///
#[derive(Debug, Clone)]
pub struct AHasher {
    buffer: u64,
    pad: u64,
    extra_keys: [u64; 2]
}

impl AHasher {
    /// Creates a new hasher keyed to the provided key.
    #[inline]
    #[allow(dead_code)] // Is not called if non-fallback hash is used.
    pub fn new_with_keys(key1: u128, key2: u128) -> AHasher {
        let keys: [u64; 2] = key1.convert();
        AHasher {
            buffer: keys[0],
            pad: keys[1],
            extra_keys: key2.convert(),
        }
    }

    #[cfg(test)]
    pub(crate) fn test_with_keys(key1: u64, key2: u64) -> AHasher {
        use crate::random_state::scramble_keys;
        let (k1, k2, k3, k4) = scramble_keys(key1, key2);
        AHasher {
            buffer: k1,
            pad: k2,
            extra_keys: [k3, k4]
        }
    }

    /// This update function has the goal of updating the buffer with a single multiply
    /// FxHash does this but is vulnerable to attack. To avoid this input needs to be masked to with an
    /// unpredictable value. Other hashes such as murmurhash have taken this approach but were found vulnerable
    /// to attack. The attack was based on the idea of reversing the pre-mixing (Which is necessarily
    /// reversible otherwise bits would be lost) then placing a difference in the highest bit before the
    /// multiply used to mix the data. Because a multiply can never affect the bits to the right of it, a
    /// subsequent update that also differed in this bit could result in a predictable collision.
    ///
    /// This version avoids this vulnerability while still only using a single multiply. It takes advantage
    /// of the fact that when a 64 bit multiply is performed the upper 64 bits are usually computed and thrown
    /// away. Instead it creates two 128 bit values where the upper 64 bits are zeros and multiplies them.
    /// (The compiler is smart enough to turn this into a 64 bit multiplication in the assembly)
    /// Then the upper bits are xored with the lower bits to produce a single 64 bit result.
    ///
    /// To understand why this is a good scrambling function it helps to understand multiply-with-carry PRNGs:
    /// https://en.wikipedia.org/wiki/Multiply-with-carry_pseudorandom_number_generator
    /// If the multiple is chosen well, this creates a long period, decent quality PRNG.
    /// Notice that this function is equivalent to this except the `buffer`/`state` is being xored with each
    /// new block of data. In the event that data is all zeros, it is exactly equivalent to a MWC PRNG.
    ///
    /// This is impervious to attack because every bit buffer at the end is dependent on every bit in
    /// `new_data ^ buffer`. For example suppose two inputs differed in only the 5th bit. Then when the
    /// multiplication is performed the `result` will differ in bits 5-69. More specifically it will differ by
    /// 2^5 * MULTIPLE. However in the next step bits 65-128 are turned into a separate 64 bit value. So the
    /// differing bits will be in the lower 6 bits of this value. The two intermediate values that differ in
    /// bits 5-63 and in bits 0-5 respectively get added together. Producing an output that differs in every
    /// bit. The addition carries in the multiplication and at the end additionally mean that the even if an
    /// attacker somehow knew part of (but not all) the contents of the buffer before hand,
    /// they would not be able to predict any of the bits in the buffer at the end.
    #[inline(always)]
    fn update(&mut self, new_data: u64) {
        self.buffer = (new_data ^ self.buffer).folded_multiply(MULTIPLE);
    }

    /// Similar to the above this function performs an update using a "folded multiply".
    /// However it takes in 128 bits of data instead of 64. Both halves must be masked.
    ///
    /// This makes it impossible for an attacker to place a single bit difference between
    /// two blocks so as to cancel each other.
    ///
    /// However this is not sufficient. to prevent (a,b) from hashing the same as (b,a) the buffer itself must
    /// be updated between calls in a way that does not commute. To achieve this XOR and Rotate are used.
    /// Add followed by xor is not the same as xor followed by add, and rotate ensures that the same out bits
    /// can't be changed by the same set of input bits. To cancel this sequence with subsequent input would require
    /// knowing the keys.
    #[inline(always)]
    fn large_update(&mut self, new_data: u128) {
        let block: [u64; 2] = new_data.convert();
        let combined = (block[0] ^ self.extra_keys[0]).folded_multiply(block[1] ^ self.extra_keys[1]);
        self.buffer = (combined.wrapping_add(self.buffer) ^ self.pad).rotate_left(ROT);
    }
}

impl HasherExt for AHasher {
    #[inline]
    fn short_finish(&self) -> u64 {
        self.buffer.wrapping_add(self.pad)
    }
}

/// Provides methods to hash all of the primitive types.
impl Hasher for AHasher {
    #[inline]
    fn write_u8(&mut self, i: u8) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u16(&mut self, i: u16) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u32(&mut self, i: u32) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u64(&mut self, i: u64) {
        self.update(i as u64);
    }

    #[inline]
    fn write_u128(&mut self, i: u128) {
        let data: [u64; 2] = i.convert();
        self.update(data[0]);
        self.update(data[1]);
    }

    #[inline]
    fn write_usize(&mut self, i: usize) {
        self.write_u64(i as u64);
    }

    #[inline]
    #[allow(clippy::collapsible_if)]
    fn write(&mut self, input: &[u8]) {
        let mut data = input;
        let length = data.len() as u64;
        //Needs to be an add rather than an xor because otherwise it could be canceled with carefully formed input.
        self.buffer = self.buffer.wrapping_add(length).wrapping_mul(MULTIPLE);
        //A 'binary search' on sizes reduces the number of comparisons.
        if data.len() > 8 {
            if data.len() > 32 {
                let tail = data.read_last_u128();
                self.large_update(tail);
                while data.len() > 16 {
                    let (block, rest) = data.read_u128();
                    self.large_update( block);
                    data = rest;
                }
            } else {
                if data.len() > 16 {
                    self.large_update(data.read_u128().0);
                    self.large_update(data.read_last_u128());
                } else {
                    self.update(data.read_u64().0);
                    self.update(data.read_last_u64());
                }
            }
        } else {
            if data.len() >= 2 {
                if data.len() >= 4 {
                    let block: [u32; 2] = [data.read_u32().0, data.read_last_u32()];
                    self.update(block.convert());
                } else {
                    let value = [data.read_u16().0 as u32, data[data.len() - 1] as u32];
                    self.update(value.convert());
                }
            } else {
                let value = if data.len() > 0 {
                    data[0] //len 1
                } else {
                    0
                };
                self.update(value as u64);
            }
        }
    }
    #[inline]
    fn finish(&self) -> u64 {
        let rot = (self.buffer & 63) as u32;
        (self.buffer ^ self.pad).folded_multiply(MULTIPLE).rotate_left(rot)
    }
}

#[cfg(test)]
mod tests {
    use crate::convert::Convert;
    use crate::fallback_hash::*;

    #[test]
    fn test_hash() {
        let mut hasher = AHasher::new_with_keys(0, 0);
        let value: u64 = 1 << 32;
        hasher.update(value);
        let result = hasher.buffer;
        let mut hasher = AHasher::new_with_keys(0, 0);
        let value2: u64 = 1;
        hasher.update(value2);
        let result2 = hasher.buffer;
        let result: [u8; 8] = result.convert();
        let result2: [u8; 8] = result2.convert();
        assert_ne!(hex::encode(result), hex::encode(result2));
    }

    #[test]
    fn test_conversion() {
        let input: &[u8] = "dddddddd".as_bytes();
        let bytes: u64 = as_array!(input, 8).convert();
        assert_eq!(bytes, 0x6464646464646464);
    }
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::query;
use crate::model;
use crate::model::v1::*;
use crate::params::{
    coord_param, dataset_param, get_param_array, paginate_param, shape_param, timeout_param,
    types_param,
};
use hyper::mime::Mime;
use iron::status::Status as IronStatus;
use iron::typemap::Key;
use mimir::rubber::Rubber;
use prometheus;
use prometheus::Encoder;
use rustless;
use rustless::server::header;
use rustless::{Api, Nesting};
use serde;
use serde_json;
use std::time;
use valico::json_dsl;

use navitia_model::objects::Coord;

const DEFAULT_LIMIT: u64 = 10u64;
const DEFAULT_OFFSET: u64 = 0u64;

struct Timer;
impl Key for Timer {
    type Value = prometheus::HistogramTimer;
}

lazy_static! {
    static ref HTTP_COUNTER: prometheus::CounterVec = register_counter_vec!(
        "bragi_http_requests_total",
        "Total number of HTTP requests made.",
        &["handler", "method", "status"]
    )
    .unwrap();
    static ref HTTP_REQ_HISTOGRAM: prometheus::HistogramVec = register_histogram_vec!(
        "bragi_http_request_duration_seconds",
        "The HTTP request latencies in seconds.",
        &["handler", "method"],
        prometheus::exponential_buckets(0.001, 1.5, 25).unwrap()
    )
    .unwrap();
    static ref HTTP_IN_FLIGHT: prometheus::Gauge = register_gauge!(
        "bragi_http_requests_in_flight",
        "current number of http request being served"
    )
    .unwrap();
}

/// get the timeout from the query 'timeout' parameter
/// if the param is not there, use the default value
fn parse_timeout(
    params: &rustless::json::JsonValue,
    default_timeout: Option<time::Duration>,
) -> Option<time::Duration> {
    params
        .find("timeout")
        .and_then(|v| v.as_u64())
        .map(time::Duration::from_millis)
        .map(|t| match default_timeout {
            Some(dt) => t.min(dt),
            None => t,
        })
        .or(default_timeout)
}

fn add_distance(autocomp_resp: &mut model::Autocomplete, origin_coord: &Coord) {
    for feature in &mut autocomp_resp.features {
        if let ::geojson::Value::Point(p) = &feature.geometry.value {
            if let [mut lon, mut lat] = p.as_slice() {
                let feature_coord = Coord { lon, lat };
                feature.distance = Some(feature_coord.distance_to(&origin_coord) as u32);
            }
        }
    }
}

fn render<T>(
    mut client: rustless::Client,
    obj: T,
) -> Result<rustless::Client, rustless::ErrorResponse>
where
    T: serde::Serialize + model::v1::HasStatus,
{
    client.set_json_content_type();
    client.set_header(header::AccessControlAllowOrigin::Any);
    client.set_status(obj.status());
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String,
    pub max_es_timeout: Option<time::Duration>,
}

impl ApiEndPoint {
    pub fn root(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("", |endpoint| {
                endpoint.handle(|client, _params| {
                    let desc = EndPoint {
                        description: "autocomplete service".to_string(),
                    };
                    render(client, desc)
                })
            });

            api.error_formatter(|error, _media| {
                let err = if error.is::<rustless::errors::Validation>() {
                    let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                    // TODO better message, we shouldn't use {:?} but access the `path`
                    // and `detail` of all errrors in val_err.reason
                    CustomError {
                        short: "validation error".to_string(),
                        long: format!("invalid arguments {:?}", val_err.reason),
                        status: IronStatus::BadRequest,
                    }
                } else {
                    CustomError {
                        short: "bad_request".to_string(),
                        long: format!("bad request, error: {}", error),
                        status: IronStatus::BadRequest,
                    }
                };
                let mut resp = rustless::Response::from(
                    err.status,
                    Box::new(serde_json::to_string(&err).unwrap()),
                );
                resp.set_json_content_type();
                Some(resp)
            });

            api.before(|client, _params| {
                let method = client.endpoint.method.to_string();

                HTTP_REQ_HISTOGRAM
                    .get_metric_with(&labels! {
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                    })
                    .map(|timer| {
                        client.ext.insert::<Timer>(timer.start_timer());
                    })
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_REQ_HISTOGRAM metrics";
                               "err" => err.to_string());
                    });
                HTTP_IN_FLIGHT.inc();
                Ok(())
            });

            api.after(|client, _params| {
                let method = client.endpoint.method.to_string();
                let code = client.status().to_string();
                HTTP_IN_FLIGHT.dec();

                HTTP_COUNTER
                    .get_metric_with(&labels! {
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                        "status" => code.as_str(),
                    })
                    .map(|counter| counter.inc())
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_COUNTER metrics"; "err" => err.to_string());
                    });
                client
                    .ext
                    .remove::<Timer>()
                    .map(|timer| timer.observe_duration())
                    .unwrap_or_else(|| error!("impossible to get timers from typemap"));
                Ok(())
            });
            api.mount(self.v1());
        })
    }

    fn v1(&self) -> rustless::Api {
        Api::build(|api| {
            api.mount(self.status());
            api.mount(self.autocomplete());
            api.mount(self.features());
            api.mount(self.reverse());
            api.mount(self.metrics());
        })
    }

    fn status(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("status", |endpoint| {
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, _params| {
                    let status = Status {
                        version: env!("CARGO_PKG_VERSION").to_string(),
                        es: cnx.to_string(),
                        status: "good".to_string(),
                    };
                    render(client, status)
                })
            });
        })
    }

    fn metrics(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("metrics", |endpoint| {
                endpoint.handle(move |mut client, _params| {
                    let encoder = prometheus::TextEncoder::new();
                    let metric_familys = prometheus::gather();
                    let mut buffer = vec![];
                    encoder.encode(&metric_familys, &mut buffer).unwrap();
                    client.set_content_type(encoder.format_type().parse::<Mime>().unwrap());
                    client.text(String::from_utf8(buffer).unwrap())
                })
            });
        })
    }

    fn reverse(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("reverse", |endpoint| {
                endpoint.params(|params| {
                    coord_param(params, false);
                    timeout_param(params);
                });
                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let coord = ::mimir::Coord::new(
                        params.find("lon").and_then(|p| p.as_f64()).unwrap(),
                        params.find("lat").and_then(|p| p.as_f64()).unwrap(),
                    );
                    let mut rubber = Rubber::new(&cnx);
                    let timeout = parse_timeout(params, default_timeout);
                    rubber.set_read_timeout(timeout);
                    rubber.set_write_timeout(timeout);
                    let model_autocomplete = rubber
                        .get_address(&coord, timeout)
                        .map_err(model::BragiError::from);

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }

    fn features(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("features/:id", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("id", json_dsl::string());
                    dataset_param(params);
                    timeout_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let id = params.find("id").unwrap().as_str().unwrap();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let timeout = parse_timeout(params, default_timeout);
                    let features = query::features(&pt_datasets, all_data, &cnx, &id, timeout);
                    let response = model::v1::AutocompleteResponse::from(features);
                    render(client, response)
                })
            });
        })
    }

    fn autocomplete(&self) -> rustless::Api {
        Api::build(|api| {
            api.post("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    shape_param(params);
                    types_param(params);
                    timeout_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let geometry = params.find_path(&["shape", "geometry"]).unwrap();
                    let coordinates = geometry
                        .find_path(&["coordinates"])
                        .unwrap()
                        .as_array()
                        .unwrap();
                    let mut shape = Vec::new();
                    for ar in coordinates[0].as_array().unwrap() {
                        // (Lat, Lon)
                        shape.push((
                            ar.as_array().unwrap()[1].as_f64().unwrap(),
                            ar.as_array().unwrap()[0].as_f64().unwrap(),
                        ));
                    }
                    let types = get_param_array(params, "type");
                    let timeout = parse_timeout(params, default_timeout);
                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        None,
                        &cnx,
                        Some(shape),
                        &types,
                        timeout,
                    );
                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
            api.get("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    coord_param(params, true);
                    types_param(params);
                    timeout_param(params);
                });
                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let lon = params.find("lon").and_then(|p| p.as_f64());
                    let lat = params.find("lat").and_then(|p| p.as_f64());
                    // we have already checked that if there is a lon, lat
                    // is not None so we can unwrap
                    let coord = lon.and_then(|lon| {
                        Some(Coord {
                            lon: lon,
                            lat: lat.unwrap(),
                        })
                    });

                    let types = get_param_array(params, "type");
                    let timeout = parse_timeout(params, default_timeout);
                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        coord,
                        &cnx,
                        None,
                        &types,
                        timeout,
                    );

                    let mut response = model::v1::AutocompleteResponse::from(model_autocomplete);

                    // Optional : add distance for each feature (in meters)
                    use crate::model::v1::AutocompleteResponse::Autocomplete;
                    if let (Some(coord), Autocomplete(autocomplete_resp)) = (&coord, &mut response)
                    {
                        add_distance(autocomplete_resp, coord);
                    }

                    render(client, response)
                })
            });
        })
    }
}
<commit_msg>remove warning<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::query;
use crate::model;
use crate::model::v1::*;
use crate::params::{
    coord_param, dataset_param, get_param_array, paginate_param, shape_param, timeout_param,
    types_param,
};
use hyper::mime::Mime;
use iron::status::Status as IronStatus;
use iron::typemap::Key;
use mimir::rubber::Rubber;
use prometheus;
use prometheus::Encoder;
use rustless;
use rustless::server::header;
use rustless::{Api, Nesting};
use serde;
use serde_json;
use std::time;
use valico::json_dsl;

use navitia_model::objects::Coord;

const DEFAULT_LIMIT: u64 = 10u64;
const DEFAULT_OFFSET: u64 = 0u64;

struct Timer;
impl Key for Timer {
    type Value = prometheus::HistogramTimer;
}

lazy_static! {
    static ref HTTP_COUNTER: prometheus::CounterVec = register_counter_vec!(
        "bragi_http_requests_total",
        "Total number of HTTP requests made.",
        &["handler", "method", "status"]
    )
    .unwrap();
    static ref HTTP_REQ_HISTOGRAM: prometheus::HistogramVec = register_histogram_vec!(
        "bragi_http_request_duration_seconds",
        "The HTTP request latencies in seconds.",
        &["handler", "method"],
        prometheus::exponential_buckets(0.001, 1.5, 25).unwrap()
    )
    .unwrap();
    static ref HTTP_IN_FLIGHT: prometheus::Gauge = register_gauge!(
        "bragi_http_requests_in_flight",
        "current number of http request being served"
    )
    .unwrap();
}

/// get the timeout from the query 'timeout' parameter
/// if the param is not there, use the default value
fn parse_timeout(
    params: &rustless::json::JsonValue,
    default_timeout: Option<time::Duration>,
) -> Option<time::Duration> {
    params
        .find("timeout")
        .and_then(|v| v.as_u64())
        .map(time::Duration::from_millis)
        .map(|t| match default_timeout {
            Some(dt) => t.min(dt),
            None => t,
        })
        .or(default_timeout)
}

fn add_distance(autocomp_resp: &mut model::Autocomplete, origin_coord: &Coord) {
    for feature in &mut autocomp_resp.features {
        if let ::geojson::Value::Point(p) = &feature.geometry.value {
            if let [lon, lat] = p.as_slice() {
                let feature_coord = Coord {
                    lon: *lon,
                    lat: *lat,
                };
                feature.distance = Some(feature_coord.distance_to(&origin_coord) as u32);
            }
        }
    }
}

fn render<T>(
    mut client: rustless::Client,
    obj: T,
) -> Result<rustless::Client, rustless::ErrorResponse>
where
    T: serde::Serialize + model::v1::HasStatus,
{
    client.set_json_content_type();
    client.set_header(header::AccessControlAllowOrigin::Any);
    client.set_status(obj.status());
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String,
    pub max_es_timeout: Option<time::Duration>,
}

impl ApiEndPoint {
    pub fn root(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("", |endpoint| {
                endpoint.handle(|client, _params| {
                    let desc = EndPoint {
                        description: "autocomplete service".to_string(),
                    };
                    render(client, desc)
                })
            });

            api.error_formatter(|error, _media| {
                let err = if error.is::<rustless::errors::Validation>() {
                    let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                    // TODO better message, we shouldn't use {:?} but access the `path`
                    // and `detail` of all errrors in val_err.reason
                    CustomError {
                        short: "validation error".to_string(),
                        long: format!("invalid arguments {:?}", val_err.reason),
                        status: IronStatus::BadRequest,
                    }
                } else {
                    CustomError {
                        short: "bad_request".to_string(),
                        long: format!("bad request, error: {}", error),
                        status: IronStatus::BadRequest,
                    }
                };
                let mut resp = rustless::Response::from(
                    err.status,
                    Box::new(serde_json::to_string(&err).unwrap()),
                );
                resp.set_json_content_type();
                Some(resp)
            });

            api.before(|client, _params| {
                let method = client.endpoint.method.to_string();

                HTTP_REQ_HISTOGRAM
                    .get_metric_with(&labels! {
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                    })
                    .map(|timer| {
                        client.ext.insert::<Timer>(timer.start_timer());
                    })
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_REQ_HISTOGRAM metrics";
                               "err" => err.to_string());
                    });
                HTTP_IN_FLIGHT.inc();
                Ok(())
            });

            api.after(|client, _params| {
                let method = client.endpoint.method.to_string();
                let code = client.status().to_string();
                HTTP_IN_FLIGHT.dec();

                HTTP_COUNTER
                    .get_metric_with(&labels! {
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                        "status" => code.as_str(),
                    })
                    .map(|counter| counter.inc())
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_COUNTER metrics"; "err" => err.to_string());
                    });
                client
                    .ext
                    .remove::<Timer>()
                    .map(|timer| timer.observe_duration())
                    .unwrap_or_else(|| error!("impossible to get timers from typemap"));
                Ok(())
            });
            api.mount(self.v1());
        })
    }

    fn v1(&self) -> rustless::Api {
        Api::build(|api| {
            api.mount(self.status());
            api.mount(self.autocomplete());
            api.mount(self.features());
            api.mount(self.reverse());
            api.mount(self.metrics());
        })
    }

    fn status(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("status", |endpoint| {
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, _params| {
                    let status = Status {
                        version: env!("CARGO_PKG_VERSION").to_string(),
                        es: cnx.to_string(),
                        status: "good".to_string(),
                    };
                    render(client, status)
                })
            });
        })
    }

    fn metrics(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("metrics", |endpoint| {
                endpoint.handle(move |mut client, _params| {
                    let encoder = prometheus::TextEncoder::new();
                    let metric_familys = prometheus::gather();
                    let mut buffer = vec![];
                    encoder.encode(&metric_familys, &mut buffer).unwrap();
                    client.set_content_type(encoder.format_type().parse::<Mime>().unwrap());
                    client.text(String::from_utf8(buffer).unwrap())
                })
            });
        })
    }

    fn reverse(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("reverse", |endpoint| {
                endpoint.params(|params| {
                    coord_param(params, false);
                    timeout_param(params);
                });
                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let coord = ::mimir::Coord::new(
                        params.find("lon").and_then(|p| p.as_f64()).unwrap(),
                        params.find("lat").and_then(|p| p.as_f64()).unwrap(),
                    );
                    let mut rubber = Rubber::new(&cnx);
                    let timeout = parse_timeout(params, default_timeout);
                    rubber.set_read_timeout(timeout);
                    rubber.set_write_timeout(timeout);
                    let model_autocomplete = rubber
                        .get_address(&coord, timeout)
                        .map_err(model::BragiError::from);

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }

    fn features(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("features/:id", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("id", json_dsl::string());
                    dataset_param(params);
                    timeout_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let id = params.find("id").unwrap().as_str().unwrap();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let timeout = parse_timeout(params, default_timeout);
                    let features = query::features(&pt_datasets, all_data, &cnx, &id, timeout);
                    let response = model::v1::AutocompleteResponse::from(features);
                    render(client, response)
                })
            });
        })
    }

    fn autocomplete(&self) -> rustless::Api {
        Api::build(|api| {
            api.post("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    shape_param(params);
                    types_param(params);
                    timeout_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let geometry = params.find_path(&["shape", "geometry"]).unwrap();
                    let coordinates = geometry
                        .find_path(&["coordinates"])
                        .unwrap()
                        .as_array()
                        .unwrap();
                    let mut shape = Vec::new();
                    for ar in coordinates[0].as_array().unwrap() {
                        // (Lat, Lon)
                        shape.push((
                            ar.as_array().unwrap()[1].as_f64().unwrap(),
                            ar.as_array().unwrap()[0].as_f64().unwrap(),
                        ));
                    }
                    let types = get_param_array(params, "type");
                    let timeout = parse_timeout(params, default_timeout);
                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        None,
                        &cnx,
                        Some(shape),
                        &types,
                        timeout,
                    );
                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
            api.get("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    coord_param(params, true);
                    types_param(params);
                    timeout_param(params);
                });
                let cnx = self.es_cnx_string.clone();
                let default_timeout = self.max_es_timeout.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let lon = params.find("lon").and_then(|p| p.as_f64());
                    let lat = params.find("lat").and_then(|p| p.as_f64());
                    // we have already checked that if there is a lon, lat
                    // is not None so we can unwrap
                    let coord = lon.and_then(|lon| {
                        Some(Coord {
                            lon: lon,
                            lat: lat.unwrap(),
                        })
                    });

                    let types = get_param_array(params, "type");
                    let timeout = parse_timeout(params, default_timeout);
                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        coord,
                        &cnx,
                        None,
                        &types,
                        timeout,
                    );

                    let mut response = model::v1::AutocompleteResponse::from(model_autocomplete);

                    // Optional : add distance for each feature (in meters)
                    use crate::model::v1::AutocompleteResponse::Autocomplete;
                    if let (Some(coord), Autocomplete(autocomplete_resp)) = (&coord, &mut response)
                    {
                        add_distance(autocomplete_resp, coord);
                    }

                    render(client, response)
                })
            });
        })
    }
}
<|endoftext|>