<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! General memory manipulation utilities, such as arenas.

#![allow(unsafe_code)]

use core::cell::Cell;
use core::marker::PhantomData;
use core::mem;
use core::ptr::NonNull;
use core::slice;

use static_assertions::assert_obj_safe;

use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::LayoutVerified;

use crate::mem::align_to;

/// An error indicating that an [`Arena`] has run out of allocatable
/// memory or that memory that is more aligned than is supported
/// was requested.
///
/// [`Arena`]: trait.Arena.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OutOfMemory;

/// Represents a re-usable allocation arena.
///
/// An arena can be used to dynamically allocate temporary byte buffers. The
/// `reset()` method can be used to reset the arena to its unallocated state,
/// once all allocated buffers have been rendered unreachable.
///
/// See [`BumpArena`] for an illustration of how the borrow checker is used to
/// do this safely.
///
/// # Safety
///
/// `alloc_aligned()` is required to return memory with certain size and
/// alignment guarantees. While it itself is a safe function, unsafe code
/// is permitted to rely on these guarantees.
///
/// [`BumpArena`]: struct.BumpArena.html
pub unsafe trait Arena {
    /// Allocates `len` bytes of `align`-aligned memory from this arena.
    ///
    /// This is a low-level function: prefer instead to use the helpers defined
    /// in [`ArenaExt`].
    ///
    /// This function may be called multiple times, and the returned slices
    /// will be disjoint.
    ///
    /// After reset is called, this function is permited to return
    /// previously-allocated memory gain, in a way that is visible to Rust.
    /// As such, "poisoned bits", such as the padding bits of Rust structs,
    /// should never be written to memory returned by this function.
    ///
    /// Calling `alloc(0, 1)` must never fail. Note that there is no
    /// requirement that calling `alloc(0, n)` will not return a subslice
    /// of previously returned memory.
    ///
    /// # Panics
    ///
    /// This function will panic if `align` is not a power of two.
    ///
    /// This function is permitted to panic under catastrophic failure
    /// conditions, such as completely running out of program memory.
    /// Implementations must advertize whether they panic.
    ///
    /// [`ArenaExt`]: trait.ArenaExt.html
    fn alloc_aligned(
        &self,
        len: usize,
        align: usize,
    ) -> Result<&mut [u8], OutOfMemory>;

    /// Resets this arena, essentially freeing all memory that was given out
    /// and allowing it to be allocated once more.
    ///
    /// Calling this function requires that all slices that were given out by
    /// this arena are unreachable. Hence, this function must take `self` by
    /// unique reference: this ensures that no one else is holding references
    /// to the memory inside.
    ///
    /// This function need not actually do anything. It is merely required that
    /// the following behavior holds:
    /// ```
    /// # use manticore::mem::*;
    /// # let mut data = [0; 128];
    /// # let mut arena = BumpArena::new(&mut data);
    /// // If the first alloc succeeds (regardless of the value of `len`), then
    /// // the subsequent alloc after resetting the arena must also succeed.
    /// assert!(arena.alloc_aligned(64, 1).is_ok());
    /// arena.reset();
    /// assert!(arena.alloc_aligned(64, 1).is_ok());
    /// ```
    fn reset(&mut self);
}

assert_obj_safe!(Arena);

/// Convenience functions for arenas, exposed as a trait.
///
/// Note that this trait is implemened for `&impl Arena`, which is the reason
/// for the slightly odd signature.
pub trait ArenaExt<'arena> {
    /// Allocates a value of type `T`.
    ///
    /// Because of the `reset()` function, it needs to be safe to transmute
    /// `T` back into bytes. As such, There is an additional `AsBytes` bound.
    /// To avoid having to mess around with destructors, we additionally
    /// require a `Copy` bound, though this may eventually be removed.
    ///
    /// # Panics
    ///
    /// This function is permitted to panic under catastrophic failure
    /// conditions, such as completely running out of program memory.
    /// Implementations must advertize whether they panic.
    fn alloc<T>(self) -> Result<&'arena mut T, OutOfMemory>
    where
        T: AsBytes + FromBytes + Copy;

    /// Allocates a slice with `n` elements.
    ///
    /// Because of the `reset()` function, it needs to be safe to transmute
    /// `T` back into bytes. As such, There is an additional `AsBytes` bound.
    /// To avoid having to mess around with destructors, we additionally
    /// require a `Copy` bound, though this may eventually be removed.
    ///
    /// # Panics
    ///
    /// This function is permitted to panic under catastrophic failure
    /// conditions, such as completely running out of program memory.
    /// Implementations must advertize whether they panic.
    fn alloc_slice<T>(self, n: usize) -> Result<&'arena mut [T], OutOfMemory>
    where
        T: AsBytes + FromBytes + Copy;
}

impl<'arena, A: Arena + ?Sized> ArenaExt<'arena> for &'arena A {
    fn alloc<T: AsBytes + FromBytes + Copy>(
        self,
    ) -> Result<&'arena mut T, OutOfMemory> {
        let bytes =
            self.alloc_aligned(mem::size_of::<T>(), mem::align_of::<T>())?;

        let lv = LayoutVerified::new(bytes)
            .expect("alloc_aligned() implemented incorrectly");
        Ok(lv.into_mut())
    }

    fn alloc_slice<T: AsBytes + FromBytes + Copy>(
        self,
        n: usize,
    ) -> Result<&'arena mut [T], OutOfMemory> {
        let bytes_requested =
            mem::size_of::<T>().checked_mul(n).ok_or(OutOfMemory)?;
        let bytes =
            self.alloc_aligned(bytes_requested, mem::align_of::<T>())?;

        let lv = LayoutVerified::new_slice(bytes)
            .expect("alloc_aligned() implemented incorrectly");
        Ok(lv.into_mut_slice())
    }
}

/// A bump-allocating [`Arena`] that is backed by a byte slice.
///
/// [`Arena`]: trait.Arena.html
///
/// # Examples
/// ```
/// # use manticore::mem::*;
/// let mut data = [0; 128];
/// let mut arena = BumpArena::new(&mut data);
///
/// let buf1 = arena.alloc::<[u8; 64]>()?;
/// let buf2 = arena.alloc_slice::<u8>(64)?;
/// assert_eq!(buf1.len(), 64);
/// assert!(arena.alloc_slice::<u8>(1).is_err());
///
/// arena.reset();
/// let buf3 = arena.alloc_slice::<u8>(64)?;
/// assert_eq!(buf3.len(), 64);
/// # Ok::<(), OutOfMemory>(())
/// ```
///
/// Note that `BumpArena`'s interface protects callers from mistakedly
/// resetting it while previously-allocated memory remains reachable. For
/// example:
/// ```compile_fail
/// # use manticore::mem::*;
/// let mut data = [0; 128];
/// let mut arena = BumpArena::new(&mut data);
///
/// let buf = arena.alloc_slice::<u8>(64)?;
/// arena.reset();
/// buf[0] = 42;  // Does not compile!
/// # Ok::<(), OutOfMemory>(())
/// ```
///
/// # Panics
///
/// `BumpArena::alloc_aligned()` will never panic.
pub struct BumpArena<'arena> {
    // Even though we do not store `slice`, the lifetime trapped in
    // `lifetime_phantom` ensures that the slice is unaccessible until
    // `self` goes out of scope.
    lifetime_phantom: PhantomData<&'arena mut ()>,
    // Invariant: this pointer points to an allocation, with lifetime 'arena,
    // of `buf_len` bytes at all times. In other words, it points to the first
    // byte of the buffer it was constructed from.
    buf_ptr: NonNull<u8>,
    buf_len: usize,
    // Invariant: cursor <= buf_len. This invariant is assumed when performing
    // unsafe operations.
    cursor: Cell<usize>,
}

impl<'arena> BumpArena<'arena> {
    /// Create a new `BumpArena` by taking ownership of `slice`.
    pub fn new(slice: &'arena mut [u8]) -> Self {
        // There is no way we could ever allocate this much memory. But we
        // include this assertion just in case.
        assert!(slice.len() < isize::MAX as usize);

        Self {
            lifetime_phantom: PhantomData,
            buf_ptr: NonNull::new(slice.as_mut_ptr())
                .expect("slice pointer can never be null"),
            buf_len: slice.len(),
            cursor: Cell::new(0),
        }
    }

    /// Allocates unaligned memory of the given length, moving the cursor
    /// forward as necessary.
    fn alloc_raw(&self, len: usize) -> Result<&mut [u8], OutOfMemory> {
        if len == 0 {
            return Ok(&mut []);
        }
        let cursor = self.cursor.get();
        let proposed_cursor = len.checked_add(cursor).ok_or(OutOfMemory)?;
        if proposed_cursor > self.buf_len {
            return Err(OutOfMemory);
        }

        self.cursor.set(proposed_cursor);
        // At this point, it is not possible for following calls to access
        // the range buf_ptr[cursor..proposed_cursor]. Moreover, the invariants
        // of BumpArena ensure that buf_ptr + cursor must land inside (or one
        // past) the allocation. Hence, the pointer arithmetic that follows is
        // safe, as is the cast to isize.
        let slice = unsafe {
            // SAFE: buf_len >= cursor, and buf_len < isize::MAX; therefore,
            // this cast always produces a positive offset.
            let offset_ptr = self.buf_ptr.as_ptr().add(cursor);
            // SAFE: buf_ptr[cursor..proposed_cursor] is initialized.
            slice::from_raw_parts_mut(offset_ptr, len)
        };

        Ok(slice)
    }

    /// Aligns the internal buffer to the given alignment.
    ///
    /// # Panics
    ///
    /// `align` must be a power of two.
    fn align_to(&self, align: usize) -> Result<(), OutOfMemory> {
        assert!(align.is_power_of_two());

        // SAFE: see the safety notes in alloc_raw().
        let current_addr =
            unsafe { self.buf_ptr.as_ptr().add(self.cursor.get()) as usize };
        let aligned = align_to(current_addr, align);
        if aligned == usize::MAX && align > 1 {
            // We've hit the top of the address space, so we have no hope of
            // allocating aligned memory.
            return Err(OutOfMemory);
        }
        let misalignment = aligned - current_addr;

        self.alloc_raw(misalignment)?;
        Ok(())
    }
}

unsafe impl<'arena> Arena for BumpArena<'arena> {
    fn alloc_aligned(
        &self,
        len: usize,
        align: usize,
    ) -> Result<&mut [u8], OutOfMemory> {
        if len == 0 {
            assert!(align.is_power_of_two());
            // SAFE: zero-length slices have no restrictions on the pointer
            // beyond non-null-ness and well-aligned-ness, so we materialize
            // one out of thin air.
            //
            // NonNull::dangling() is optimial, but that required having a
            // concrete type.
            return Ok(unsafe {
                slice::from_raw_parts_mut(align as *mut u8, 0)
            });
        }

        self.align_to(align)?;
        self.alloc_raw(len)
    }

    // NOTE: because this function takes `self` by unique reference, no mutable
    // slices returned by `alloc()` could have survied, since that would require
    // us to hold a reference to `self`.
    //
    // Thus, followup calls to alloc cannot create aliases, since there are no
    // outstanding poitners to alias.
    fn reset(&mut self) {
        self.cursor.set(0)
    }
}

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

    #[test]
    fn complex_alignments() {
        let mut data = [0; 128];
        let arena = BumpArena::new(&mut data);

        let buf = arena.alloc_aligned(1, 1).unwrap();
        assert_eq!(buf.len(), 1);

        let buf = arena.alloc_aligned(1, 4).unwrap();
        assert_eq!(buf.len(), 1);
        assert_eq!(buf.as_ptr() as usize % 4, 0);

        let buf = arena.alloc_aligned(0, 4).unwrap();
        assert_eq!(buf.len(), 0);
        assert_eq!(buf.as_ptr() as usize % 4, 0);
    }
}
<commit_msg>[mem] Add a "trivial" arena, mostly for testing<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! General memory manipulation utilities, such as arenas.

#![allow(unsafe_code)]

use core::cell::Cell;
use core::marker::PhantomData;
use core::mem;
use core::ptr::NonNull;
use core::slice;

use static_assertions::assert_obj_safe;

use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::LayoutVerified;

use crate::mem::align_to;

/// An error indicating that an [`Arena`] has run out of allocatable
/// memory or that memory that is more aligned than is supported
/// was requested.
///
/// Additionally, `OutOfMemory` implements [`Arena`] itself, acting as the
/// "trivial" no-memory-was-provided arena. This is particularly useful for
/// when it is known that no memory will ever be allocated on the arena.
///
/// [`Arena`]: trait.Arena.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OutOfMemory;

unsafe impl Arena for OutOfMemory {
    fn alloc_aligned(
        &self,
        len: usize,
        align: usize,
    ) -> Result<&mut [u8], OutOfMemory> {
        assert!(align.is_power_of_two());
        if len == 0 {
            // SAFE: zero-length slices have no restrictions on the pointer
            // beyond non-null-ness and well-aligned-ness, so we materialize
            // one out of thin air.
            //
            // NonNull::dangling() is optimial, but that required having a
            // concrete type. See the note on zero-length slices in
            // https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html
            Ok(unsafe { slice::from_raw_parts_mut(align as *mut u8, 0) })
        } else {
            Err(*self)
        }
    }

    fn reset(&mut self) {}
}

/// Represents a re-usable allocation arena.
///
/// An arena can be used to dynamically allocate temporary byte buffers. The
/// `reset()` method can be used to reset the arena to its unallocated state,
/// once all allocated buffers have been rendered unreachable.
///
/// See [`BumpArena`] for an illustration of how the borrow checker is used to
/// do this safely.
///
/// # Safety
///
/// `alloc_aligned()` is required to return memory with certain size and
/// alignment guarantees. While it itself is a safe function, unsafe code
/// is permitted to rely on these guarantees.
///
/// [`BumpArena`]: struct.BumpArena.html
pub unsafe trait Arena {
    /// Allocates `len` bytes of `align`-a`igned memory from this arena.
    ///
    /// This is a low-level function: prefer instead to use the helpers defined
    /// in [`ArenaExt`].
    ///
    /// This function may be called multiple times, and the returned slices
    /// will be disjoint.
    ///
    /// After reset is called, this function is permited to return
    /// previously-allocated memory gain, in a way that is visible to Rust.
    /// As such, "poisoned bits", such as the padding bits of Rust structs,
    /// should never be written to memory returned by this function.
    ///
    /// Calling `alloc(0, 1)` must never fail. Note that there is no
    /// requirement that calling `alloc(0, n)` will not return a subslice
    /// of previously returned memory.
    ///
    /// # Panics
    ///
    /// This function will panic if `align` is not a power of two.
    ///
    /// This function is permitted to panic under catastrophic failure
    /// conditions, such as completely running out of program memory.
    /// Implementations must advertize whether they panic.
    ///
    /// [`ArenaExt`]: trait.ArenaExt.html
    fn alloc_aligned(
        &self,
        len: usize,
        align: usize,
    ) -> Result<&mut [u8], OutOfMemory>;

    /// Resets this arena, essentially freeing all memory that was given out
    /// and allowing it to be allocated once more.
    ///
    /// Calling this function requires that all slices that were given out by
    /// this arena are unreachable. Hence, this function must take `self` by
    /// unique reference: this ensures that no one else is holding references
    /// to the memory inside.
    ///
    /// This function need not actually do anything. It is merely required that
    /// the following behavior holds:
    /// ```
    /// # use manticore::mem::*;
    /// # let mut data = [0; 128];
    /// # let mut arena = BumpArena::new(&mut data);
    /// // If the first alloc succeeds (regardless of the value of `len`), then
    /// // the subsequent alloc after resetting the arena must also succeed.
    /// assert!(arena.alloc_aligned(64, 1).is_ok());
    /// arena.reset();
    /// assert!(arena.alloc_aligned(64, 1).is_ok());
    /// ```
    fn reset(&mut self);
}

assert_obj_safe!(Arena);

/// Convenience functions for arenas, exposed as a trait.
///
/// Note that this trait is implemened for `&impl Arena`, which is the reason
/// for the slightly odd signature.
pub trait ArenaExt<'arena> {
    /// Allocates a value of type `T`.
    ///
    /// Because of the `reset()` function, it needs to be safe to transmute
    /// `T` back into bytes. As such, There is an additional `AsBytes` bound.
    /// To avoid having to mess around with destructors, we additionally
    /// require a `Copy` bound, though this may eventually be removed.
    ///
    /// # Panics
    ///
    /// This function is permitted to panic under catastrophic failure
    /// conditions, such as completely running out of program memory.
    /// Implementations must advertize whether they panic.
    fn alloc<T>(self) -> Result<&'arena mut T, OutOfMemory>
    where
        T: AsBytes + FromBytes + Copy;

    /// Allocates a slice with `n` elements.
    ///
    /// Because of the `reset()` function, it needs to be safe to transmute
    /// `T` back into bytes. As such, There is an additional `AsBytes` bound.
    /// To avoid having to mess around with destructors, we additionally
    /// require a `Copy` bound, though this may eventually be removed.
    ///
    /// # Panics
    ///
    /// This function is permitted to panic under catastrophic failure
    /// conditions, such as completely running out of program memory.
    /// Implementations must advertize whether they panic.
    fn alloc_slice<T>(self, n: usize) -> Result<&'arena mut [T], OutOfMemory>
    where
        T: AsBytes + FromBytes + Copy;
}

impl<'arena, A: Arena + ?Sized> ArenaExt<'arena> for &'arena A {
    fn alloc<T: AsBytes + FromBytes + Copy>(
        self,
    ) -> Result<&'arena mut T, OutOfMemory> {
        let bytes =
            self.alloc_aligned(mem::size_of::<T>(), mem::align_of::<T>())?;

        let lv = LayoutVerified::<_, T>::new(bytes)
            .expect("alloc_aligned() implemented incorrectly");
        Ok(lv.into_mut())
    }

    fn alloc_slice<T: AsBytes + FromBytes + Copy>(
        self,
        n: usize,
    ) -> Result<&'arena mut [T], OutOfMemory> {
        let bytes_requested =
            mem::size_of::<T>().checked_mul(n).ok_or(OutOfMemory)?;
        let bytes =
            self.alloc_aligned(bytes_requested, mem::align_of::<T>())?;

        let lv = LayoutVerified::<_, [T]>::new_slice(bytes)
            .expect("alloc_aligned() implemented incorrectly");
        Ok(lv.into_mut_slice())
    }
}

/// A bump-allocating [`Arena`] that is backed by a byte slice.
///
/// [`Arena`]: trait.Arena.html
///
/// # Examples
/// ```
/// # use manticore::mem::*;
/// let mut data = [0; 128];
/// let mut arena = BumpArena::new(&mut data);
///
/// let buf1 = arena.alloc::<[u8; 64]>()?;
/// let buf2 = arena.alloc_slice::<u8>(64)?;
/// assert_eq!(buf1.len(), 64);
/// assert!(arena.alloc_slice::<u8>(1).is_err());
///
/// arena.reset();
/// let buf3 = arena.alloc_slice::<u8>(64)?;
/// assert_eq!(buf3.len(), 64);
/// # Ok::<(), OutOfMemory>(())
/// ```
///
/// Note that `BumpArena`'s interface protects callers from mistakedly
/// resetting it while previously-allocated memory remains reachable. For
/// example:
/// ```compile_fail
/// # use manticore::mem::*;
/// let mut data = [0; 128];
/// let mut arena = BumpArena::new(&mut data);
///
/// let buf = arena.alloc_slice::<u8>(64)?;
/// arena.reset();
/// buf[0] = 42;  // Does not compile!
/// # Ok::<(), OutOfMemory>(())
/// ```
///
/// # Panics
///
/// `BumpArena::alloc_aligned()` will never panic.
pub struct BumpArena<'arena> {
    // Even though we do not store `slice`, the lifetime trapped in
    // `lifetime_phantom` ensures that the slice is unaccessible until
    // `self` goes out of scope.
    lifetime_phantom: PhantomData<&'arena mut ()>,
    // Invariant: this pointer points to an allocation, with lifetime 'arena,
    // of `buf_len` bytes at all times. In other words, it points to the first
    // byte of the buffer it was constructed from.
    buf_ptr: NonNull<u8>,
    buf_len: usize,
    // Invariant: cursor <= buf_len. This invariant is assumed when performing
    // unsafe operations.
    cursor: Cell<usize>,
}

impl<'arena> BumpArena<'arena> {
    /// Create a new `BumpArena` by taking ownership of `slice`.
    pub fn new(slice: &'arena mut [u8]) -> Self {
        // There is no way we could ever allocate this much memory. But we
        // include this assertion just in case.
        assert!(slice.len() < isize::MAX as usize);

        Self {
            lifetime_phantom: PhantomData,
            buf_ptr: NonNull::new(slice.as_mut_ptr())
                .expect("slice pointer can never be null"),
            buf_len: slice.len(),
            cursor: Cell::new(0),
        }
    }

    /// Allocates unaligned memory of the given length, moving the cursor
    /// forward as necessary.
    fn alloc_raw(&self, len: usize) -> Result<&mut [u8], OutOfMemory> {
        if len == 0 {
            return Ok(&mut []);
        }
        let cursor = self.cursor.get();
        let proposed_cursor = len.checked_add(cursor).ok_or(OutOfMemory)?;
        if proposed_cursor > self.buf_len {
            return Err(OutOfMemory);
        }

        self.cursor.set(proposed_cursor);
        // At this point, it is not possible for following calls to access
        // the range buf_ptr[cursor..proposed_cursor]. Moreover, the invariants
        // of BumpArena ensure that buf_ptr + cursor must land inside (or one
        // past) the allocation. Hence, the pointer arithmetic that follows is
        // safe, as is the cast to isize.
        let slice = unsafe {
            // SAFE: buf_len >= cursor, and buf_len < isize::MAX; therefore,
            // this cast always produces a positive offset.
            let offset_ptr = self.buf_ptr.as_ptr().add(cursor);
            // SAFE: buf_ptr[cursor..proposed_cursor] is initialized.
            slice::from_raw_parts_mut(offset_ptr, len)
        };

        Ok(slice)
    }

    /// Aligns the internal buffer to the given alignment.
    ///
    /// # Panics
    ///
    /// `align` must be a power of two.
    fn align_to(&self, align: usize) -> Result<(), OutOfMemory> {
        assert!(align.is_power_of_two());

        // SAFE: see the safety notes in alloc_raw().
        let current_addr =
            unsafe { self.buf_ptr.as_ptr().add(self.cursor.get()) as usize };
        let aligned = align_to(current_addr, align);
        if aligned == usize::MAX && align > 1 {
            // We've hit the top of the address space, so we have no hope of
            // allocating aligned memory.
            return Err(OutOfMemory);
        }
        let misalignment = aligned - current_addr;

        self.alloc_raw(misalignment)?;
        Ok(())
    }
}

unsafe impl<'arena> Arena for BumpArena<'arena> {
    fn alloc_aligned(
        &self,
        len: usize,
        align: usize,
    ) -> Result<&mut [u8], OutOfMemory> {
        if len == 0 {
            // Forward to OutOfMemory, which will always succeed on zero-length
            // allocations.
            return OutOfMemory.alloc_aligned(0, align);
        }

        self.align_to(align)?;
        self.alloc_raw(len)
    }

    // NOTE: because this function takes `self` by unique reference, no mutable
    // slices returned by `alloc()` could have survied, since that would require
    // us to hold a reference to `self`.
    //
    // Thus, followup calls to alloc cannot create aliases, since there are no
    // outstanding poitners to alias.
    fn reset(&mut self) {
        self.cursor.set(0)
    }
}

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

    #[test]
    fn complex_alignments() {
        let mut data = [0; 128];
        let arena = BumpArena::new(&mut data);

        let buf = arena.alloc_aligned(1, 1).unwrap();
        assert_eq!(buf.len(), 1);

        let buf = arena.alloc_aligned(1, 4).unwrap();
        assert_eq!(buf.len(), 1);
        assert_eq!(buf.as_ptr() as usize % 4, 0);

        let buf = arena.alloc_aligned(0, 4).unwrap();
        assert_eq!(buf.len(), 0);
        assert_eq!(buf.as_ptr() as usize % 4, 0);
    }
}
<|endoftext|>


<commit_before>enum BinaryTree<T> {
    Empty,
    NonEmpty(Box<TreeNode<T>>)
}

struct TreeNode<T> {
    element: T,
    left: BinaryTree<T>,
    right: BinaryTree<T>
}

impl<T: Ord> BinaryTree<T> {
    fn add(&mut self, value: T) {
        match *self {
            BinaryTree::Empty =>
                *self = BinaryTree::NonEmpty(Box::new(TreeNode {
                    element: value,
                    left: BinaryTree::Empty,
                    right: BinaryTree::Empty
                })),
            BinaryTree::NonEmpty(ref mut node) =>
                if value <= node.element {
                    node.left.add(value);
                } else {
                    node.right.add(value);
                }
        }
    }
}

fn main() {

}
<commit_msg>examples of using BinaryTree<commit_after>enum BinaryTree<T> {
    Empty,
    NonEmpty(Box<TreeNode<T>>)
}

struct TreeNode<T> {
    element: T,
    left: BinaryTree<T>,
    right: BinaryTree<T>
}

impl<T: Ord> BinaryTree<T> {
    fn add(&mut self, value: T) {
        match *self {
            BinaryTree::Empty =>
                *self = BinaryTree::NonEmpty(Box::new(TreeNode {
                    element: value,
                    left: BinaryTree::Empty,
                    right: BinaryTree::Empty
                })),
            BinaryTree::NonEmpty(ref mut node) =>
                if value <= node.element {
                    node.left.add(value);
                } else {
                    node.right.add(value);
                }
        }
    }
}

fn main() {
    use self::BinaryTree::*;

    let jupyter_tree = NonEmpty(Box::new(TreeNode {
        element: "jupiter",
        left: Empty,
        right: Empty,
    }));
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    if let Some(path) = env::args().nth(1) {
        let mut contents = String::new();
        match File::open(path) {
            Ok(mut file) => {
                match file.read_to_string(&mut contents) {
                    Ok(_) => {
                        match brainfuck::read(&contents) {
                            Ok(program) => {
                                let stdio = io::stdin();
                                let input = stdio.lock();
                                let output = io::stdout();
                                brainfuck::eval(program, input, output);
                            }
                            Err(msg) => println!("Read the file, but could not parse it: {}", msg),
                        }
                    }
                    Err(_) => println!("Found the file but failed to read it."),
                }
            }
            Err(_) => println!("Failed to open the file for reading."),
        };
    } else {
        println!("Please provide a path to some brainfuck source as the first argument.")
    }
}

mod brainfuck {
    use std::io::{Read, Write}
;

    #[derive(PartialEq)]
    #[derive(Debug)]
    pub enum Command {
        IncrementPointer,
        DecrementPointer,
        IncrementValue,
        DecrementValue,
        OutputValue,
        InputValue,
        ForwardsTo(usize),
        BackwardsTo(usize),
    }

    pub fn read(source: &str) -> Result<Vec<Command>, &'static str> {
        let mut jumps: Vec<usize> = vec![];
        let mut swaps: Vec<(usize, usize)> = vec![];
        let mut extra_close = false;
        let mut position: usize = 0;
        let mut program: Vec<Command> = source
            .chars()
            .filter_map(|token| {
                let command = match token {
                    '>' => Some(Command::IncrementPointer),
                    '<' => Some(Command::DecrementPointer),
                    '+' => Some(Command::IncrementValue),
                    '-' => Some(Command::DecrementValue),
                    '.' => Some(Command::OutputValue),
                    ',' => Some(Command::InputValue),
                    '[' => {
                        jumps.push(position);
                        Some(Command::BackwardsTo(position))
                    }
                    ']' => {
                        match jumps.pop() {
                            Some(jump) => {
                                swaps.push((jump, position));
                                Some(Command::ForwardsTo(position))
                            },
                            None => {
                                extra_close = true;
                                None
                            }
                        }
                    }
                    _ => None,
                };

                if command.is_some() {
                    position += 1
                }

                command
            })
            .collect();

        if jumps.len() > 0 {
            Err("Too many open braces")
        } else if extra_close {
            Err("Too many close braces")
        } else {
            for (a, b) in swaps {
                program.swap(a, b)
            }

            Ok(program)
        }
    }

    fn write_byte<W>(mut writer: W, byte: i32)
        where W: Write
    {
        write!(&mut writer, "{}", byte as u8 as char).expect("Unable to write");
    }

    fn read_byte<R>(reader: R) -> i32
        where R: Read
    {
        reader
            .bytes()
            .next()
            .and_then(|result| result.ok())
            .map(|byte| byte as i32)
            .unwrap_or(0)
    }

    pub fn eval<R, W>(program: Vec<Command>, mut reader: R, mut writer: W)
        where R: Read,
              W: Write
    {
        let mut memory: Vec<i32> = vec![0; 30000];
        let mut mem_pointer: usize = 0;
        let mut prog_pointer: usize = 0;
        let prog_len = program.len();

        while prog_pointer < prog_len {
            match program[prog_pointer] {
                Command::IncrementPointer => {
                    mem_pointer += 1;
                }
                Command::DecrementPointer => {
                    mem_pointer -= 1;
                }
                Command::IncrementValue => {
                    memory[mem_pointer] += 1;
                }
                Command::DecrementValue => {
                    memory[mem_pointer] -= 1;
                }
                Command::OutputValue => {
                    write_byte(&mut writer, memory[mem_pointer]);
                }
                Command::InputValue => {
                    memory[mem_pointer] = read_byte(&mut reader);
                }
                Command::ForwardsTo(next) => {
                    if memory[mem_pointer] == 0 {
                        prog_pointer = next;
                    }
                }
                Command::BackwardsTo(next) => {
                    if memory[mem_pointer] != 0 {
                        prog_pointer = next;
                    }
                }
            };

            prog_pointer += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use brainfuck::*;
    use brainfuck::Command::*;
    use std::io::Cursor;

    #[test]
    fn read_empty() {
        let source = "";
        let expected: Vec<Command> = vec![];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_simple() {
        let source = "+>-<.,<.-+>,";
        let expected = vec![IncrementValue,
                            IncrementPointer,
                            DecrementValue,
                            DecrementPointer,
                            OutputValue,
                            InputValue,
                            DecrementPointer,
                            OutputValue,
                            DecrementValue,
                            IncrementValue,
                            IncrementPointer,
                            InputValue];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_garbage() {
        let source = ",.+lol,hey.>there<";
        let expected = vec![InputValue,
                            OutputValue,
                            IncrementValue,
                            InputValue,
                            OutputValue,
                            IncrementPointer,
                            DecrementPointer];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_loop() {
        let source = "[->+<]";
        let expected = vec![ForwardsTo(5),
                            DecrementValue,
                            IncrementPointer,
                            IncrementValue,
                            DecrementPointer,
                            BackwardsTo(0)];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_too_many_opens() {
        let source = "[,.[,.>+-][./.[]]";
        let expected = "Too many open braces";

        match read(source) {
            Ok(_) => assert!(false),
            Err(actual) => assert_eq!(expected, actual),
        };
    }

    #[test]
    fn read_too_many_closes() {
        let source = "[,.[,.>+-][./.[]]]]]]";
        let expected = "Too many close braces";

        match read(source) {
            Ok(_) => assert!(false),
            Err(actual) => assert_eq!(expected, actual),
        };
    }

    #[test]
    fn read_nested_loops() {
        let source = "[[]]";
        let expected = vec![ForwardsTo(3),
                            ForwardsTo(2),
                            BackwardsTo(1),
                            BackwardsTo(0)];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_more_nested_loops() {
        let source = "[[[][]]]";
        let expected = vec![ForwardsTo(7),
                            ForwardsTo(6),
                            ForwardsTo(3),
                            BackwardsTo(2),
                            ForwardsTo(5),
                            BackwardsTo(4),
                            BackwardsTo(1),
                            BackwardsTo(0)];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn eval_empty() {
        let input = Cursor::new(&b""[..]);
        let mut output = Cursor::new(vec![]);

        match read("") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }

    #[test]
    fn eval_new_line() {
        let input = Cursor::new(&b""[..]);
        let mut output = Cursor::new(vec![]);

        match read("++++++++++.") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "\n";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }

    #[test]
    fn eval_cat() {
        let input = Cursor::new(&b"Hello, World!"[..]);
        let mut output = Cursor::new(vec![]);

        match read(",[.,]") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "Hello, World!";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }

    // #[test]
    // fn eval_hello_world() {
    //     let input = Cursor::new(&b""[..]);
    //     let mut output = Cursor::new(vec![]);

    //     match read("--------[>+>+++++>-->-->--->++++>------<<<<<<<-------]>.>---.>----..>-.>++++.>.>+++++++.<<<.+++.<.<-.>>>>+.") {
    //         Ok(program) => eval(program, input, &mut output),
    //         Err(_) => assert!(false),
    //     }

    //     let expected = "Hello, World!";
    //     let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

    //     assert_eq!(expected, actual);
    // }
}
<commit_msg>Everything is working in Rust! :D<commit_after>use std::env;
use std::io;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    if let Some(path) = env::args().nth(1) {
        let mut contents = String::new();
        match File::open(path) {
            Ok(mut file) => {
                match file.read_to_string(&mut contents) {
                    Ok(_) => {
                        match brainfuck::read(&contents) {
                            Ok(program) => {
                                let stdio = io::stdin();
                                let input = stdio.lock();
                                let output = io::stdout();
                                brainfuck::eval(program, input, output);
                            }
                            Err(msg) => println!("Read the file, but could not parse it: {}", msg),
                        }
                    }
                    Err(_) => println!("Found the file but failed to read it."),
                }
            }
            Err(_) => println!("Failed to open the file for reading."),
        };
    } else {
        println!("Please provide a path to some brainfuck source as the first argument.")
    }
}

mod brainfuck {
    use std::io::{Read, Write}
;

    #[derive(PartialEq)]
    #[derive(Debug)]
    pub enum Command {
        IncrementPointer,
        DecrementPointer,
        IncrementValue,
        DecrementValue,
        OutputValue,
        InputValue,
        ForwardsTo(usize),
        BackwardsTo(usize),
    }

    pub fn read(source: &str) -> Result<Vec<Command>, &'static str> {
        let mut jumps: Vec<usize> = vec![];
        let mut swaps: Vec<(usize, usize)> = vec![];
        let mut extra_close = false;
        let mut position: usize = 0;
        let mut program: Vec<Command> = source
            .chars()
            .filter_map(|token| {
                let command = match token {
                    '>' => Some(Command::IncrementPointer),
                    '<' => Some(Command::DecrementPointer),
                    '+' => Some(Command::IncrementValue),
                    '-' => Some(Command::DecrementValue),
                    '.' => Some(Command::OutputValue),
                    ',' => Some(Command::InputValue),
                    '[' => {
                        jumps.push(position);
                        Some(Command::BackwardsTo(position))
                    }
                    ']' => {
                        match jumps.pop() {
                            Some(jump) => {
                                swaps.push((jump, position));
                                Some(Command::ForwardsTo(position))
                            },
                            None => {
                                extra_close = true;
                                None
                            }
                        }
                    }
                    _ => None,
                };

                if command.is_some() {
                    position += 1
                }

                command
            })
            .collect();

        if jumps.len() > 0 {
            Err("Too many open braces")
        } else if extra_close {
            Err("Too many close braces")
        } else {
            for (a, b) in swaps {
                program.swap(a, b)
            }

            Ok(program)
        }
    }

    fn write_byte<W>(mut writer: W, byte: i32)
        where W: Write
    {
        write!(&mut writer, "{}", byte as u8 as char).expect("Unable to write");
    }

    fn read_byte<R>(reader: R) -> i32
        where R: Read
    {
        reader
            .bytes()
            .next()
            .and_then(|result| result.ok())
            .map(|byte| byte as i32)
            .unwrap_or(0)
    }

    pub fn eval<R, W>(program: Vec<Command>, mut reader: R, mut writer: W)
        where R: Read,
              W: Write
    {
        let mut memory: Vec<i32> = vec![0; 30000];
        let mut mem_pointer: usize = 0;
        let mut prog_pointer: usize = 0;
        let prog_len = program.len();

        while prog_pointer < prog_len {
            match program[prog_pointer] {
                Command::IncrementPointer => {
                    mem_pointer += 1;
                }
                Command::DecrementPointer => {
                    mem_pointer -= 1;
                }
                Command::IncrementValue => {
                    memory[mem_pointer] += 1;
                }
                Command::DecrementValue => {
                    memory[mem_pointer] -= 1;
                }
                Command::OutputValue => {
                    write_byte(&mut writer, memory[mem_pointer]);
                }
                Command::InputValue => {
                    memory[mem_pointer] = read_byte(&mut reader);
                }
                Command::ForwardsTo(next) => {
                    if memory[mem_pointer] == 0 {
                        prog_pointer = next;
                    }
                }
                Command::BackwardsTo(next) => {
                    if memory[mem_pointer] != 0 {
                        prog_pointer = next;
                    }
                }
            };

            prog_pointer += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use brainfuck::*;
    use brainfuck::Command::*;
    use std::io::Cursor;

    #[test]
    fn read_empty() {
        let source = "";
        let expected: Vec<Command> = vec![];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_simple() {
        let source = "+>-<.,<.-+>,";
        let expected = vec![IncrementValue,
                            IncrementPointer,
                            DecrementValue,
                            DecrementPointer,
                            OutputValue,
                            InputValue,
                            DecrementPointer,
                            OutputValue,
                            DecrementValue,
                            IncrementValue,
                            IncrementPointer,
                            InputValue];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_garbage() {
        let source = ",.+lol,hey.>there<";
        let expected = vec![InputValue,
                            OutputValue,
                            IncrementValue,
                            InputValue,
                            OutputValue,
                            IncrementPointer,
                            DecrementPointer];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_loop() {
        let source = "[->+<]";
        let expected = vec![ForwardsTo(5),
                            DecrementValue,
                            IncrementPointer,
                            IncrementValue,
                            DecrementPointer,
                            BackwardsTo(0)];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_too_many_opens() {
        let source = "[,.[,.>+-][./.[]]";
        let expected = "Too many open braces";

        match read(source) {
            Ok(_) => assert!(false),
            Err(actual) => assert_eq!(expected, actual),
        };
    }

    #[test]
    fn read_too_many_closes() {
        let source = "[,.[,.>+-][./.[]]]]]]";
        let expected = "Too many close braces";

        match read(source) {
            Ok(_) => assert!(false),
            Err(actual) => assert_eq!(expected, actual),
        };
    }

    #[test]
    fn read_nested_loops() {
        let source = "[[]]";
        let expected = vec![ForwardsTo(3),
                            ForwardsTo(2),
                            BackwardsTo(1),
                            BackwardsTo(0)];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn read_more_nested_loops() {
        let source = "[[[][]]]";
        let expected = vec![ForwardsTo(7),
                            ForwardsTo(6),
                            ForwardsTo(3),
                            BackwardsTo(2),
                            ForwardsTo(5),
                            BackwardsTo(4),
                            BackwardsTo(1),
                            BackwardsTo(0)];

        match read(source) {
            Ok(actual) => assert_eq!(expected, actual),
            Err(_) => assert!(false),
        };
    }

    #[test]
    fn eval_empty() {
        let input = Cursor::new(&b""[..]);
        let mut output = Cursor::new(vec![]);

        match read("") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }

    #[test]
    fn eval_new_line() {
        let input = Cursor::new(&b""[..]);
        let mut output = Cursor::new(vec![]);

        match read("++++++++++.") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "\n";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }

    #[test]
    fn eval_cat() {
        let input = Cursor::new(&b"Hello, World!"[..]);
        let mut output = Cursor::new(vec![]);

        match read(",[.,]") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "Hello, World!";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }

    #[test]
    fn eval_hello_world() {
        let input = Cursor::new(&b""[..]);
        let mut output = Cursor::new(vec![]);

        match read("++++++++[>++++[>++>+++>+++>+<<<<-]>+>->+>>+[<]<-]>>.>
>---.+++++++..+++.>.<<-.>.+++.------.--------.>+.>++.") {
            Ok(program) => eval(program, input, &mut output),
            Err(_) => assert!(false),
        }

        let expected = "Hello World!\n";
        let actual = String::from_utf8(output.into_inner()).expect("Not UTF-8");

        assert_eq!(expected, actual);
    }
}
<|endoftext|>


<commit_before>use core::ops::{Add, Mul, Sub};
use num_traits::MulAdd;

/// Performs linear interpolation between two values.
#[cfg(not(target_os = "emscripten"))]
#[inline]
pub(crate) fn linear<T>(a: T, b: T, x: T) -> T
where
    T: MulAdd<Output = T> + Sub<Output = T> + Copy,
{
    x.mul_add(b - a, a)
}

/// Performs linear interpolation between two values.
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn linear<T>(a: T, b: T, x: T) -> T
where
    T: MulAdd<Output = T> + Sub<Output = T> + Copy,
{
    (x * (b - a)) + a
}

/// Performs cubic interpolation between two values bound between two other
/// values.
///
/// - n0 - The value before the first value.
/// - n1 - The first value.
/// - n2 - The second value.
/// - n3 - The value after the second value.
/// - alpha - The alpha value.
///
/// The alpha value should range from 0.0 to 1.0. If the alpha value is
/// 0.0, this function returns _n1_. If the alpha value is 1.0, this
/// function returns _n2_.
#[inline]
pub(crate) fn cubic<T>(n0: T, n1: T, n2: T, n3: T, alpha: T) -> T
where
    T: Add<Output = T> + Mul<Output = T> + Sub<Output = T> + Copy,
{
    let p = (n3 - n2) - (n0 - n1);
    let q = (n0 - n1) - p;
    let r = n2 - n0;
    let s = n1;
    p * alpha * alpha * alpha + q * alpha * alpha + r * alpha + s
}
<commit_msg>fix math traits for interpolate on emscripten<commit_after>use core::ops::{Add, Mul, Sub};
use num_traits::MulAdd;

/// Performs linear interpolation between two values.
#[cfg(not(target_os = "emscripten"))]
#[inline]
pub(crate) fn linear<T>(a: T, b: T, x: T) -> T
where
    T: MulAdd<Output = T> + Sub<Output = T> + Copy,
{
    x.mul_add(b - a, a)
}

/// Performs linear interpolation between two values.
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn linear<T>(a: T, b: T, x: T) -> T
where
    T: Mul<Output = T> + Add<Output = T> + Sub<Output = T> + Copy,
{
    (x * (b - a)) + a
}

/// Performs cubic interpolation between two values bound between two other
/// values.
///
/// - n0 - The value before the first value.
/// - n1 - The first value.
/// - n2 - The second value.
/// - n3 - The value after the second value.
/// - alpha - The alpha value.
///
/// The alpha value should range from 0.0 to 1.0. If the alpha value is
/// 0.0, this function returns _n1_. If the alpha value is 1.0, this
/// function returns _n2_.
#[inline]
pub(crate) fn cubic<T>(n0: T, n1: T, n2: T, n3: T, alpha: T) -> T
where
    T: Add<Output = T> + Mul<Output = T> + Sub<Output = T> + Copy,
{
    let p = (n3 - n2) - (n0 - n1);
    let q = (n0 - n1) - p;
    let r = n2 - n0;
    let s = n1;
    p * alpha * alpha * alpha + q * alpha * alpha + r * alpha + s
}
<|endoftext|>


<commit_before>
#[allow(dead_code)]
pub fn multiples(upper_bound: i64) -> i64 {
    let range = 0..upper_bound;
    let divisible = range.filter( |x| x % 3 == 0 || x % 5 == 0);
    let mut sum = 0;
    for x in divisible {
        sum = sum + x;
    }
    sum
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn example_data() {
        assert_eq!(multiples(10), 23);
    }
}
<commit_msg>arbitrary bound<commit_after>
#[allow(dead_code)]
pub fn multiples(upper_bound: i64) -> i64 {
    let range = 0..upper_bound;
    let divisible = range.filter( |x| x % 3 == 0 || x % 5 == 0);
    let mut sum = 0;
    for x in divisible {
        sum = sum + x;
    }
    sum
}

#[cfg(test)]
mod tests {

        use super::*;

    #[test]
    fn example() {
        assert_eq!(multiples(10), 23);
    }

    #[test]
    fn solution() {
        assert_eq!(multiples(1000), 233168);
    }
}
<|endoftext|>


<commit_before>use regex::{Regex};
use std::str::{FromStr};

use super::super::metrics::*;

/// Reader that takes a log line string and returns any metrics found in it.
pub trait LogLineReader: Send + Sync {
    fn read(&self, &str) -> Vec<Metric>;
}

/// Reads metrics from log lines in the standard formats:
///
/// - Measures: `measure#metric=1.2`
/// - Counts: `count#metric=3`
pub struct StandardLogLineReader;

lazy_static! {
    static ref LOG_MEASURE_REGEX: Regex =
        Regex::new(r"measure#([a-zA-Z0-9._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_SAMPLE_REGEX: Regex =
        Regex::new(r"sample#([a-zA-Z0-9._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_COUNT_REGEX: Regex =
        Regex::new(r"count#([a-zA-Z0-9._]+)=(\d+)").unwrap();
}

impl LogLineReader for StandardLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        let mut metrics = vec![];

        // Look for counts
        for cap in LOG_COUNT_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = u64::from_str(cap.at(2).unwrap()) {
                metrics.push(Count(name.to_owned(), value))
            }
        }

        // Look for measures
        for cap in LOG_MEASURE_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = f64::from_str(cap.at(2).unwrap()) {
                metrics.push(Measure(name.to_owned(), value))
            }
        }

        // Look for samples
        for cap in LOG_SAMPLE_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = f64::from_str(cap.at(2).unwrap()) {
                metrics.push(Sample(name.to_owned(), value))
            }
        }

        metrics
    } // fn read
}

/// Reads Heroku's logging metrics.
pub struct HerokuLogLineReader;

lazy_static! {
    static ref DYNO_TYPE_REGEX: Regex =
        Regex::new(r"dyno=(\w+)").unwrap();

    static ref SERVICE_REGEX: Regex =
        Regex::new(r"service=(\d+)ms").unwrap();

    static ref STATUS_REGEX: Regex =
        Regex::new(r"status=(\d+)").unwrap();

    static ref HEROKU_HTTP_ERROR_CODE_REGEX: Regex =
        Regex::new(r"code=(H\d+)").unwrap();

    static ref HEROKU_RUNTIME_ERROR_CODE_REGEX: Regex =
        Regex::new(r"Error (R\d+)").unwrap();

    static ref LOAD_AVG_1M_REGEX: Regex =
        Regex::new(r"sample#load_avg_1m=([0-9.]+)").unwrap();

    static ref SOURCE_REGEX: Regex =
        Regex::new(r"source=(\w+).(\d+)").unwrap();
}

impl HerokuLogLineReader {
    /// Parses Heroku router status lines for the response service time (how
    /// long it took) and the HTTP response status.
    pub fn parse_status(line: &str) -> Option<Vec<Metric>> {
        let mut metrics: Vec<Metric> = vec![];

        let status = match STATUS_REGEX.captures(line) {
            Some(captures) => captures.at(1).unwrap(),
            None => return None,
        };
        let status = u16::from_str(status).unwrap();

        let service = SERVICE_REGEX.captures(line)
            .and_then(|c| c.at(1))
            .and_then(|s| u32::from_str(s).ok())
            .unwrap();

        let dyno_type = DYNO_TYPE_REGEX.captures(line).and_then(|c| c.at(1)).unwrap();

        let base = format!("dyno.{}", dyno_type);

        // Counting a 499 as a 500
        let is_500 = status >= 499 && status < 600;

        // Don't record timing for 499 and 5xx errors
        if !is_500 {
            let service_time = format!("{}.service_time", base);
            metrics.push(Measure(service_time.to_owned(), service as f64));
        }

        // Count the status
        metrics.push(Count(format!("{}.status.{}", base, status).to_owned(), 1));

        Some(metrics)
    }

    /// Parses Heroku warning and error codes like "Hxx" and "Rxx" where "xx" is a pair
    /// of numbers. See the [Heroku][] site for more details.
    ///
    /// [Heroku]: https://devcenter.heroku.com/articles/error-codes
    pub fn parse_heroku_code(line: &str) -> Option<Metric> {
        let code: &str;

        if let Some(http_code) = HEROKU_HTTP_ERROR_CODE_REGEX.captures(line).and_then(|c| c.at(1)) {
            code = http_code;

        } else if let Some(runtime_code) = HEROKU_RUNTIME_ERROR_CODE_REGEX.captures(line).and_then(|c| c.at(1)) {
            code = runtime_code;

        } else {
            return None
        }

        Some(Count(format!("heroku.error.{}", code), 1))
    }

    /// Parses the `sample#load_avg_1m=` metrics from Heroku logs.
    pub fn parse_load(line: &str) -> Option<Metric> {
        let load_avg_1m = match LOAD_AVG_1M_REGEX.captures(line) {
            Some(captures) => captures.at(1).and_then(|c| f64::from_str(c).ok()).unwrap(),
            None => return None,
        };

        let dyno_type = SOURCE_REGEX.captures(line).and_then(|c| c.at(1)).unwrap();

        Some(Measure(format!("dyno.{}.load_avg_1m", dyno_type), load_avg_1m))
    }
}

impl LogLineReader for HerokuLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        if !line.contains("heroku") { return vec![] }

        let mut metrics: Vec<Metric> = vec![];

        if let Some(statuses) = HerokuLogLineReader::parse_status(line)      { metrics.extend(statuses) }
        if let Some(code)     = HerokuLogLineReader::parse_heroku_code(line) { metrics.push(code) }
        if let Some(load)     = HerokuLogLineReader::parse_load(line)        { metrics.push(load) }

        metrics
    }
}

#[cfg(test)]
mod tests {
    use super::{
        LogLineReader,
        StandardLogLineReader,
        HerokuLogLineReader
    };
    use super::super::super::metrics::*;

    #[test]
    fn standard_reader_reads_measure() {
        let reader = StandardLogLineReader;
        let line = "measure#foo=1.2\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure("foo".to_owned(), 1.2) ]
        )
    }

    #[test]
    fn standard_reader_reads_count() {
        let reader = StandardLogLineReader;
        let line = "count#foo=1\n";

        assert_eq!(
            reader.read(line),
            vec![ Count("foo".to_owned(), 1) ]
        )
    }

    #[test]
    fn standard_reader_reads_sample() {
        let reader = StandardLogLineReader;
        let line = "sample#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![ Sample("bar".to_owned(), 3.4) ]
        )
    }

    #[test]
    fn standard_reader_returns_nothing_on_failed_read() {
        let reader = StandardLogLineReader;
        let line = "metric#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![]
        )
    }

    #[test]
    fn heroku_reader_reads_loads() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.429615+00:00 heroku web.2 - - source=web.2 dyno=heroku.123.XYZ sample#load_avg_1m=0.56 sample#load_avg_5m=0.26 sample#load_avg_15m=0.17\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure("dyno.web.load_avg_1m".to_owned(), 0.56) ]
        )
    }

    #[test]
    fn heroku_reader_reads_http_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:50:36.352129+00:00 heroku router - - sock=backend at=error code=H18 desc=\"Server Request Interrupted\" method=GET path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.5 connect=0ms service=495ms status=503 bytes=1648\n";

        assert_eq!(
            reader.read(line),
            vec![
                Count("dyno.web.status.503".to_owned(), 1),
                Count("heroku.error.H18".to_owned(), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_runtime_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-25 21:35:34.990292+00:00 heroku scheduler.5451 - - Error R14 (Memory quota exceeded)\n";

        assert_eq!(
            reader.read(line),
            vec![
                Count("heroku.error.R14".to_owned(), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_service_times() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.370813+00:00 heroku router - - at=info method=PUT path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.1 connect=0ms service=39ms status=200 bytes=1627\n";

        assert_eq!(
            reader.read(line),
            vec![
                Measure("dyno.web.service_time".to_owned(), 39.0),
                Count("dyno.web.status.200".to_owned(), 1),
            ]
        )
    }
}
<commit_msg>Make Heroku log line parsing more robust<commit_after>use regex::{Regex};
use std::str::{FromStr};

use super::super::metrics::*;

/// Reader that takes a log line string and returns any metrics found in it.
pub trait LogLineReader: Send + Sync {
    fn read(&self, &str) -> Vec<Metric>;
}

/// Reads metrics from log lines in the standard formats:
///
/// - Measures: `measure#metric=1.2`
/// - Counts: `count#metric=3`
pub struct StandardLogLineReader;

lazy_static! {
    static ref LOG_MEASURE_REGEX: Regex =
        Regex::new(r"measure#([a-zA-Z0-9._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_SAMPLE_REGEX: Regex =
        Regex::new(r"sample#([a-zA-Z0-9._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_COUNT_REGEX: Regex =
        Regex::new(r"count#([a-zA-Z0-9._]+)=(\d+)").unwrap();
}

impl LogLineReader for StandardLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        let mut metrics = vec![];

        // Look for counts
        for cap in LOG_COUNT_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = u64::from_str(cap.at(2).unwrap()) {
                metrics.push(Count(name.to_owned(), value))
            }
        }

        // Look for measures
        for cap in LOG_MEASURE_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = f64::from_str(cap.at(2).unwrap()) {
                metrics.push(Measure(name.to_owned(), value))
            }
        }

        // Look for samples
        for cap in LOG_SAMPLE_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = f64::from_str(cap.at(2).unwrap()) {
                metrics.push(Sample(name.to_owned(), value))
            }
        }

        metrics
    } // fn read
}

/// Reads Heroku's logging metrics.
pub struct HerokuLogLineReader;

lazy_static! {
    static ref DYNO_TYPE_REGEX: Regex =
        Regex::new(r"dyno=(\w+)").unwrap();

    static ref SERVICE_REGEX: Regex =
        Regex::new(r"service=(\d+)ms").unwrap();

    static ref STATUS_REGEX: Regex =
        Regex::new(r"status=(\d+)").unwrap();

    static ref HEROKU_HTTP_ERROR_CODE_REGEX: Regex =
        Regex::new(r"code=(H\d+)").unwrap();

    static ref HEROKU_RUNTIME_ERROR_CODE_REGEX: Regex =
        Regex::new(r"Error (R\d+)").unwrap();

    static ref LOAD_AVG_1M_REGEX: Regex =
        Regex::new(r"sample#load_avg_1m=([0-9.]+)").unwrap();

    static ref SOURCE_REGEX: Regex =
        Regex::new(r"source=(\w+).(\d+)").unwrap();
}

impl HerokuLogLineReader {
    /// Parses Heroku router status lines for the response service time (how
    /// long it took) and the HTTP response status.
    pub fn parse_status(line: &str) -> Option<Vec<Metric>> {
        let mut metrics: Vec<Metric> = vec![];

        let status = match STATUS_REGEX.captures(line) {
            Some(captures) => captures.at(1).unwrap(),
            None => return None,
        };
        let status = u16::from_str(status).unwrap();

        let service = match SERVICE_REGEX.captures(line)
            .and_then(|c| c.at(1))
            .and_then(|s| u32::from_str(s).ok()) {
                Some(s) => s,
                None => return None,
            };

        let dyno_type = match DYNO_TYPE_REGEX.captures(line)
            .and_then(|c| c.at(1)) {
                Some(d) => d,
                None => return None,
            };

        let base = format!("dyno.{}", dyno_type);

        // Counting a 499 as a 500
        let is_500 = status >= 499 && status < 600;

        // Don't record timing for 499 and 5xx errors
        if !is_500 {
            let service_time = format!("{}.service_time", base);
            metrics.push(Measure(service_time.to_owned(), service as f64));
        }

        // Count the status
        metrics.push(Count(format!("{}.status.{}", base, status).to_owned(), 1));

        Some(metrics)
    }

    /// Parses Heroku warning and error codes like "Hxx" and "Rxx" where "xx" is a pair
    /// of numbers. See the [Heroku][] site for more details.
    ///
    /// [Heroku]: https://devcenter.heroku.com/articles/error-codes
    pub fn parse_heroku_code(line: &str) -> Option<Metric> {
        let code: &str;

        if let Some(http_code) = HEROKU_HTTP_ERROR_CODE_REGEX.captures(line).and_then(|c| c.at(1)) {
            code = http_code;

        } else if let Some(runtime_code) = HEROKU_RUNTIME_ERROR_CODE_REGEX.captures(line).and_then(|c| c.at(1)) {
            code = runtime_code;

        } else {
            return None
        }

        Some(Count(format!("heroku.error.{}", code), 1))
    }

    /// Parses the `sample#load_avg_1m=` metrics from Heroku logs.
    pub fn parse_load(line: &str) -> Option<Metric> {
        let load_avg_1m = match LOAD_AVG_1M_REGEX.captures(line) {
            Some(captures) => captures.at(1).and_then(|c| f64::from_str(c).ok()).unwrap(),
            None => return None,
        };

        let dyno_type = SOURCE_REGEX.captures(line).and_then(|c| c.at(1)).unwrap();

        Some(Measure(format!("dyno.{}.load_avg_1m", dyno_type), load_avg_1m))
    }
}

impl LogLineReader for HerokuLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        if !line.contains("heroku") { return vec![] }

        let mut metrics: Vec<Metric> = vec![];

        if let Some(statuses) = HerokuLogLineReader::parse_status(line)      { metrics.extend(statuses) }
        if let Some(code)     = HerokuLogLineReader::parse_heroku_code(line) { metrics.push(code) }
        if let Some(load)     = HerokuLogLineReader::parse_load(line)        { metrics.push(load) }

        metrics
    }
}

#[cfg(test)]
mod tests {
    use super::{
        LogLineReader,
        StandardLogLineReader,
        HerokuLogLineReader
    };
    use super::super::super::metrics::*;

    #[test]
    fn standard_reader_reads_measure() {
        let reader = StandardLogLineReader;
        let line = "measure#foo=1.2\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure("foo".to_owned(), 1.2) ]
        )
    }

    #[test]
    fn standard_reader_reads_count() {
        let reader = StandardLogLineReader;
        let line = "count#foo=1\n";

        assert_eq!(
            reader.read(line),
            vec![ Count("foo".to_owned(), 1) ]
        )
    }

    #[test]
    fn standard_reader_reads_sample() {
        let reader = StandardLogLineReader;
        let line = "sample#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![ Sample("bar".to_owned(), 3.4) ]
        )
    }

    #[test]
    fn standard_reader_returns_nothing_on_failed_read() {
        let reader = StandardLogLineReader;
        let line = "metric#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![]
        )
    }

    #[test]
    fn heroku_reader_reads_loads() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.429615+00:00 heroku web.2 - - source=web.2 dyno=heroku.123.XYZ sample#load_avg_1m=0.56 sample#load_avg_5m=0.26 sample#load_avg_15m=0.17\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure("dyno.web.load_avg_1m".to_owned(), 0.56) ]
        )
    }

    #[test]
    fn heroku_reader_reads_http_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:50:36.352129+00:00 heroku router - - sock=backend at=error code=H18 desc=\"Server Request Interrupted\" method=GET path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.5 connect=0ms service=495ms status=503 bytes=1648\n";

        assert_eq!(
            reader.read(line),
            vec![
                Count("dyno.web.status.503".to_owned(), 1),
                Count("heroku.error.H18".to_owned(), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_runtime_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-25 21:35:34.990292+00:00 heroku scheduler.5451 - - Error R14 (Memory quota exceeded)\n";

        assert_eq!(
            reader.read(line),
            vec![
                Count("heroku.error.R14".to_owned(), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_service_times() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.370813+00:00 heroku router - - at=info method=PUT path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.1 connect=0ms service=39ms status=200 bytes=1627\n";

        assert_eq!(
            reader.read(line),
            vec![
                Measure("dyno.web.service_time".to_owned(), 39.0),
                Count("dyno.web.status.200".to_owned(), 1),
            ]
        )
    }
}
<|endoftext|>


<commit_before>pub fn format_fasta(id: &str, maybe_desc: Option<String>,
                    seq: &str, width: usize) -> String {
    let mut ret = ">".to_owned() + id;

    if let Some(desc) = maybe_desc {
        ret.push(' ');
        ret.push_str(&desc);
    }

    ret.push('\n');

    let mut count = 0;
    for c in seq.chars() {
        ret.push(c);
        if count % width == 0 {
            ret.push('\n');
        }
        count += 1;
    }

    if count % width != 0 {
        ret.push('\n');
    }

    ret
}

#[test]
fn test_format_fasta() {
    assert!(format_fasta("id1", None, "", 8) == ">id1\n");
    assert!(format_fasta("id2", Some("desc1".to_owned()), "atgc", 4) ==
            ">id2 desc1\natgc\n");
    assert!(format_fasta("id3", None, "atgc", 4) == "atgc");
    let input1 = "acgtacgattattaccggttacgcatccgtgtaaca";
    let expected1 = "acgtacga\nttattacc\nggttacgc\natccgtgt\naaca\n";
    assert!(format_fasta("id4", Some("desc4".to_owned()), input1, 8) == expected1);
    let input2 = "acgtacgattattaccggttacgcatccgtgt";
    let expected2 = "acgtacga\nttattacc\nggttacgc\natccgtgt\n";
    assert!(format_fasta("id5", None, input2, 8) == expected2);
}
<commit_msg>Fix formatting of fasta output<commit_after>pub fn format_fasta(id: &str, maybe_desc: Option<String>,
                    seq: &str, width: usize) -> String {
    let mut ret = ">".to_owned() + id;

    if let Some(desc) = maybe_desc {
        ret.push(' ');
        ret.push_str(&desc);
    }

    ret.push('\n');

    if seq.len() == 0 {
        ret.push('\n');
    } else {
        let mut count = 0;
        for c in seq.chars() {
            ret.push(c);
            count += 1;
            if count % width == 0 {
                ret.push('\n');
            }
        }

        if count % width != 0 {
            ret.push('\n');
        }
    }

    ret
}

#[test]
fn test_format_fasta() {
    assert!(format_fasta("id1", None, "", 8) == ">id1\n");
    assert!(format_fasta("id2", Some("desc1".to_owned()), "atgc", 4) ==
            ">id2 desc1\natgc\n");
    assert!(format_fasta("id3", None, "atgc", 4) == "atgc");
    let input1 = "acgtacgattattaccggttacgcatccgtgtaaca";
    let expected1 = "acgtacga\nttattacc\nggttacgc\natccgtgt\naaca\n";
    assert!(format_fasta("id4", Some("desc4".to_owned()), input1, 8) == expected1);
    let input2 = "acgtacgattattaccggttacgcatccgtgt";
    let expected2 = "acgtacga\nttattacc\nggttacgc\natccgtgt\n";
    assert!(format_fasta("id5", None, input2, 8) == expected2);
}
<|endoftext|>


<commit_before>use validation::common::{ValidationError, ValidationErrors};
use structure::model::ModelDocument;

#[derive(Debug)]
pub struct Validation {}

impl Validation {
    pub fn validate(model: &ModelDocument) -> ValidationErrors {
        let mut errors = Vec::<ValidationError>::new();

        errors.extend(Validation::all_locales_have_translation_docs(&model));
        errors.extend(Validation::all_translation_docs_have_locales(&model));
        errors.extend(Validation::all_page_xflow_references_point_to_existing_entities(&model));

        errors
    }

    pub fn all_locales_have_translation_docs(model: &ModelDocument) -> ValidationErrors {
        debug!("all_locales_have_translation_docs");
        let mut errors = Vec::<ValidationError>::new();

        for locale in &model.doc.config.doc.locales {
            if !model.has_translation(&locale) {
                errors.push(ValidationError {
                                code: 1,
                                message: format!("Model: locale '{:?}' has no translation document",
                                                 &locale),
                                paths: vec!["/config/locales".to_owned()],
                            });
            }
        }

        errors
    }

    pub fn all_translation_docs_have_locales(model: &ModelDocument) -> Vec<ValidationError> {
        debug!("all_translation_docs_have_locales");
        let mut errors = Vec::<ValidationError>::new();

        for t in &model.doc.translations {
            if !model.has_locale(&t.doc.locale) {
                errors.push(ValidationError {
                                code: 1,
                                message: format!("Model: translation doc exists with locale '{:?}' but it is not listed as a supported locale",
                                                 &t.doc.locale),
                                paths: vec!["/config/translations".to_owned()],
                            });
            }
        }

        errors
    }

    pub fn all_page_xflow_references_point_to_existing_entities(model: &ModelDocument)
                                                                -> Vec<ValidationError> {
        debug!("all_page_xflow_references_point_to_existing_entities");
        let mut errors = Vec::<ValidationError>::new();
        let xflow_ids = model.all_xflow_ids();

        for page in &model.doc.pages {
            for xflow_reference in &page.all_xflow_references() {
                if !xflow_ids.contains(xflow_reference) {
                    let message = format!("Page: Contains a reference to xflow id '{}', which does not exist in this model",
                                          xflow_reference);
                    errors.push(ValidationError {
                                    code: 1,
                                    message: message,
                                    paths: vec![format!("/pages/{id}", id = page.id)],
                                });
                }
            }
        }

        errors
    }
}
<commit_msg>feat: Additional validations for unique keys<commit_after>use validation::common::{ValidationError, ValidationErrors};
use structure::model::ModelDocument;

use std::collections::HashSet;

#[derive(Debug)]
pub struct Validation {}

impl Validation {
    pub fn validate(model: &ModelDocument) -> ValidationErrors {
        let mut errors = Vec::<ValidationError>::new();

        errors.extend(Validation::all_locales_have_translation_docs(&model));
        errors.extend(Validation::all_translation_docs_have_locales(&model));
        errors.extend(Validation::all_page_xflow_references_point_to_existing_entities(&model));
        errors.extend(Validation::all_doc_collections_have_correct_unique_keys(&model));

        errors
    }

    pub fn all_locales_have_translation_docs(model: &ModelDocument) -> ValidationErrors {
        debug!("all_locales_have_translation_docs");
        let mut errors = Vec::<ValidationError>::new();

        for locale in &model.doc.config.doc.locales {
            if !model.has_translation(&locale) {
                errors.push(ValidationError {
                                code: 1,
                                message: format!("Model: locale '{:?}' has no translation document",
                                                 &locale),
                                paths: vec!["/config/locales".to_owned()],
                            });
            }
        }

        errors
    }

    pub fn all_translation_docs_have_locales(model: &ModelDocument) -> Vec<ValidationError> {
        debug!("all_translation_docs_have_locales");
        let mut errors = Vec::<ValidationError>::new();

        for t in &model.doc.translations {
            if !model.has_locale(&t.doc.locale) {
                errors.push(ValidationError {
                                code: 1,
                                message: format!("Model: translation doc exists with locale '{:?}' but it is not listed as a supported locale",
                                                 &t.doc.locale),
                                paths: vec!["/config/translations".to_owned()],
                            });
            }
        }

        errors
    }

    pub fn all_page_xflow_references_point_to_existing_entities(model: &ModelDocument)
                                                                -> Vec<ValidationError> {
        debug!("all_page_xflow_references_point_to_existing_entities");
        let mut errors = Vec::<ValidationError>::new();
        let xflow_ids = model.all_xflow_ids();

        for page in &model.doc.pages {
            for xflow_reference in &page.all_xflow_references() {
                if !xflow_ids.contains(xflow_reference) {
                    let message = format!("Page: Contains a reference to xflow id '{}', which does not exist in this model",
                                          xflow_reference);
                    errors.push(ValidationError {
                                    code: 1,
                                    message: message,
                                    paths: vec![format!("/pages/{id}", id = page.id)],
                                });
                }
            }
        }

        errors
    }

    pub fn all_doc_collections_have_correct_unique_keys(model: &ModelDocument)
                                                        -> Vec<ValidationError> {
        debug!("all_doc_collections_have_correct_unique_keys");
        let mut errors = Vec::<ValidationError>::new();

        let mut xflow_ids = HashSet::<&String>::new();

        for doc in &model.doc.xflows {
            if xflow_ids.contains(&doc.id) {
                let message = format!("xflow: Duplicate ID found in XFlow document '{}'", doc.id);
                errors.push(ValidationError {
                                code: 1,
                                message: message,
                                paths: vec![format!("/xflow/{id}", id = doc.id)],
                            });
            } else {
                xflow_ids.insert(&doc.id);
            }
        }

        let mut page_ids = HashSet::<&String>::new();

        for doc in &model.doc.pages {
            if page_ids.contains(&doc.id) {
                let message = format!("page: Duplicate ID found in page document '{}'", doc.id);
                errors.push(ValidationError {
                                code: 1,
                                message: message,
                                paths: vec![format!("/page/{id}", id = doc.id)],
                            });
            } else {
                page_ids.insert(&doc.id);
            }
        }

        let mut translation_ids = HashSet::<&String>::new();

        for doc in &model.doc.translations {
            if translation_ids.contains(&doc.id) {
                let message = format!("translation: Duplicate ID found in translation document '{}'",
                                      doc.id);
                errors.push(ValidationError {
                                code: 1,
                                message: message,
                                paths: vec![format!("/translation/{id}", id = doc.id)],
                            });
            } else {
                translation_ids.insert(&doc.id);
            }
        }

        errors
    }
}
<|endoftext|>


<commit_before>use super::validate_module;
use builder::module;
use elements::{BlockType, ExportEntry, External, FunctionType, GlobalEntry, GlobalType,
				ImportEntry, InitExpr, Internal, MemoryType, Opcode, Opcodes, TableType,
				ValueType};

#[test]
fn empty_is_valid() {
	let module = module().build();
	assert!(validate_module(&module).is_ok());
}

#[test]
fn mem_limits() {
	// min > max
	let m = module()
		.memory()
			.with_min(10)
			.with_max(Some(9))
			.build()
		.build();
	assert!(validate_module(&m).is_err());

	// min = max
	let m = module()
		.memory()
			.with_min(10)
			.with_max(Some(10))
			.build()
		.build();
	assert!(validate_module(&m).is_ok());

	// mem is always valid without max
	let m = module()
		.memory()
			.with_min(10)
			.build()
		.build();
	assert!(validate_module(&m).is_ok());
}

#[test]
fn table_limits() {
	// min > max
	let m = module()
		.table()
			.with_min(10)
			.with_max(Some(9))
			.build()
		.build();
	assert!(validate_module(&m).is_err());

	// min = max
	let m = module()
		.table()
			.with_min(10)
			.with_max(Some(10))
			.build()
		.build();
	assert!(validate_module(&m).is_ok());

	// table is always valid without max
	let m = module()
		.table()
			.with_min(10)
			.build()
		.build();
	assert!(validate_module(&m).is_ok());
}

#[test]
fn global_init_const() {
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(
					vec![Opcode::I32Const(42), Opcode::End]
				)
			)
		)
		.build();
	assert!(validate_module(&m).is_ok());

	// init expr type differs from declared global type
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I64, true),
				InitExpr::new(vec![Opcode::I32Const(42), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn global_init_global() {
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, false))
			)
		)
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_ok());

	// get_global can reference only previously defined globals
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// get_global can reference only const globals
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, true))
			)
		)
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// get_global in init_expr can only refer to imported globals.
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, false),
				InitExpr::new(vec![Opcode::I32Const(0), Opcode::End])
			)
		)
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn global_init_misc() {
	// without delimiting End opcode
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::I32Const(42)])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// empty init expr
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// not an constant opcode used
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::Unreachable, Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn module_limits_validity() {
	// module cannot contain more than 1 memory atm.
	let m = module()
		.with_import(
			ImportEntry::new(
				"core".into(),
				"memory".into(),
				External::Memory(MemoryType::new(10, None))
			)
		)
		.memory()
			.with_min(10)
			.build()
		.build();
	assert!(validate_module(&m).is_err());

	// module cannot contain more than 1 table atm.
	let m = module()
		.with_import(
			ImportEntry::new(
				"core".into(),
				"table".into(),
				External::Table(TableType::new(10, None))
			)
		)
		.table()
			.with_min(10)
			.build()
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn funcs() {
	// recursive function calls is legal.
	let m = module()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(1),
				Opcode::End,
			])).build()
			.build()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(0),
				Opcode::End,
			])).build()
			.build()
		.build();
	assert!(validate_module(&m).is_ok());
}

#[test]
fn globals() {
	// import immutable global is legal.
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, false))
			)
		)
		.build();
	assert!(validate_module(&m).is_ok());

	// import mutable global is invalid.
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, true))
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

// TODO: pepyakin
// #[test]
// fn if_else_with_return_type_validation() {
// 	let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap();
// 	let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default()));

// 	Validator::validate_function(&mut context, BlockType::NoResult, &[
// 		Opcode::I32Const(1),
// 		Opcode::If(BlockType::NoResult),
// 			Opcode::I32Const(1),
// 			Opcode::If(BlockType::Value(ValueType::I32)),
// 				Opcode::I32Const(1),
// 			Opcode::Else,
// 				Opcode::I32Const(2),
// 			Opcode::End,
// 		Opcode::Drop,
// 		Opcode::End,
// 		Opcode::End,
// 	]).unwrap();
// }
<commit_msg>Refactor limits test<commit_after>use super::validate_module;
use builder::module;
use elements::{BlockType, ExportEntry, External, FunctionType, GlobalEntry, GlobalType,
				ImportEntry, InitExpr, Internal, MemoryType, Opcode, Opcodes, TableType,
				ValueType};

#[test]
fn empty_is_valid() {
	let module = module().build();
	assert!(validate_module(&module).is_ok());
}

#[test]
fn limits() {
	let test_cases = vec![
		// min > max
		(10, Some(9), false),
		// min = max
		(10, Some(10), true),
		// table/memory is always valid without max
		(10, None, true),
	];

	for (min, max, is_valid) in test_cases {
		// defined table
		let m = module()
			.table()
				.with_min(min)
				.with_max(max)
				.build()
			.build();
		assert_eq!(validate_module(&m).is_ok(), is_valid);

		// imported table
		let m = module()
			.with_import(
				ImportEntry::new(
					"core".into(),
					"table".into(),
					External::Table(TableType::new(min, max))
				)
			)
			.build();
		assert_eq!(validate_module(&m).is_ok(), is_valid);

		// defined memory
		let m = module()
			.memory()
				.with_min(min)
				.with_max(max)
				.build()
			.build();
		assert_eq!(validate_module(&m).is_ok(), is_valid);

		// imported table
		let m = module()
			.with_import(
				ImportEntry::new(
					"core".into(),
					"memory".into(),
					External::Memory(MemoryType::new(min, max))
				)
			)
			.build();
		assert_eq!(validate_module(&m).is_ok(), is_valid);
	}
}

#[test]
fn global_init_const() {
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(
					vec![Opcode::I32Const(42), Opcode::End]
				)
			)
		)
		.build();
	assert!(validate_module(&m).is_ok());

	// init expr type differs from declared global type
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I64, true),
				InitExpr::new(vec![Opcode::I32Const(42), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn global_init_global() {
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, false))
			)
		)
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_ok());

	// get_global can reference only previously defined globals
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// get_global can reference only const globals
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, true))
			)
		)
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// get_global in init_expr can only refer to imported globals.
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, false),
				InitExpr::new(vec![Opcode::I32Const(0), Opcode::End])
			)
		)
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn global_init_misc() {
	// without delimiting End opcode
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::I32Const(42)])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// empty init expr
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());

	// not an constant opcode used
	let m = module()
		.with_global(
			GlobalEntry::new(
				GlobalType::new(ValueType::I32, true),
				InitExpr::new(vec![Opcode::Unreachable, Opcode::End])
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn module_limits_validity() {
	// module cannot contain more than 1 memory atm.
	let m = module()
		.with_import(
			ImportEntry::new(
				"core".into(),
				"memory".into(),
				External::Memory(MemoryType::new(10, None))
			)
		)
		.memory()
			.with_min(10)
			.build()
		.build();
	assert!(validate_module(&m).is_err());

	// module cannot contain more than 1 table atm.
	let m = module()
		.with_import(
			ImportEntry::new(
				"core".into(),
				"table".into(),
				External::Table(TableType::new(10, None))
			)
		)
		.table()
			.with_min(10)
			.build()
		.build();
	assert!(validate_module(&m).is_err());
}

#[test]
fn funcs() {
	// recursive function calls is legal.
	let m = module()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(1),
				Opcode::End,
			])).build()
			.build()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(0),
				Opcode::End,
			])).build()
			.build()
		.build();
	assert!(validate_module(&m).is_ok());
}

#[test]
fn globals() {
	// import immutable global is legal.
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, false))
			)
		)
		.build();
	assert!(validate_module(&m).is_ok());

	// import mutable global is invalid.
	let m = module()
		.with_import(
			ImportEntry::new(
				"env".into(),
				"ext_global".into(),
				External::Global(GlobalType::new(ValueType::I32, true))
			)
		)
		.build();
	assert!(validate_module(&m).is_err());
}

// TODO: pepyakin
// #[test]
// fn if_else_with_return_type_validation() {
// 	let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap();
// 	let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default()));

// 	Validator::validate_function(&mut context, BlockType::NoResult, &[
// 		Opcode::I32Const(1),
// 		Opcode::If(BlockType::NoResult),
// 			Opcode::I32Const(1),
// 			Opcode::If(BlockType::Value(ValueType::I32)),
// 				Opcode::I32Const(1),
// 			Opcode::Else,
// 				Opcode::I32Const(2),
// 			Opcode::End,
// 		Opcode::Drop,
// 		Opcode::End,
// 		Opcode::End,
// 	]).unwrap();
// }
<|endoftext|>