<commit_before>//! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes.

use std::cmp;
use std::hash::Hasher;
use std::mem;
use std::ptr;

#[cfg(test)]
mod tests;

#[derive(Debug, Clone)]
pub struct SipHasher128 {
    k0: u64,
    k1: u64,
    length: usize, // how many bytes we've processed
    state: State,  // hash State
    tail: u64,     // unprocessed bytes le
    ntail: usize,  // how many bytes in tail are valid
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct State {
    // v0, v2 and v1, v3 show up in pairs in the algorithm,
    // and simd implementations of SipHash will use vectors
    // of v02 and v13. By placing them in this order in the struct,
    // the compiler can pick up on just a few simd optimizations by itself.
    v0: u64,
    v2: u64,
    v1: u64,
    v3: u64,
}

macro_rules! compress {
    ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }};
    ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{
        $v0 = $v0.wrapping_add($v1);
        $v1 = $v1.rotate_left(13);
        $v1 ^= $v0;
        $v0 = $v0.rotate_left(32);
        $v2 = $v2.wrapping_add($v3);
        $v3 = $v3.rotate_left(16);
        $v3 ^= $v2;
        $v0 = $v0.wrapping_add($v3);
        $v3 = $v3.rotate_left(21);
        $v3 ^= $v0;
        $v2 = $v2.wrapping_add($v1);
        $v1 = $v1.rotate_left(17);
        $v1 ^= $v2;
        $v2 = $v2.rotate_left(32);
    }};
}

/// Loads an integer of the desired type from a byte stream, in LE order. Uses
/// `copy_nonoverlapping` to let the compiler generate the most efficient way
/// to load it from a possibly unaligned address.
///
/// Unsafe because: unchecked indexing at i..i+size_of(int_ty)
macro_rules! load_int_le {
    ($buf:expr, $i:expr, $int_ty:ident) => {{
        debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len());
        let mut data = 0 as $int_ty;
        ptr::copy_nonoverlapping(
            $buf.get_unchecked($i),
            &mut data as *mut _ as *mut u8,
            mem::size_of::<$int_ty>(),
        );
        data.to_le()
    }};
}

/// Loads an u64 using up to 7 bytes of a byte slice.
///
/// Unsafe because: unchecked indexing at start..start+len
#[inline]
unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
    debug_assert!(len < 8);
    let mut i = 0; // current byte index (from LSB) in the output u64
    let mut out = 0;
    if i + 3 < len {
        out = u64::from(load_int_le!(buf, start + i, u32));
        i += 4;
    }
    if i + 1 < len {
        out |= u64::from(load_int_le!(buf, start + i, u16)) << (i * 8);
        i += 2
    }
    if i < len {
        out |= u64::from(*buf.get_unchecked(start + i)) << (i * 8);
        i += 1;
    }
    debug_assert_eq!(i, len);
    out
}

impl SipHasher128 {
    #[inline]
    pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 {
        let mut state = SipHasher128 {
            k0: key0,
            k1: key1,
            length: 0,
            state: State { v0: 0, v1: 0, v2: 0, v3: 0 },
            tail: 0,
            ntail: 0,
        };
        state.reset();
        state
    }

    #[inline]
    fn reset(&mut self) {
        self.length = 0;
        self.state.v0 = self.k0 ^ 0x736f6d6570736575;
        self.state.v1 = self.k1 ^ 0x646f72616e646f6d;
        self.state.v2 = self.k0 ^ 0x6c7967656e657261;
        self.state.v3 = self.k1 ^ 0x7465646279746573;
        self.ntail = 0;

        // This is only done in the 128 bit version:
        self.state.v1 ^= 0xee;
    }

    // A specialized write function for values with size <= 8.
    //
    // The hashing of multi-byte integers depends on endianness. E.g.:
    // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
    // - big-endian:    `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
    //
    // This function does the right thing for little-endian hardware. On
    // big-endian hardware `x` must be byte-swapped first to give the right
    // behaviour. After any byte-swapping, the input must be zero-extended to
    // 64-bits. The caller is responsible for the byte-swapping and
    // zero-extension.
    #[inline]
    fn short_write<T>(&mut self, _x: T, x: u64) {
        let size = mem::size_of::<T>();
        self.length += size;

        // The original number must be zero-extended, not sign-extended.
        debug_assert!(if size < 8 { x >> (8 * size) == 0 } else { true });

        // The number of bytes needed to fill `self.tail`.
        let needed = 8 - self.ntail;

        // SipHash parses the input stream as 8-byte little-endian integers.
        // Inputs are put into `self.tail` until 8 bytes of data have been
        // collected, and then that word is processed.
        //
        // For example, imagine that `self.tail` is 0x0000_00EE_DDCC_BBAA,
        // `self.ntail` is 5 (because 5 bytes have been put into `self.tail`),
        // and `needed` is therefore 3.
        //
        // - Scenario 1, `self.write_u8(0xFF)`: we have already zero-extended
        //   the input to 0x0000_0000_0000_00FF. We now left-shift it five
        //   bytes, giving 0x0000_FF00_0000_0000. We then bitwise-OR that value
        //   into `self.tail`, resulting in 0x0000_FFEE_DDCC_BBAA.
        //   (Zero-extension of the original input is critical in this scenario
        //   because we don't want the high two bytes of `self.tail` to be
        //   touched by the bitwise-OR.) `self.tail` is not yet full, so we
        //   return early, after updating `self.ntail` to 6.
        //
        // - Scenario 2, `self.write_u32(0xIIHH_GGFF)`: we have already
        //   zero-extended the input to 0x0000_0000_IIHH_GGFF. We now
        //   left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then
        //   bitwise-OR that value into `self.tail`, resulting in
        //   0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it
        //   to update `self.state`. (As mentioned above, this assumes a
        //   little-endian machine; on a big-endian machine we would have
        //   byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we
        //   would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into
        //   `self.tail`).
        //
        self.tail |= x << (8 * self.ntail);
        if size < needed {
            self.ntail += size;
            return;
        }

        // `self.tail` is full, process it.
        self.state.v3 ^= self.tail;
        Sip24Rounds::c_rounds(&mut self.state);
        self.state.v0 ^= self.tail;

        // Continuing scenario 2: we have one byte left over from the input. We
        // set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >>
        // 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine
        // the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.)
        //
        // The `if` is needed to avoid shifting by 64 bits, which Rust
        // complains about.
        self.ntail = size - needed;
        self.tail = if needed < 8 { x >> (8 * needed) } else { 0 };
    }

    #[inline]
    pub fn finish128(mut self) -> (u64, u64) {
        let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;

        self.state.v3 ^= b;
        Sip24Rounds::c_rounds(&mut self.state);
        self.state.v0 ^= b;

        self.state.v2 ^= 0xee;
        Sip24Rounds::d_rounds(&mut self.state);
        let _0 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3;

        self.state.v1 ^= 0xdd;
        Sip24Rounds::d_rounds(&mut self.state);
        let _1 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3;
        (_0, _1)
    }
}

impl Hasher for SipHasher128 {
    #[inline]
    fn write_u8(&mut self, i: u8) {
        self.short_write(i, i as u64);
    }

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

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

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

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

    #[inline]
    fn write_i8(&mut self, i: i8) {
        self.short_write(i, i as u8 as u64);
    }

    #[inline]
    fn write_i16(&mut self, i: i16) {
        self.short_write(i, (i as u16).to_le() as u64);
    }

    #[inline]
    fn write_i32(&mut self, i: i32) {
        self.short_write(i, (i as u32).to_le() as u64);
    }

    #[inline]
    fn write_i64(&mut self, i: i64) {
        self.short_write(i, (i as u64).to_le() as u64);
    }

    #[inline]
    fn write_isize(&mut self, i: isize) {
        self.short_write(i, (i as usize).to_le() as u64);
    }

    #[inline]
    fn write(&mut self, msg: &[u8]) {
        let length = msg.len();
        self.length += length;

        let mut needed = 0;

        if self.ntail != 0 {
            needed = 8 - self.ntail;
            self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail);
            if length < needed {
                self.ntail += length;
                return;
            } else {
                self.state.v3 ^= self.tail;
                Sip24Rounds::c_rounds(&mut self.state);
                self.state.v0 ^= self.tail;
                self.ntail = 0;
            }
        }

        // Buffered tail is now flushed, process new input.
        let len = length - needed;
        let left = len & 0x7;

        let mut i = needed;
        while i < len - left {
            let mi = unsafe { load_int_le!(msg, i, u64) };

            self.state.v3 ^= mi;
            Sip24Rounds::c_rounds(&mut self.state);
            self.state.v0 ^= mi;

            i += 8;
        }

        self.tail = unsafe { u8to64_le(msg, i, left) };
        self.ntail = left;
    }

    fn finish(&self) -> u64 {
        panic!("SipHasher128 cannot provide valid 64 bit hashes")
    }
}

#[derive(Debug, Clone, Default)]
struct Sip24Rounds;

impl Sip24Rounds {
    #[inline]
    fn c_rounds(state: &mut State) {
        compress!(state);
        compress!(state);
    }

    #[inline]
    fn d_rounds(state: &mut State) {
        compress!(state);
        compress!(state);
        compress!(state);
        compress!(state);
    }
}
<commit_msg>Improve `u8to64_le`.<commit_after>//! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes.

use std::cmp;
use std::hash::Hasher;
use std::mem;
use std::ptr;

#[cfg(test)]
mod tests;

#[derive(Debug, Clone)]
pub struct SipHasher128 {
    k0: u64,
    k1: u64,
    length: usize, // how many bytes we've processed
    state: State,  // hash State
    tail: u64,     // unprocessed bytes le
    ntail: usize,  // how many bytes in tail are valid
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct State {
    // v0, v2 and v1, v3 show up in pairs in the algorithm,
    // and simd implementations of SipHash will use vectors
    // of v02 and v13. By placing them in this order in the struct,
    // the compiler can pick up on just a few simd optimizations by itself.
    v0: u64,
    v2: u64,
    v1: u64,
    v3: u64,
}

macro_rules! compress {
    ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }};
    ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{
        $v0 = $v0.wrapping_add($v1);
        $v1 = $v1.rotate_left(13);
        $v1 ^= $v0;
        $v0 = $v0.rotate_left(32);
        $v2 = $v2.wrapping_add($v3);
        $v3 = $v3.rotate_left(16);
        $v3 ^= $v2;
        $v0 = $v0.wrapping_add($v3);
        $v3 = $v3.rotate_left(21);
        $v3 ^= $v0;
        $v2 = $v2.wrapping_add($v1);
        $v1 = $v1.rotate_left(17);
        $v1 ^= $v2;
        $v2 = $v2.rotate_left(32);
    }};
}

/// Loads up to 8 bytes from a byte-slice into a little-endian u64.
#[inline]
fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
    assert!(len <= 8 && start + len <= buf.len());

    let mut out = 0u64;
    unsafe {
        let out_ptr = &mut out as *mut _ as *mut u8;
        ptr::copy_nonoverlapping(buf.as_ptr().offset(start as isize), out_ptr, len);
    }
    out.to_le()
}

impl SipHasher128 {
    #[inline]
    pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 {
        let mut state = SipHasher128 {
            k0: key0,
            k1: key1,
            length: 0,
            state: State { v0: 0, v1: 0, v2: 0, v3: 0 },
            tail: 0,
            ntail: 0,
        };
        state.reset();
        state
    }

    #[inline]
    fn reset(&mut self) {
        self.length = 0;
        self.state.v0 = self.k0 ^ 0x736f6d6570736575;
        self.state.v1 = self.k1 ^ 0x646f72616e646f6d;
        self.state.v2 = self.k0 ^ 0x6c7967656e657261;
        self.state.v3 = self.k1 ^ 0x7465646279746573;
        self.ntail = 0;

        // This is only done in the 128 bit version:
        self.state.v1 ^= 0xee;
    }

    // A specialized write function for values with size <= 8.
    //
    // The hashing of multi-byte integers depends on endianness. E.g.:
    // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
    // - big-endian:    `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
    //
    // This function does the right thing for little-endian hardware. On
    // big-endian hardware `x` must be byte-swapped first to give the right
    // behaviour. After any byte-swapping, the input must be zero-extended to
    // 64-bits. The caller is responsible for the byte-swapping and
    // zero-extension.
    #[inline]
    fn short_write<T>(&mut self, _x: T, x: u64) {
        let size = mem::size_of::<T>();
        self.length += size;

        // The original number must be zero-extended, not sign-extended.
        debug_assert!(if size < 8 { x >> (8 * size) == 0 } else { true });

        // The number of bytes needed to fill `self.tail`.
        let needed = 8 - self.ntail;

        // SipHash parses the input stream as 8-byte little-endian integers.
        // Inputs are put into `self.tail` until 8 bytes of data have been
        // collected, and then that word is processed.
        //
        // For example, imagine that `self.tail` is 0x0000_00EE_DDCC_BBAA,
        // `self.ntail` is 5 (because 5 bytes have been put into `self.tail`),
        // and `needed` is therefore 3.
        //
        // - Scenario 1, `self.write_u8(0xFF)`: we have already zero-extended
        //   the input to 0x0000_0000_0000_00FF. We now left-shift it five
        //   bytes, giving 0x0000_FF00_0000_0000. We then bitwise-OR that value
        //   into `self.tail`, resulting in 0x0000_FFEE_DDCC_BBAA.
        //   (Zero-extension of the original input is critical in this scenario
        //   because we don't want the high two bytes of `self.tail` to be
        //   touched by the bitwise-OR.) `self.tail` is not yet full, so we
        //   return early, after updating `self.ntail` to 6.
        //
        // - Scenario 2, `self.write_u32(0xIIHH_GGFF)`: we have already
        //   zero-extended the input to 0x0000_0000_IIHH_GGFF. We now
        //   left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then
        //   bitwise-OR that value into `self.tail`, resulting in
        //   0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it
        //   to update `self.state`. (As mentioned above, this assumes a
        //   little-endian machine; on a big-endian machine we would have
        //   byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we
        //   would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into
        //   `self.tail`).
        //
        self.tail |= x << (8 * self.ntail);
        if size < needed {
            self.ntail += size;
            return;
        }

        // `self.tail` is full, process it.
        self.state.v3 ^= self.tail;
        Sip24Rounds::c_rounds(&mut self.state);
        self.state.v0 ^= self.tail;

        // Continuing scenario 2: we have one byte left over from the input. We
        // set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >>
        // 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine
        // the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.)
        //
        // The `if` is needed to avoid shifting by 64 bits, which Rust
        // complains about.
        self.ntail = size - needed;
        self.tail = if needed < 8 { x >> (8 * needed) } else { 0 };
    }

    #[inline]
    pub fn finish128(mut self) -> (u64, u64) {
        let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;

        self.state.v3 ^= b;
        Sip24Rounds::c_rounds(&mut self.state);
        self.state.v0 ^= b;

        self.state.v2 ^= 0xee;
        Sip24Rounds::d_rounds(&mut self.state);
        let _0 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3;

        self.state.v1 ^= 0xdd;
        Sip24Rounds::d_rounds(&mut self.state);
        let _1 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3;
        (_0, _1)
    }
}

impl Hasher for SipHasher128 {
    #[inline]
    fn write_u8(&mut self, i: u8) {
        self.short_write(i, i as u64);
    }

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

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

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

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

    #[inline]
    fn write_i8(&mut self, i: i8) {
        self.short_write(i, i as u8 as u64);
    }

    #[inline]
    fn write_i16(&mut self, i: i16) {
        self.short_write(i, (i as u16).to_le() as u64);
    }

    #[inline]
    fn write_i32(&mut self, i: i32) {
        self.short_write(i, (i as u32).to_le() as u64);
    }

    #[inline]
    fn write_i64(&mut self, i: i64) {
        self.short_write(i, (i as u64).to_le() as u64);
    }

    #[inline]
    fn write_isize(&mut self, i: isize) {
        self.short_write(i, (i as usize).to_le() as u64);
    }

    #[inline]
    fn write(&mut self, msg: &[u8]) {
        let length = msg.len();
        self.length += length;

        let mut needed = 0;

        if self.ntail != 0 {
            needed = 8 - self.ntail;
            self.tail |= u8to64_le(msg, 0, cmp::min(length, needed)) << (8 * self.ntail);
            if length < needed {
                self.ntail += length;
                return;
            } else {
                self.state.v3 ^= self.tail;
                Sip24Rounds::c_rounds(&mut self.state);
                self.state.v0 ^= self.tail;
                self.ntail = 0;
            }
        }

        // Buffered tail is now flushed, process new input.
        let len = length - needed;
        let left = len & 0x7;

        let mut i = needed;
        while i < len - left {
            let mi = u8to64_le(msg, i, 8);

            self.state.v3 ^= mi;
            Sip24Rounds::c_rounds(&mut self.state);
            self.state.v0 ^= mi;

            i += 8;
        }

        self.tail = u8to64_le(msg, i, left);
        self.ntail = left;
    }

    fn finish(&self) -> u64 {
        panic!("SipHasher128 cannot provide valid 64 bit hashes")
    }
}

#[derive(Debug, Clone, Default)]
struct Sip24Rounds;

impl Sip24Rounds {
    #[inline]
    fn c_rounds(state: &mut State) {
        compress!(state);
        compress!(state);
    }

    #[inline]
    fn d_rounds(state: &mut State) {
        compress!(state);
        compress!(state);
        compress!(state);
        compress!(state);
    }
}
<|endoftext|>


<commit_before>use crate::*;
use rustc::mir;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();

        match link_name {
            "__errno_location" => {
                let errno_place = this.machine.last_error.unwrap();
                this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
            }

            // File related shims

            // The only reason this is not in the `posix` module is because the `macos` item has a
            // different name.
            "close" => {
                let result = this.close(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // Time related shims

            // This is a POSIX function but it has only been tested on linux.
            "clock_gettime" => {
                let result = this.clock_gettime(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // Other shims
            "pthread_getattr_np" => {
                this.write_null(dest)?;
            }

            "syscall" => {
                let sys_getrandom = this
                    .eval_path_scalar(&["libc", "SYS_getrandom"])?
                    .expect("Failed to get libc::SYS_getrandom")
                    .to_machine_usize(this)?;

                let sys_statx = this
                    .eval_path_scalar(&["libc", "SYS_statx"])?
                    .expect("Failed to get libc::SYS_statx")
                    .to_machine_usize(this)?;

                match this.read_scalar(args[0])?.to_machine_usize(this)? {
                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                    id if id == sys_getrandom => {
                        // The first argument is the syscall id,
                        // so skip over it.
                        getrandom(this, &args[1..], dest)?;
                    }
                    // `statx` is used by `libstd` to retrieve metadata information in `linux`
                    // instead of using `stat`,`lstat` or `fstat` as in the `macos` platform.
                    id if id == sys_statx => {
                        // The first argument is the syscall id,
                        // so skip over it.
                        let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?;
                        this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
                    }
                    id => throw_unsup_format!("miri does not support syscall ID {}", id),
                }
            }

            "getrandom" => {
                getrandom(this, args, dest)?;
            }

            "sched_getaffinity" => {
                // Return an error; `num_cpus` then falls back to `sysconf`.
                this.write_scalar(Scalar::from_int(-1, dest.layout.size), dest)?;
            }

            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
        };

        Ok(true)
    }
}

// Shims the linux 'getrandom()' syscall.
fn getrandom<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    args: &[OpTy<'tcx, Tag>],
    dest: PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    let ptr = this.read_scalar(args[0])?.not_undef()?;
    let len = this.read_scalar(args[1])?.to_machine_usize(this)?;

    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
    // neither of which have any effect on our current PRNG.
    let _flags = this.read_scalar(args[2])?.to_i32()?;

    this.gen_random(ptr, len as usize)?;
    this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?;
    Ok(())
}
<commit_msg>improve comments<commit_after>use crate::*;
use rustc::mir;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();

        match link_name {
            "__errno_location" => {
                let errno_place = this.machine.last_error.unwrap();
                this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
            }

            // File related shims (but also see "syscall" below for statx)

            // The only reason this is not in the `posix` module is because the `macos` item has a
            // different name.
            "close" => {
                let result = this.close(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // Time related shims

            // This is a POSIX function but it has only been tested on linux.
            "clock_gettime" => {
                let result = this.clock_gettime(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // Other shims
            "pthread_getattr_np" => {
                this.write_null(dest)?;
            }

            "syscall" => {
                let sys_getrandom = this
                    .eval_path_scalar(&["libc", "SYS_getrandom"])?
                    .expect("Failed to get libc::SYS_getrandom")
                    .to_machine_usize(this)?;

                let sys_statx = this
                    .eval_path_scalar(&["libc", "SYS_statx"])?
                    .expect("Failed to get libc::SYS_statx")
                    .to_machine_usize(this)?;

                match this.read_scalar(args[0])?.to_machine_usize(this)? {
                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                    id if id == sys_getrandom => {
                        // The first argument is the syscall id,
                        // so skip over it.
                        getrandom(this, &args[1..], dest)?;
                    }
                    // `statx` is used by `libstd` to retrieve metadata information on `linux`
                    // instead of using `stat`,`lstat` or `fstat` as on `macos`.
                    id if id == sys_statx => {
                        // The first argument is the syscall id,
                        // so skip over it.
                        let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?;
                        this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
                    }
                    id => throw_unsup_format!("miri does not support syscall ID {}", id),
                }
            }

            "getrandom" => {
                getrandom(this, args, dest)?;
            }

            "sched_getaffinity" => {
                // Return an error; `num_cpus` then falls back to `sysconf`.
                this.write_scalar(Scalar::from_int(-1, dest.layout.size), dest)?;
            }

            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
        };

        Ok(true)
    }
}

// Shims the linux `getrandom` syscall.
fn getrandom<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    args: &[OpTy<'tcx, Tag>],
    dest: PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    let ptr = this.read_scalar(args[0])?.not_undef()?;
    let len = this.read_scalar(args[1])?.to_machine_usize(this)?;

    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
    // neither of which have any effect on our current PRNG.
    let _flags = this.read_scalar(args[2])?.to_i32()?;

    this.gen_random(ptr, len as usize)?;
    this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?;
    Ok(())
}
<|endoftext|>


<commit_before>//! Parsing of s-expressions.

use cell_gc::{GcHeapSession, GcLeaf};
use nom::IResult;
use std::str::FromStr;
use value::{InternedString, Pair, Value};

// === Tokens

// #[derive(Clone)]
// enum Token {
//     Identifier(Arc<String>),
//     Boolean(bool),
//     Number(String),
//     Char(char),
//     String(Arc<String>),
//     Open,
//     Close,
//     OpenVector,
//     Quote,
//     Quasiquote,
//     UnquoteSplicing,
//     Unquote,
//     Dot,
// }

fn push_char(mut s: String, c: char) -> String {
    s.push(c);
    s
}

// named!(
//     token(&str) -> Token,
//     preceded!(
//         intertoken_space,
//         alt!(
//             map!(identifier, |s| Token::Identifier(Arc::new(s)))
//           | map!(boolean, Token::Boolean)
//           | map!(number, Token::Number)
//           | map!(character, Token::Char)
//           | map!(string, |s| Token::String(Arc::new(s)))
//           | value!(Token::Open, open)
//           | value!(Token::Close, char!(')'))
//           | value!(Token::OpenVector, open_vector)
//           | value!(Token::Quote, char!('\''))
//           | value!(Token::Quasiquote, char!('`'))
//           | value!(Token::UnquoteSplicing, tag!(",@"))
//           | value!(Token::Unquote, char!(','))
//           | value!(Token::Dot, char!('.'))
//         )
//     )
// );

named!(open(&str) -> (), value!((), char!('(')));
named!(close(&str) -> (), value!((), char!(')')));
named!(open_vector(&str) -> (), value!((), tag!("#(")));
named!(delimiter(&str) -> (), alt!(value!((), one_of!(" \n\r\t()\";")) | value!((), eof!())));
named!(whitespace(&str) -> (), value!((), one_of!(" \n\r\t")));
named!(comment(&str) -> (), value!((), preceded!(char!(';'), take_until_and_consume!("\n"))));
named!(atmosphere(&str) -> (), alt!(whitespace | comment));
named!(intertoken_space(&str) -> (), value!((), many0!(atmosphere)));

#[test]
fn test_whitespace() {
    assert_eq!(whitespace(" \n\r\t"), IResult::Done("\n\r\t", ()));
    assert_eq!(
        comment("; hello\nnext line"),
        IResult::Done("next line", ())
    );
    assert_eq!(
        intertoken_space(";; nothing to see here\n;;\n;; just a program\n\n\t\n\n"),
        IResult::Done("", ())
    );
}

named!(
    identifier(&str) -> String,
    alt!(
        do_parse!(
            first: initial >>
            s: fold_many0!(
                subsequent,
                first.to_string(),
                push_char
            ) >>
            (s)
        )
      | map!(
            alt!(
                tag!("+")
                    | tag!("-")
                    | tag!("...")
            ),
            str::to_string
        )
    )
);

named!(initial(&str) -> char, alt!(letter | special_initial));

named!(letter(&str) -> char, one_of!("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));

named!(special_initial(&str) -> char, one_of!("!$%&*/:<=>?~_^"));

named!(subsequent(&str) -> char, alt!(initial | digit | one_of!(".+-")));

named!(digit(&str) -> char, one_of!("0123456789"));

named!(
    boolean(&str) -> bool,
    alt!(
        value!(true, terminated!(tag!("#t"), peek!(delimiter)))
      | value!(false, terminated!(tag!("#f"), peek!(delimiter)))
    )
);

named!(
    character(&str) -> char,
    alt!(
        value!(' ', terminated!(tag!("#\\space"), peek!(delimiter)))
      | value!('\n', terminated!(tag!("#\\newline"), peek!(delimiter)))
      | preceded!(tag!("#\\"), none_of!(""))
    )
);

named!(
    string(&str) -> String,
    delimited!(
        tag!("\""),
        fold_many0!(
            string_element,
            String::new(),
            push_char
        ),
        tag!("\"")
    )
);

named!(
    string_element(&str) -> char,
    alt!(
        none_of!("\"\\")
      | preceded!(char!('\\'), one_of!("\\\""))
    )
);

named!(
    number(&str) -> String,
    do_parse!(
        sign: sign >>
        num_str: fold_many1!(
            digit,
            sign.to_string(),
            push_char
        ) >>
        (num_str)
    )
);

named!(
    sign(&str) -> &'static str,
    alt!(
        value!("-", char!('-'))
      | value!("", opt!(char!('+')))
    )
);

// === Parsing

#[derive(PartialEq, Debug)]
enum Datum {
    Boolean(bool),
    Number(String),
    Char(char),
    String(String),
    Identifier(String),
    List(Vec<Datum>),
    ImproperList(Vec<Datum>),
    Vector(Vec<Datum>),
}

named!(
    file_to_data(&str) -> Vec<Datum>,
    delimited!(
        intertoken_space,
        many0!(terminated!(datum, intertoken_space)),
        eof!()
    )
);

#[test]
fn test_parse() {
    use nom::ErrorKind;

    assert_eq!(
        datum("376    "),
        IResult::Done("    ", Datum::Number("376".to_string()))
    );
    assert_eq!(datum("    376"), IResult::Error(ErrorKind::Alt));

    assert_eq!(file_to_data(""), IResult::Done("", vec![]));
    assert_eq!(file_to_data("\t"), IResult::Done("", vec![]));
    assert_eq!(
        file_to_data("    376"),
        IResult::Done("", vec![Datum::Number("376".to_string())])
    );
    assert_eq!(
        file_to_data("    376\n    212\n"),
        IResult::Done(
            "",
            vec![
                Datum::Number("376".to_string()),
                Datum::Number("212".to_string()),
            ],
        )
    );
    assert_eq!(
        file_to_data("    376\n    ;212\n"),
        IResult::Done("", vec![Datum::Number("376".to_string())])
    );
    assert_eq!(
        file_to_data(";; nothing to see here\n\n"),
        IResult::Done("", vec![])
    );
    assert_eq!(
        file_to_data("(1 2)"),
        IResult::Done(
            "",
            vec![
                Datum::List(vec![
                    Datum::Number("1".to_string()),
                    Datum::Number("2".to_string()),
                ]),
            ],
        )
    );
}

named!(
    datum(&str) -> Datum,
    alt!(simple_datum | compound_datum)
);

named!(
    simple_datum(&str) -> Datum,
    alt!(
        map!(boolean, Datum::Boolean)
      | map!(number, Datum::Number)
      | map!(character, Datum::Char)
      | map!(string, Datum::String)
      | map!(identifier, Datum::Identifier)
    )
);

named!(
    compound_datum(&str) -> Datum,
    alt!(list | vector)
);

named!(
    list(&str) -> Datum,
    alt!(
        preceded!(
            terminated!(open, intertoken_space),
            alt!(
                // Nonempty list or improper list.
                map!(
                    terminated!(
                        pair!(
                            // Normal list elements.
                            many1!(terminated!(datum, intertoken_space)),
                            // Optional improper ending.
                            opt!(
                                do_parse!(
                                    value!((), char!('.')) >>
                                    intertoken_space >>
                                    d: datum >>
                                    intertoken_space >>
                                    (d)
                                )
                            )
                        ),
                        close
                    ),
                    |(mut data, opt_improper)| {
                        match opt_improper {
                            Some(improper) => {
                                data.push(improper);
                                Datum::ImproperList(data)
                            }
                            None =>
                                Datum::List(data),
                        }
                    }
                )
              | map!(
                    close,
                    |()| Datum::List(vec![])
                )
            )
        )
      | do_parse!(
            prefix: alt!(
                value!("quote", char!('\''))
              | value!("quasiquote", char!('`'))
              | value!("unquote", char!(','))
              | value!("unquote-splicing", tag!(",@"))
            ) >>
            intertoken_space >>
            arg: datum >>
            (
                Datum::List(vec![
                    // allocation here is dumb; symbols should be reused, of course
                    Datum::Identifier(prefix.to_string()),
                    arg,
                ])
            )
        )
    )
);

named!(
    vector(&str) -> Datum,
    map!(
        delimited!(
            terminated!(open_vector, intertoken_space),
            many0!(terminated!(datum, intertoken_space)),
            close
        ),
        Datum::Vector
    )
);

#[test]
fn test_parse_vector() {
    use nom::ErrorKind;

    assert_eq!(vector("#()"), IResult::Done("", Datum::Vector(vec![])));
    assert_eq!(
        vector("#(a)"),
        IResult::Done("", Datum::Vector(vec![Datum::Identifier("a".to_string())]))
    );
    assert_eq!(vector("#(0 . 1)"), IResult::Error(ErrorKind::Char));
}

fn into_list<'h>(
    hs: &mut GcHeapSession<'h>,
    elements: Vec<Datum>,
    tail: Value<'h>,
) -> Result<Value<'h>, &'static str> {
    let mut list = tail;
    for d in elements.into_iter().rev() {
        let value = datum_to_value(hs, d)?;
        list = Value::Cons(hs.alloc(Pair {
            car: value,
            cdr: list,
        }));
    }
    Ok(list)
}

fn datum_to_value<'h>(hs: &mut GcHeapSession<'h>, datum: Datum) -> Result<Value<'h>, &'static str> {
    match datum {
        Datum::Boolean(b) => Ok(Value::Bool(b)),
        Datum::Number(s) => {
            if let Ok(i) = i32::from_str(&s) {
                Ok(Value::Int(i))
            } else {
                Err("unsupported number token")
            }
        }
        Datum::Char(c) => Ok(Value::Char(c)),
        Datum::String(s) => Ok(Value::ImmString(GcLeaf::new(InternedString::get(&s)))),
        Datum::Identifier(s) => Ok(Value::Symbol(GcLeaf::new(InternedString::get(&s)))),
        Datum::List(data) => into_list(hs, data, Value::Nil),
        Datum::ImproperList(mut data) => {
            let tail = data.pop()
                .expect("internal error: improper lists require a last value");
            let tail_val = datum_to_value(hs, tail)?;
            into_list(hs, data, tail_val)
        }
        Datum::Vector(data) => {
            let values: Result<Vec<Value<'h>>, &'static str> =
                data.into_iter().map(|d| datum_to_value(hs, d)).collect();
            Ok(Value::Vector(hs.alloc(values?)))
        }
    }
}

/// Top level entry point to s-expression parsing. Takes a source string and
/// returns a `Value` which can contain `Pair`s allocated in the GC heap.
pub fn parse<'h>(hs: &mut GcHeapSession<'h>, source: &str) -> Result<Vec<Value<'h>>, &'static str> {
    match file_to_data(source) {
        IResult::Error(_e) => Err("syntax error"),
        IResult::Incomplete(_e) => Err("incomplete input"),
        IResult::Done(leftovers, data) => {
            // The parser uses `eof!()` to ensure that on success, all input has been consumed.
            assert!(leftovers.is_empty(), "parser did not consume all input");

            Ok(data.into_iter()
                .map(|d| datum_to_value(hs, d))
                .collect::<Result<Vec<_>, _>>()?)
        }
    }
}
<commit_msg>lisp: Minor fix for parsing character literals.<commit_after>//! Parsing of s-expressions.

use cell_gc::{GcHeapSession, GcLeaf};
use nom::IResult;
use std::str::FromStr;
use value::{InternedString, Pair, Value};

// === Tokens

// #[derive(Clone)]
// enum Token {
//     Identifier(Arc<String>),
//     Boolean(bool),
//     Number(String),
//     Char(char),
//     String(Arc<String>),
//     Open,
//     Close,
//     OpenVector,
//     Quote,
//     Quasiquote,
//     UnquoteSplicing,
//     Unquote,
//     Dot,
// }

fn push_char(mut s: String, c: char) -> String {
    s.push(c);
    s
}

// named!(
//     token(&str) -> Token,
//     preceded!(
//         intertoken_space,
//         alt!(
//             map!(identifier, |s| Token::Identifier(Arc::new(s)))
//           | map!(boolean, Token::Boolean)
//           | map!(number, Token::Number)
//           | map!(character, Token::Char)
//           | map!(string, |s| Token::String(Arc::new(s)))
//           | value!(Token::Open, open)
//           | value!(Token::Close, char!(')'))
//           | value!(Token::OpenVector, open_vector)
//           | value!(Token::Quote, char!('\''))
//           | value!(Token::Quasiquote, char!('`'))
//           | value!(Token::UnquoteSplicing, tag!(",@"))
//           | value!(Token::Unquote, char!(','))
//           | value!(Token::Dot, char!('.'))
//         )
//     )
// );

named!(open(&str) -> (), value!((), char!('(')));
named!(close(&str) -> (), value!((), char!(')')));
named!(open_vector(&str) -> (), value!((), tag!("#(")));
named!(delimiter(&str) -> (), alt!(value!((), one_of!(" \n\r\t()\";")) | value!((), eof!())));
named!(whitespace(&str) -> (), value!((), one_of!(" \n\r\t")));
named!(comment(&str) -> (), value!((), preceded!(char!(';'), take_until_and_consume!("\n"))));
named!(atmosphere(&str) -> (), alt!(whitespace | comment));
named!(intertoken_space(&str) -> (), value!((), many0!(atmosphere)));

#[test]
fn test_whitespace() {
    assert_eq!(whitespace(" \n\r\t"), IResult::Done("\n\r\t", ()));
    assert_eq!(
        comment("; hello\nnext line"),
        IResult::Done("next line", ())
    );
    assert_eq!(
        intertoken_space(";; nothing to see here\n;;\n;; just a program\n\n\t\n\n"),
        IResult::Done("", ())
    );
}

named!(
    identifier(&str) -> String,
    alt!(
        do_parse!(
            first: initial >>
            s: fold_many0!(
                subsequent,
                first.to_string(),
                push_char
            ) >>
            (s)
        )
      | map!(
            alt!(
                tag!("+")
                    | tag!("-")
                    | tag!("...")
            ),
            str::to_string
        )
    )
);

named!(initial(&str) -> char, alt!(letter | special_initial));

named!(letter(&str) -> char, one_of!("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));

named!(special_initial(&str) -> char, one_of!("!$%&*/:<=>?~_^"));

named!(subsequent(&str) -> char, alt!(initial | digit | one_of!(".+-")));

named!(digit(&str) -> char, one_of!("0123456789"));

named!(
    boolean(&str) -> bool,
    alt!(
        value!(true, terminated!(tag!("#t"), peek!(delimiter)))
      | value!(false, terminated!(tag!("#f"), peek!(delimiter)))
    )
);

named!(
    character(&str) -> char,
    terminated!(
        alt!(
            value!(' ', tag!("#\\space"))
          | value!('\n', tag!("#\\newline"))
          | preceded!(tag!("#\\"), none_of!(""))
        ),
        peek!(delimiter)
    )
);

#[test]
fn test_character() {
    assert_eq!(
        character(r"#\space "),
        IResult::Done(" ", ' ')
    );
    assert_eq!(
        character(r"#\s "),
        IResult::Done(" ", 's')
    );
    assert!(character(r"#\sp ").is_err());
    assert_eq!(
        character(r"#\λ "),
        IResult::Done(" ", 'λ')
    );
    assert!(character(r"#\λx").is_err());
}

named!(
    string(&str) -> String,
    delimited!(
        tag!("\""),
        fold_many0!(
            string_element,
            String::new(),
            push_char
        ),
        tag!("\"")
    )
);

named!(
    string_element(&str) -> char,
    alt!(
        none_of!("\"\\")
      | preceded!(char!('\\'), one_of!("\\\""))
    )
);

named!(
    number(&str) -> String,
    do_parse!(
        sign: sign >>
        num_str: fold_many1!(
            digit,
            sign.to_string(),
            push_char
        ) >>
        (num_str)
    )
);

named!(
    sign(&str) -> &'static str,
    alt!(
        value!("-", char!('-'))
      | value!("", opt!(char!('+')))
    )
);

// === Parsing

#[derive(PartialEq, Debug)]
enum Datum {
    Boolean(bool),
    Number(String),
    Char(char),
    String(String),
    Identifier(String),
    List(Vec<Datum>),
    ImproperList(Vec<Datum>),
    Vector(Vec<Datum>),
}

named!(
    file_to_data(&str) -> Vec<Datum>,
    delimited!(
        intertoken_space,
        many0!(terminated!(datum, intertoken_space)),
        eof!()
    )
);

#[test]
fn test_parse() {
    use nom::ErrorKind;

    assert_eq!(
        datum("376    "),
        IResult::Done("    ", Datum::Number("376".to_string()))
    );
    assert_eq!(datum("    376"), IResult::Error(ErrorKind::Alt));

    assert_eq!(file_to_data(""), IResult::Done("", vec![]));
    assert_eq!(file_to_data("\t"), IResult::Done("", vec![]));
    assert_eq!(
        file_to_data("    376"),
        IResult::Done("", vec![Datum::Number("376".to_string())])
    );
    assert_eq!(
        file_to_data("    376\n    212\n"),
        IResult::Done(
            "",
            vec![
                Datum::Number("376".to_string()),
                Datum::Number("212".to_string()),
            ],
        )
    );
    assert_eq!(
        file_to_data("    376\n    ;212\n"),
        IResult::Done("", vec![Datum::Number("376".to_string())])
    );
    assert_eq!(
        file_to_data(";; nothing to see here\n\n"),
        IResult::Done("", vec![])
    );
    assert_eq!(
        file_to_data("(1 2)"),
        IResult::Done(
            "",
            vec![
                Datum::List(vec![
                    Datum::Number("1".to_string()),
                    Datum::Number("2".to_string()),
                ]),
            ],
        )
    );
}

named!(
    datum(&str) -> Datum,
    alt!(simple_datum | compound_datum)
);

named!(
    simple_datum(&str) -> Datum,
    alt!(
        map!(boolean, Datum::Boolean)
      | map!(number, Datum::Number)
      | map!(character, Datum::Char)
      | map!(string, Datum::String)
      | map!(identifier, Datum::Identifier)
    )
);

named!(
    compound_datum(&str) -> Datum,
    alt!(list | vector)
);

named!(
    list(&str) -> Datum,
    alt!(
        preceded!(
            terminated!(open, intertoken_space),
            alt!(
                // Nonempty list or improper list.
                map!(
                    terminated!(
                        pair!(
                            // Normal list elements.
                            many1!(terminated!(datum, intertoken_space)),
                            // Optional improper ending.
                            opt!(
                                do_parse!(
                                    value!((), char!('.')) >>
                                    intertoken_space >>
                                    d: datum >>
                                    intertoken_space >>
                                    (d)
                                )
                            )
                        ),
                        close
                    ),
                    |(mut data, opt_improper)| {
                        match opt_improper {
                            Some(improper) => {
                                data.push(improper);
                                Datum::ImproperList(data)
                            }
                            None =>
                                Datum::List(data),
                        }
                    }
                )
              | map!(
                    close,
                    |()| Datum::List(vec![])
                )
            )
        )
      | do_parse!(
            prefix: alt!(
                value!("quote", char!('\''))
              | value!("quasiquote", char!('`'))
              | value!("unquote", char!(','))
              | value!("unquote-splicing", tag!(",@"))
            ) >>
            intertoken_space >>
            arg: datum >>
            (
                Datum::List(vec![
                    // allocation here is dumb; symbols should be reused, of course
                    Datum::Identifier(prefix.to_string()),
                    arg,
                ])
            )
        )
    )
);

named!(
    vector(&str) -> Datum,
    map!(
        delimited!(
            terminated!(open_vector, intertoken_space),
            many0!(terminated!(datum, intertoken_space)),
            close
        ),
        Datum::Vector
    )
);

#[test]
fn test_parse_vector() {
    use nom::ErrorKind;

    assert_eq!(vector("#()"), IResult::Done("", Datum::Vector(vec![])));
    assert_eq!(
        vector("#(a)"),
        IResult::Done("", Datum::Vector(vec![Datum::Identifier("a".to_string())]))
    );
    assert_eq!(vector("#(0 . 1)"), IResult::Error(ErrorKind::Char));
}

fn into_list<'h>(
    hs: &mut GcHeapSession<'h>,
    elements: Vec<Datum>,
    tail: Value<'h>,
) -> Result<Value<'h>, &'static str> {
    let mut list = tail;
    for d in elements.into_iter().rev() {
        let value = datum_to_value(hs, d)?;
        list = Value::Cons(hs.alloc(Pair {
            car: value,
            cdr: list,
        }));
    }
    Ok(list)
}

fn datum_to_value<'h>(hs: &mut GcHeapSession<'h>, datum: Datum) -> Result<Value<'h>, &'static str> {
    match datum {
        Datum::Boolean(b) => Ok(Value::Bool(b)),
        Datum::Number(s) => {
            if let Ok(i) = i32::from_str(&s) {
                Ok(Value::Int(i))
            } else {
                Err("unsupported number token")
            }
        }
        Datum::Char(c) => Ok(Value::Char(c)),
        Datum::String(s) => Ok(Value::ImmString(GcLeaf::new(InternedString::get(&s)))),
        Datum::Identifier(s) => Ok(Value::Symbol(GcLeaf::new(InternedString::get(&s)))),
        Datum::List(data) => into_list(hs, data, Value::Nil),
        Datum::ImproperList(mut data) => {
            let tail = data.pop()
                .expect("internal error: improper lists require a last value");
            let tail_val = datum_to_value(hs, tail)?;
            into_list(hs, data, tail_val)
        }
        Datum::Vector(data) => {
            let values: Result<Vec<Value<'h>>, &'static str> =
                data.into_iter().map(|d| datum_to_value(hs, d)).collect();
            Ok(Value::Vector(hs.alloc(values?)))
        }
    }
}

/// Top level entry point to s-expression parsing. Takes a source string and
/// returns a `Value` which can contain `Pair`s allocated in the GC heap.
pub fn parse<'h>(hs: &mut GcHeapSession<'h>, source: &str) -> Result<Vec<Value<'h>>, &'static str> {
    match file_to_data(source) {
        IResult::Error(_e) => Err("syntax error"),
        IResult::Incomplete(_e) => Err("incomplete input"),
        IResult::Done(leftovers, data) => {
            // The parser uses `eof!()` to ensure that on success, all input has been consumed.
            assert!(leftovers.is_empty(), "parser did not consume all input");

            Ok(data.into_iter()
                .map(|d| datum_to_value(hs, d))
                .collect::<Result<Vec<_>, _>>()?)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Grin Developers
//
// 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.

//! Synchronization of the local blockchain with the rest of the network. Used
//! either on a brand new node or when a node is late based on others' heads.
//! Always starts by downloading the header chain before asking either for full
//! blocks or a full UTXO set with related information.

/// How many block bodies to download in parallel
const MAX_BODY_DOWNLOADS: usize = 8;

use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Instant, Duration};

use core::core::hash::{Hash, Hashed};
use chain;
use p2p;
use types::Error;
use util::LOGGER;

#[derive(Debug)]
struct BlockDownload {
  hash: Hash,
  start_time: Instant,
  retries: u8,
}

/// Manages syncing the local chain with other peers. Needs both a head chain
/// and a full block chain to operate. First tries to advance the header
/// chain as much as possible, then downloads the full blocks by batches.
pub struct Syncer {
	chain: Arc<chain::Chain>,
	p2p: Arc<p2p::Server>,

	sync: Mutex<bool>,
	last_header_req: Mutex<Instant>,
	blocks_to_download: Mutex<Vec<Hash>>,
	blocks_downloading: Mutex<Vec<BlockDownload>>,
}

impl Syncer {
	pub fn new(chain_ref: Arc<chain::Chain>, p2p: Arc<p2p::Server>) -> Syncer {
		Syncer {
			chain: chain_ref,
			p2p: p2p,
			sync: Mutex::new(true),
			last_header_req: Mutex::new(Instant::now() - Duration::from_secs(2)),
			blocks_to_download: Mutex::new(vec![]),
			blocks_downloading: Mutex::new(vec![]),
		}
	}

	pub fn syncing(&self) -> bool {
		*self.sync.lock().unwrap()
	}

	/// Checks the local chain state, comparing it with our peers and triggers
	/// syncing if required.
	pub fn run(&self) -> Result<(), Error> {
		debug!(LOGGER, "Starting syncer.");
		let start = Instant::now();
		loop {
			let pc = self.p2p.peer_count();
			if pc > 3 {
				break;
			}
			if pc > 0 && (Instant::now() - start > Duration::from_secs(10)) {
				break;
			}
			thread::sleep(Duration::from_millis(200));
		}

		// check if we have missing full blocks for which we already have a header
		self.init_download()?;

		// main syncing loop, requests more headers and bodies periodically as long
		// as a peer with higher difficulty exists and we're not fully caught up
		info!(LOGGER, "Starting sync loop.");
		loop {
			let tip = self.chain.get_header_head()?;
			// TODO do something better (like trying to get more) if we lose peers
			let peer = self.p2p.most_work_peer().unwrap();
      debug!(LOGGER, "Sync: peer {} vs us {}", peer.info.total_difficulty, tip.total_difficulty);

			let more_headers = peer.info.total_difficulty > tip.total_difficulty;
			let more_bodies = {
				let blocks_to_download = self.blocks_to_download.lock().unwrap();
				let blocks_downloading = self.blocks_downloading.lock().unwrap();
				blocks_to_download.len() > 0 || blocks_downloading.len() > 0
			};

			{
				let last_header_req = self.last_header_req.lock().unwrap().clone();
				if more_headers || (Instant::now() - Duration::from_secs(30) > last_header_req) {
					self.request_headers()?;
				}
			}
			if more_bodies {
				self.request_bodies();
			}
			if !more_headers && !more_bodies {
				// TODO check we haven't been lied to on the total work
				let mut sync = self.sync.lock().unwrap();
				*sync = false;
				break;
			}

			thread::sleep(Duration::from_secs(2));
		}
		info!(LOGGER, "Sync done.");
		Ok(())
	}

	/// Checks the gap between the header chain and the full block chain and
	/// initializes the blocks_to_download structure with the missing full
	/// blocks
	fn init_download(&self) -> Result<(), Error> {
		// compare the header's head to the full one to see what we're missing
		let header_head = self.chain.get_header_head()?;
		let full_head = self.chain.head()?;
		let mut blocks_to_download = self.blocks_to_download.lock().unwrap();

		// go back the chain and insert for download all blocks we only have the
		// head for
		let mut prev_h = header_head.last_block_h;
		while prev_h != full_head.last_block_h {
			let header = self.chain.get_block_header(&prev_h)?;
			if header.height < full_head.height {
				break;
			}
			blocks_to_download.push(header.hash());
			prev_h = header.previous;
		}

		debug!(
			LOGGER,
			"Added {} full block hashes to download.",
			blocks_to_download.len()
		);
		Ok(())
	}

	/// Asks for the blocks we haven't downloaded yet and place them in the
	/// downloading structure.
	fn request_bodies(&self) {
		let mut blocks_to_download = self.blocks_to_download.lock().unwrap();
		let mut blocks_downloading = self.blocks_downloading.lock().unwrap();

    // retry blocks not downloading
    let now = Instant::now();
    for mut download in blocks_downloading.deref_mut() {
      let elapsed = (now - download.start_time).as_secs();
      if download.retries >= 8 {
        panic!("Failed to download required block {}", download.hash);
      }
      if download.retries < (elapsed / 5) as u8 {
        debug!(LOGGER, "Retry {} on block {}", download.retries, download.hash);
        self.request_block(download.hash);
        download.retries += 1;
      }
    }

		// consume hashes from blocks to download, place them in downloading and
		// request them from the network
    let mut count = 0;
		while blocks_to_download.len() > 0 && blocks_downloading.len() < MAX_BODY_DOWNLOADS {
			let h = blocks_to_download.pop().unwrap();
      self.request_block(h);
      count += 1;
			blocks_downloading.push(
        BlockDownload {
          hash: h,
          start_time: Instant::now(),
          retries: 0
        });
 		}
 		debug!(
 			LOGGER,
			"Requested {} full blocks to download, total left: {}. Current list: {:?}.",
      count,
			blocks_to_download.len(),
      blocks_downloading.deref(),
			);
	}

	/// We added a block, clean up the downloading structure
	pub fn block_received(&self, bh: Hash) {
		// just clean up the downloading list
		let mut bds = self.blocks_downloading.lock().unwrap();
		bds.iter().position(|ref h| h.hash == bh).map(|n| bds.remove(n));
	}

	/// Request some block headers from a peer to advance us
	fn request_headers(&self) -> Result<(), Error> {
		{
			let mut last_header_req = self.last_header_req.lock().unwrap();
			*last_header_req = Instant::now();
		}

		let tip = self.chain.get_header_head()?;
		let peer = self.p2p.most_work_peer();
		let locator = self.get_locator(&tip)?;
		if let Some(p) = peer {
			debug!(
				LOGGER,
				"Asking peer {} for more block headers starting from {} at {}.",
				p.info.addr,
				tip.last_block_h,
				tip.height
			);
			p.send_header_request(locator)?;
		} else {
			warn!(LOGGER, "Could not get most worked peer to request headers.");
		}
		Ok(())
	}

	/// We added a header, add it to the full block download list
	pub fn headers_received(&self, bhs: Vec<Hash>) {
		let mut blocks_to_download = self.blocks_to_download.lock().unwrap();
		let hs_len = bhs.len();
		for h in bhs {
			// enlist for full block download
			blocks_to_download.insert(0, h);
		}
		// ask for more headers if we got as many as required
		if hs_len == (p2p::MAX_BLOCK_HEADERS as usize) {
			self.request_headers().unwrap();
		}
	}

	/// Builds a vector of block hashes that should help the remote peer sending
	/// us the right block headers.
	fn get_locator(&self, tip: &chain::Tip) -> Result<Vec<Hash>, Error> {
		// Prepare the heights we want as the latests height minus increasing powers
		// of 2 up to max.
		let mut heights = vec![tip.height];
		let mut tail = (1..p2p::MAX_LOCATORS)
			.map(|n| 2u64.pow(n))
			.filter_map(|n| if n > tip.height {
				None
			} else {
				Some(tip.height - n)
			})
			.collect::<Vec<_>>();
		heights.append(&mut tail);
    debug!(LOGGER, "Loc heights: {:?}", heights);

		// Iteratively travel the header chain back from our head and retain the
		// headers at the wanted heights.
		let mut header = self.chain.get_block_header(&tip.last_block_h)?;
		let mut locator = vec![];
		while heights.len() > 0 {
			if header.height == heights[0] {
				heights = heights[1..].to_vec();
				locator.push(header.hash());
			}
			if header.height > 0 {
				header = self.chain.get_block_header(&header.previous)?;
			}
		}
		Ok(locator)
	}

  /// Pick a random peer and ask for a block by hash
  fn request_block(&self, h: Hash) {
    let peer = self.p2p.random_peer().unwrap();
    let send_result = peer.send_block_request(h);
    if let Err(e) = send_result {
      debug!(LOGGER, "Error requesting block: {:?}", e);
    }
  }

}
<commit_msg>Spurious mut, different lint level on CI<commit_after>// Copyright 2016 The Grin Developers
//
// 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.

//! Synchronization of the local blockchain with the rest of the network. Used
//! either on a brand new node or when a node is late based on others' heads.
//! Always starts by downloading the header chain before asking either for full
//! blocks or a full UTXO set with related information.

/// How many block bodies to download in parallel
const MAX_BODY_DOWNLOADS: usize = 8;

use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Instant, Duration};

use core::core::hash::{Hash, Hashed};
use chain;
use p2p;
use types::Error;
use util::LOGGER;

#[derive(Debug)]
struct BlockDownload {
  hash: Hash,
  start_time: Instant,
  retries: u8,
}

/// Manages syncing the local chain with other peers. Needs both a head chain
/// and a full block chain to operate. First tries to advance the header
/// chain as much as possible, then downloads the full blocks by batches.
pub struct Syncer {
	chain: Arc<chain::Chain>,
	p2p: Arc<p2p::Server>,

	sync: Mutex<bool>,
	last_header_req: Mutex<Instant>,
	blocks_to_download: Mutex<Vec<Hash>>,
	blocks_downloading: Mutex<Vec<BlockDownload>>,
}

impl Syncer {
	pub fn new(chain_ref: Arc<chain::Chain>, p2p: Arc<p2p::Server>) -> Syncer {
		Syncer {
			chain: chain_ref,
			p2p: p2p,
			sync: Mutex::new(true),
			last_header_req: Mutex::new(Instant::now() - Duration::from_secs(2)),
			blocks_to_download: Mutex::new(vec![]),
			blocks_downloading: Mutex::new(vec![]),
		}
	}

	pub fn syncing(&self) -> bool {
		*self.sync.lock().unwrap()
	}

	/// Checks the local chain state, comparing it with our peers and triggers
	/// syncing if required.
	pub fn run(&self) -> Result<(), Error> {
		debug!(LOGGER, "Starting syncer.");
		let start = Instant::now();
		loop {
			let pc = self.p2p.peer_count();
			if pc > 3 {
				break;
			}
			if pc > 0 && (Instant::now() - start > Duration::from_secs(10)) {
				break;
			}
			thread::sleep(Duration::from_millis(200));
		}

		// check if we have missing full blocks for which we already have a header
		self.init_download()?;

		// main syncing loop, requests more headers and bodies periodically as long
		// as a peer with higher difficulty exists and we're not fully caught up
		info!(LOGGER, "Starting sync loop.");
		loop {
			let tip = self.chain.get_header_head()?;
			// TODO do something better (like trying to get more) if we lose peers
			let peer = self.p2p.most_work_peer().unwrap();
      debug!(LOGGER, "Sync: peer {} vs us {}", peer.info.total_difficulty, tip.total_difficulty);

			let more_headers = peer.info.total_difficulty > tip.total_difficulty;
			let more_bodies = {
				let blocks_to_download = self.blocks_to_download.lock().unwrap();
				let blocks_downloading = self.blocks_downloading.lock().unwrap();
				blocks_to_download.len() > 0 || blocks_downloading.len() > 0
			};

			{
				let last_header_req = self.last_header_req.lock().unwrap().clone();
				if more_headers || (Instant::now() - Duration::from_secs(30) > last_header_req) {
					self.request_headers()?;
				}
			}
			if more_bodies {
				self.request_bodies();
			}
			if !more_headers && !more_bodies {
				// TODO check we haven't been lied to on the total work
				let mut sync = self.sync.lock().unwrap();
				*sync = false;
				break;
			}

			thread::sleep(Duration::from_secs(2));
		}
		info!(LOGGER, "Sync done.");
		Ok(())
	}

	/// Checks the gap between the header chain and the full block chain and
	/// initializes the blocks_to_download structure with the missing full
	/// blocks
	fn init_download(&self) -> Result<(), Error> {
		// compare the header's head to the full one to see what we're missing
		let header_head = self.chain.get_header_head()?;
		let full_head = self.chain.head()?;
		let mut blocks_to_download = self.blocks_to_download.lock().unwrap();

		// go back the chain and insert for download all blocks we only have the
		// head for
		let mut prev_h = header_head.last_block_h;
		while prev_h != full_head.last_block_h {
			let header = self.chain.get_block_header(&prev_h)?;
			if header.height < full_head.height {
				break;
			}
			blocks_to_download.push(header.hash());
			prev_h = header.previous;
		}

		debug!(
			LOGGER,
			"Added {} full block hashes to download.",
			blocks_to_download.len()
		);
		Ok(())
	}

	/// Asks for the blocks we haven't downloaded yet and place them in the
	/// downloading structure.
	fn request_bodies(&self) {
		let mut blocks_to_download = self.blocks_to_download.lock().unwrap();
		let mut blocks_downloading = self.blocks_downloading.lock().unwrap();

    // retry blocks not downloading
    let now = Instant::now();
    for download in blocks_downloading.deref_mut() {
      let elapsed = (now - download.start_time).as_secs();
      if download.retries >= 8 {
        panic!("Failed to download required block {}", download.hash);
      }
      if download.retries < (elapsed / 5) as u8 {
        debug!(LOGGER, "Retry {} on block {}", download.retries, download.hash);
        self.request_block(download.hash);
        download.retries += 1;
      }
    }

		// consume hashes from blocks to download, place them in downloading and
		// request them from the network
    let mut count = 0;
		while blocks_to_download.len() > 0 && blocks_downloading.len() < MAX_BODY_DOWNLOADS {
			let h = blocks_to_download.pop().unwrap();
      self.request_block(h);
      count += 1;
			blocks_downloading.push(
        BlockDownload {
          hash: h,
          start_time: Instant::now(),
          retries: 0
        });
 		}
 		debug!(
 			LOGGER,
			"Requested {} full blocks to download, total left: {}. Current list: {:?}.",
      count,
			blocks_to_download.len(),
      blocks_downloading.deref(),
			);
	}

	/// We added a block, clean up the downloading structure
	pub fn block_received(&self, bh: Hash) {
		// just clean up the downloading list
		let mut bds = self.blocks_downloading.lock().unwrap();
		bds.iter().position(|ref h| h.hash == bh).map(|n| bds.remove(n));
	}

	/// Request some block headers from a peer to advance us
	fn request_headers(&self) -> Result<(), Error> {
		{
			let mut last_header_req = self.last_header_req.lock().unwrap();
			*last_header_req = Instant::now();
		}

		let tip = self.chain.get_header_head()?;
		let peer = self.p2p.most_work_peer();
		let locator = self.get_locator(&tip)?;
		if let Some(p) = peer {
			debug!(
				LOGGER,
				"Asking peer {} for more block headers starting from {} at {}.",
				p.info.addr,
				tip.last_block_h,
				tip.height
			);
			p.send_header_request(locator)?;
		} else {
			warn!(LOGGER, "Could not get most worked peer to request headers.");
		}
		Ok(())
	}

	/// We added a header, add it to the full block download list
	pub fn headers_received(&self, bhs: Vec<Hash>) {
		let mut blocks_to_download = self.blocks_to_download.lock().unwrap();
		let hs_len = bhs.len();
		for h in bhs {
			// enlist for full block download
			blocks_to_download.insert(0, h);
		}
		// ask for more headers if we got as many as required
		if hs_len == (p2p::MAX_BLOCK_HEADERS as usize) {
			self.request_headers().unwrap();
		}
	}

	/// Builds a vector of block hashes that should help the remote peer sending
	/// us the right block headers.
	fn get_locator(&self, tip: &chain::Tip) -> Result<Vec<Hash>, Error> {
		// Prepare the heights we want as the latests height minus increasing powers
		// of 2 up to max.
		let mut heights = vec![tip.height];
		let mut tail = (1..p2p::MAX_LOCATORS)
			.map(|n| 2u64.pow(n))
			.filter_map(|n| if n > tip.height {
				None
			} else {
				Some(tip.height - n)
			})
			.collect::<Vec<_>>();
		heights.append(&mut tail);
    debug!(LOGGER, "Loc heights: {:?}", heights);

		// Iteratively travel the header chain back from our head and retain the
		// headers at the wanted heights.
		let mut header = self.chain.get_block_header(&tip.last_block_h)?;
		let mut locator = vec![];
		while heights.len() > 0 {
			if header.height == heights[0] {
				heights = heights[1..].to_vec();
				locator.push(header.hash());
			}
			if header.height > 0 {
				header = self.chain.get_block_header(&header.previous)?;
			}
		}
		Ok(locator)
	}

  /// Pick a random peer and ask for a block by hash
  fn request_block(&self, h: Hash) {
    let peer = self.p2p.random_peer().unwrap();
    let send_result = peer.send_block_request(h);
    if let Err(e) = send_result {
      debug!(LOGGER, "Error requesting block: {:?}", e);
    }
  }

}
<|endoftext|>


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

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;

// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
#[dom_struct]
pub struct BluetoothRemoteGATTService {
    reflector_: Reflector,
    device: MutHeap<JS<BluetoothDevice>>,
    uuid: DOMString,
    isPrimary: bool,
    instanceID: String,
}

impl BluetoothRemoteGATTService {
    pub fn new_inherited(device: &BluetoothDevice,
                         uuid: DOMString,
                         isPrimary: bool,
                         instanceID: String)
                         -> BluetoothRemoteGATTService {
        BluetoothRemoteGATTService {
            reflector_: Reflector::new(),
            device: MutHeap::new(device),
            uuid: uuid,
            isPrimary: isPrimary,
            instanceID: instanceID,
        }
    }

    pub fn new(global: GlobalRef,
               device: &BluetoothDevice,
               uuid: DOMString,
               isPrimary: bool,
               instanceID: String)
               -> Root<BluetoothRemoteGATTService> {
        reflect_dom_object(box BluetoothRemoteGATTService::new_inherited(device,
                                                                         uuid,
                                                                         isPrimary,
                                                                         instanceID),
                           global,
                           BluetoothRemoteGATTServiceBinding::Wrap)
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
        let global_root = self.global();
        let global_ref = global_root.r();
        global_ref.as_window().bluetooth_thread()
    }

    pub fn get_instance_id(&self) -> String {
        self.instanceID.clone()
    }
}

impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-device
    fn Device(&self) -> Root<BluetoothDevice> {
        self.device.get()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-isprimary
    fn IsPrimary(&self) -> bool {
        self.isPrimary
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-uuid
    fn Uuid(&self) -> DOMString {
        self.uuid.clone()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
    fn GetCharacteristic(&self,
                         characteristic: BluetoothCharacteristicUUID)
                         -> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
        let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string();
        if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
            return Err(Security)
        }
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristic = receiver.recv().unwrap();
        match characteristic {
            Ok(characteristic) => {
                let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                        characteristic.broadcast,
                                                                        characteristic.read,
                                                                        characteristic.write_without_response,
                                                                        characteristic.write,
                                                                        characteristic.notify,
                                                                        characteristic.indicate,
                                                                        characteristic.authenticated_signed_writes,
                                                                        characteristic.reliable_write,
                                                                        characteristic.writable_auxiliaries);
                Ok(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                          self,
                                                          DOMString::from(characteristic.uuid),
                                                          &properties,
                                                          characteristic.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
    fn GetCharacteristics(&self,
                          characteristic: Option<BluetoothCharacteristicUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
        let mut uuid: Option<String> = None;
        if let Some(c) = characteristic {
            uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string());
            if let Some(ref uuid) = uuid {
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
            }
        };
        let mut characteristics = vec!();
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristics_vec = receiver.recv().unwrap();
        match characteristics_vec {
            Ok(characteristic_vec) => {
                for characteristic in characteristic_vec {
                    let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                            characteristic.broadcast,
                                                                            characteristic.read,
                                                                            characteristic.write_without_response,
                                                                            characteristic.write,
                                                                            characteristic.notify,
                                                                            characteristic.indicate,
                                                                            characteristic.authenticated_signed_writes,
                                                                            characteristic.reliable_write,
                                                                            characteristic.writable_auxiliaries);
                    characteristics.push(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                                                self,
                                                                                DOMString::from(characteristic.uuid),
                                                                                &properties,
                                                                                characteristic.instance_id));
                }
                Ok(characteristics)
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
    fn GetIncludedService(&self,
                          service: BluetoothServiceUUID)
                          -> Fallible<Root<BluetoothRemoteGATTService>> {
        let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string();
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedService(self.get_instance_id(),
                                                   uuid,
                                                   sender)).unwrap();
        let service = receiver.recv().unwrap();
        match service {
            Ok(service) => {
                Ok(BluetoothRemoteGATTService::new(self.global().r(),
                                                   &self.device.get(),
                                                   DOMString::from(service.uuid),
                                                   service.is_primary,
                                                   service.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
    fn GetIncludedServices(&self,
                          service: Option<BluetoothServiceUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
        let mut uuid: Option<String> = None;
        if let Some(s) = service {
            uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string())
        };
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedServices(self.get_instance_id(),
                                                    uuid,
                                                    sender)).unwrap();
        let services_vec = receiver.recv().unwrap();
        match services_vec {
            Ok(service_vec) => {
                Ok(service_vec.into_iter()
                              .map(|service| BluetoothRemoteGATTService::new(self.global().r(),
                                                                             &self.device.get(),
                                                                             DOMString::from(service.uuid),
                                                                             service.is_primary,
                                                                             service.instance_id))
                              .collect())
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }
}
<commit_msg>Auto merge of #11652 - szeged:blacklistfix, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;

// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
#[dom_struct]
pub struct BluetoothRemoteGATTService {
    reflector_: Reflector,
    device: MutHeap<JS<BluetoothDevice>>,
    uuid: DOMString,
    isPrimary: bool,
    instanceID: String,
}

impl BluetoothRemoteGATTService {
    pub fn new_inherited(device: &BluetoothDevice,
                         uuid: DOMString,
                         isPrimary: bool,
                         instanceID: String)
                         -> BluetoothRemoteGATTService {
        BluetoothRemoteGATTService {
            reflector_: Reflector::new(),
            device: MutHeap::new(device),
            uuid: uuid,
            isPrimary: isPrimary,
            instanceID: instanceID,
        }
    }

    pub fn new(global: GlobalRef,
               device: &BluetoothDevice,
               uuid: DOMString,
               isPrimary: bool,
               instanceID: String)
               -> Root<BluetoothRemoteGATTService> {
        reflect_dom_object(box BluetoothRemoteGATTService::new_inherited(device,
                                                                         uuid,
                                                                         isPrimary,
                                                                         instanceID),
                           global,
                           BluetoothRemoteGATTServiceBinding::Wrap)
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
        let global_root = self.global();
        let global_ref = global_root.r();
        global_ref.as_window().bluetooth_thread()
    }

    pub fn get_instance_id(&self) -> String {
        self.instanceID.clone()
    }
}

impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-device
    fn Device(&self) -> Root<BluetoothDevice> {
        self.device.get()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-isprimary
    fn IsPrimary(&self) -> bool {
        self.isPrimary
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-uuid
    fn Uuid(&self) -> DOMString {
        self.uuid.clone()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
    fn GetCharacteristic(&self,
                         characteristic: BluetoothCharacteristicUUID)
                         -> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
        let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string();
        if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
            return Err(Security)
        }
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristic = receiver.recv().unwrap();
        match characteristic {
            Ok(characteristic) => {
                let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                        characteristic.broadcast,
                                                                        characteristic.read,
                                                                        characteristic.write_without_response,
                                                                        characteristic.write,
                                                                        characteristic.notify,
                                                                        characteristic.indicate,
                                                                        characteristic.authenticated_signed_writes,
                                                                        characteristic.reliable_write,
                                                                        characteristic.writable_auxiliaries);
                Ok(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                          self,
                                                          DOMString::from(characteristic.uuid),
                                                          &properties,
                                                          characteristic.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
    fn GetCharacteristics(&self,
                          characteristic: Option<BluetoothCharacteristicUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
        let mut uuid: Option<String> = None;
        if let Some(c) = characteristic {
            uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string());
            if let Some(ref uuid) = uuid {
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
            }
        };
        let mut characteristics = vec!();
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristics_vec = receiver.recv().unwrap();
        match characteristics_vec {
            Ok(characteristic_vec) => {
                for characteristic in characteristic_vec {
                    let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                            characteristic.broadcast,
                                                                            characteristic.read,
                                                                            characteristic.write_without_response,
                                                                            characteristic.write,
                                                                            characteristic.notify,
                                                                            characteristic.indicate,
                                                                            characteristic.authenticated_signed_writes,
                                                                            characteristic.reliable_write,
                                                                            characteristic.writable_auxiliaries);
                    characteristics.push(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                                                self,
                                                                                DOMString::from(characteristic.uuid),
                                                                                &properties,
                                                                                characteristic.instance_id));
                }
                Ok(characteristics)
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
    fn GetIncludedService(&self,
                          service: BluetoothServiceUUID)
                          -> Fallible<Root<BluetoothRemoteGATTService>> {
        let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string();
        if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
            return Err(Security)
        }
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedService(self.get_instance_id(),
                                                   uuid,
                                                   sender)).unwrap();
        let service = receiver.recv().unwrap();
        match service {
            Ok(service) => {
                Ok(BluetoothRemoteGATTService::new(self.global().r(),
                                                   &self.device.get(),
                                                   DOMString::from(service.uuid),
                                                   service.is_primary,
                                                   service.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
    fn GetIncludedServices(&self,
                          service: Option<BluetoothServiceUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
        let mut uuid: Option<String> = None;
        if let Some(s) = service {
            uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string());
            if let Some(ref uuid) = uuid {
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
            }
        };
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedServices(self.get_instance_id(),
                                                    uuid,
                                                    sender)).unwrap();
        let services_vec = receiver.recv().unwrap();
        match services_vec {
            Ok(service_vec) => {
                Ok(service_vec.into_iter()
                              .map(|service| BluetoothRemoteGATTService::new(self.global().r(),
                                                                             &self.device.get(),
                                                                             DOMString::from(service.uuid),
                                                                             service.is_primary,
                                                                             service.instance_id))
                              .collect())
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }
}
<|endoftext|>


<commit_before>/**
use the taylor series to caculate tan^-1 (x) where |x| <= 1, x >= 0
**/
fn taylor_atan (x: f64) -> f64 {
    let mut a: f64 = 0.0;
    let mut s: f64 = 1.0;
    let mut xpow: f64;

    // by definition
    if x == 0.0 {
        return 0.0;
    }

    // this is well known, and the taylor series we are using explictly assumes |x| < 1
    if x == 1.0 {
        return std::f64::consts::FRAC_PI_4;
    }

    for ii in range(1i, 60) {
        if ii % 2 == 0 {
            continue;
        }
        xpow = 1.0;
        for _ in range(0i, ii) {
            xpow *= x;
        }
        a += s * xpow / (ii as f64);
        s = -1.0*s;
    }

    return a;
}

fn generate_table () {

    let pi: f64 = std::f64::consts::PI;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = taylor_atan(ipow) * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        } else {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        }
    }

    println!("];");
}

fn sin(theta: f64) -> f64 {

    let num_bits: uint = 32;

    let k1: f64 = 0.6072529350088812561694; // 1/k
    let mul: f64 = (1i << (num_bits-2)) as f64;
    let cordic_k1: f64 = k1 * mul;
    let mut x: int = cordic_k1.floor() as int; //0x26DD3B6A;
    let mut y: int = 0;
    let mut z: int = (mul * theta).floor() as int;
    let mut tx: int;
    let mut ty: int;
    let mut tz: int;

    let mut d: int;

    let cordic_tab = [
        0x3243f6a8, 0x1dac6705, 0xfadbafc, 0x7f56ea6,
        0x3feab76, 0x1ffd55b, 0xfffaaa, 0x7fff55,
        0x3fffea, 0x1ffffd, 0xfffff, 0x7ffff,
        0x3ffff, 0x1ffff, 0xffff, 0x7fff,
        0x3fff, 0x1fff, 0xfff, 0x7ff,
        0x3ff, 0x1ff, 0xff, 0x7f,
        0x3f, 0x1f, 0xf, 0x8,
        0x4, 0x2, 0x1, 0x0, ];

    for k in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }

        tx = x - (((y>>k) ^ d) - d);
        ty = y + (((x>>k) ^ d) - d);
        tz = z - ((cordic_tab[k] ^ d) - d);
        x = tx; y = ty; z = tz;
    }

    let s: f64 = (y as f64)/(mul as f64);

    return s;

}

fn main() {
    generate_table();

    let frac_pi_2: f64 = std::f64::consts::FRAC_PI_2; //pi/2.0;

    for ii in range(0i, 50) {
        let cur: f64 = ii as f64;
        let theta: f64 = cur/50.0 * frac_pi_2;
        let s: f64 = sin(theta);
            println!("sin({}) ~= {}", theta, s);
    }
}
<commit_msg>better comment<commit_after>/**
use the taylor series to caculate tan^-1 (x) where |x| <= 1, x >= 0
**/
fn taylor_atan (x: f64) -> f64 {
    let mut a: f64 = 0.0;
    let mut s: f64 = 1.0;
    let mut xpow: f64;

    // by definition
    if x == 0.0 {
        return 0.0;
    }

    // this is well known, and the taylor series we are using explictly assumes |x| < 1
    if x == 1.0 {
        return std::f64::consts::FRAC_PI_4;
    }

    for ii in range(1i, 60) {
        if ii % 2 == 0 {
            continue;
        }
        xpow = 1.0;
        for _ in range(0i, ii) {
            xpow *= x;
        }
        a += s * xpow / (ii as f64);
        s = -1.0*s;
    }

    return a;
}

fn generate_table () {

    let pi: f64 = std::f64::consts::PI;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = taylor_atan(ipow) * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        } else {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        }
    }

    println!("];");
}

fn sin(theta: f64) -> f64 {

    let num_bits: uint = 32;

    let k1: f64 = 0.6072529350088812561694; // 1/k
    let mul: f64 = (1i << (num_bits-2)) as f64;
    let cordic_k1: f64 = k1 * mul;
    let mut x: int = cordic_k1.floor() as int; //0x26DD3B6A;
    let mut y: int = 0;
    let mut z: int = (mul * theta).floor() as int;
    let mut tx: int;
    let mut ty: int;
    let mut tz: int;

    let mut d: int;

    // this table is generated by generate_table()
    let cordic_tab = [
        0x3243f6a8, 0x1dac6705, 0xfadbafc, 0x7f56ea6,
        0x3feab76, 0x1ffd55b, 0xfffaaa, 0x7fff55,
        0x3fffea, 0x1ffffd, 0xfffff, 0x7ffff,
        0x3ffff, 0x1ffff, 0xffff, 0x7fff,
        0x3fff, 0x1fff, 0xfff, 0x7ff,
        0x3ff, 0x1ff, 0xff, 0x7f,
        0x3f, 0x1f, 0xf, 0x8,
        0x4, 0x2, 0x1, 0x0, ];

    for k in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }

        tx = x - (((y>>k) ^ d) - d);
        ty = y + (((x>>k) ^ d) - d);
        tz = z - ((cordic_tab[k] ^ d) - d);
        x = tx; y = ty; z = tz;
    }

    let s: f64 = (y as f64)/(mul as f64);

    return s;

}

fn main() {
    generate_table();

    let frac_pi_2: f64 = std::f64::consts::FRAC_PI_2; //pi/2.0;

    for ii in range(0i, 50) {
        let cur: f64 = ii as f64;
        let theta: f64 = cur/50.0 * frac_pi_2;
        let s: f64 = sin(theta);
            println!("sin({}) ~= {}", theta, s);
    }
}
<|endoftext|>