entropic 0.1.1

Traits for converting Rust data structures to/from unstructured bytes
Documentation
mod default;

pub use default::DefaultEntropyScheme;

#[cfg(feature = "alloc")]
use alloc::{ffi::CString, string::String, vec::Vec};

use core::ffi::CStr;
use core::ops::RangeInclusive;

use crate::prelude::*;
use crate::Int;

/// Defines the means by which base types are converted to/from entropy bytes.
///
/// Different entropy schemes can be designed to suit various purposes, such as to guarantee
/// an even distribution of options or to minimize bytes consumed from a [`Source`].
pub trait EntropyScheme: Default + Clone {
    /// Constructs a value within the given range from entropy source bytes.
    fn get_uniform_range<'a, T: Int, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
        range: RangeInclusive<T>,
    ) -> Result<T, EntropicError>;

    /// Converts the value within the given range into bytes and writes them to the sink.
    fn put_uniform_range<'a, T: Int, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        range: RangeInclusive<T>,
        value: T,
    ) -> Result<usize, EntropicError>;

    /// Constructs a value within the given ranges from entropy source bytes.
    fn get_uniform_ranges<'a, T: Int, I: Iterator<Item = &'a u8>, const L: usize>(
        &mut self,
        entropy: &mut I,
        ranges: &[RangeInclusive<T>; L],
    ) -> Result<T, EntropicError>;

    /// Converts the value within the given ranges into bytes and writes them to the sink.
    fn put_uniform_ranges<'a, T: Int, I: Iterator<Item = &'a mut u8>, const L: usize>(
        &mut self,
        entropy: &mut I,
        range: &[RangeInclusive<T>; L],
        value: T,
    ) -> Result<usize, EntropicError>;

    // In general, a uniformly distributed range of values is desired.
    // However, there exist cases where a non-uniform distribution is better, such as for
    // determining the length of a collection of highly composite objects

    // for len fns: min,max,mean,deviation?
    // Add size_hint: usize

    /// Constructs a length value within the given bound from entropy source bytes.
    fn get_bounded_len<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
        range: RangeInclusive<usize>,
    ) -> Result<usize, EntropicError>;

    /// Converts the length value within the given bound into bytes and writes them to the sink.
    fn put_bounded_len<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        range: RangeInclusive<usize>,
        value: usize,
    ) -> Result<usize, EntropicError>;

    /// Constructs a length value from entropy source bytes.
    fn get_unbounded_len<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<usize, EntropicError>;

    /// Converts the length value into bytes and writes them to the sink.
    fn put_unbounded_len<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        value: usize,
    ) -> Result<usize, EntropicError>;

    /// Chooses an Option/Result choice from entropy source bytes.
    fn get_optional<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<bool, EntropicError>;

    /// Converts the Option/Result choice into bytes and writes it to the sink.
    fn put_optional<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        is_some: bool,
    ) -> Result<usize, EntropicError>;

    /// Constructs a boolean value from entropy source bytes.
    #[inline]
    fn get_bool<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<bool, EntropicError> {
        entropy
            .next()
            .ok_or(EntropicError::InsufficientBytes)
            .map(|b| (*b & 0x01) == 1)
    }

    /// Converts the boolean value into bytes and writes them to the sink.
    #[inline]
    fn put_bool<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        value: bool,
    ) -> Result<usize, EntropicError> {
        match entropy.next() {
            Some(b) => {
                *b = if value { 0x01 } else { 0x00 };
                Ok(1)
            }
            None => Err(EntropicError::InsufficientBytes),
        }
    }

    /// Constructs a char value from entropy source bytes.
    #[inline]
    fn get_char<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<char, EntropicError> {
        char::from_u32(
            self.get_uniform_ranges(entropy, &[0u32..=0xD7FFu32, 0xE000u32..=0x10FFFFu32])?,
        )
        .ok_or(EntropicError::Internal)
    }

    /// Converts the char value into bytes and writes them to the sink.
    #[inline]
    fn put_char<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        value: char,
    ) -> Result<usize, EntropicError> {
        self.put_uniform_ranges(
            entropy,
            &[0u32..=0xD7FFu32, 0xE000u32..=0x10FFFFu32],
            value as u32,
        )
    }

    /// Constructs a string value from entropy source bytes.
    #[cfg(feature = "alloc")]
    #[inline]
    fn get_string<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<String, EntropicError> {
        let byte_len = self.get_unbounded_len(entropy)?;

        let mut str_bytes = Vec::new();

        // While there's more bytes left to fill in vec...
        while let Some(rem @ 1..) = byte_len.checked_sub(str_bytes.len()) {
            let b1 = self.get_byte(entropy)?; // Get first byte of UTF-8
            match b1 {
                0b1111_0000..=0b1111_0111 if rem >= 4 => {
                    // 4-byte UTF-8 code point
                    str_bytes.push(b1);
                    for _ in 0..3 {
                        str_bytes.push((self.get_byte(entropy)? & 0b0011_1111) | 0b1000_0000);
                    }
                }
                0b1110_0000..=0b1110_1111 if rem >= 3 => {
                    // 3-byte UTF-8 code point
                    str_bytes.push(b1);
                    for _ in 0..2 {
                        str_bytes.push((self.get_byte(entropy)? & 0b0011_1111) | 0b1000_0000);
                    }
                }
                0b1100_0000..=0b1101_1111 if rem >= 2 => {
                    // 2-byte UTF-8 code point
                    str_bytes.push(b1);
                    str_bytes.push((self.get_byte(entropy)? & 0b0011_1111) | 0b1000_0000);
                }
                // FALL THROUGH: default to 1-byte UTF-8 (slightly favors these characters)
                _ => str_bytes.push(b1 & 0b0111_1111), // 1-byte UTF-8 code point
            }
        }

        String::from_utf8(str_bytes).map_err(|_| EntropicError::Internal)
    }

    /// Converts the String value into bytes and writes them to the sink.
    #[cfg(feature = "alloc")]
    #[inline]
    fn put_string<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        value: &str,
    ) -> Result<usize, EntropicError> {
        let mut len = self.put_unbounded_len(entropy, value.as_bytes().len())?;

        // While there's more bytes left to fill in vec...
        for b in value.as_bytes() {
            self.put_byte(entropy, *b)?;
        }

        len += value.as_bytes().len();
        Ok(len)
    }

    /// Constructs a cstring value from entropy source bytes.
    #[cfg(feature = "alloc")]
    #[inline]
    fn get_cstring<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<CString, EntropicError> {
        let byte_len = self.get_unbounded_len(entropy)?;
        let mut str_bytes = Vec::new();

        // While there's more bytes left to fill in vec...
        while let Some(rem @ 1..) = byte_len.checked_sub(str_bytes.len()) {
            let b1 = self.get_byte(entropy)?; // Get first byte of UTF-8
            match b1 {
                0b1111_0000..=0b1111_0111 if rem >= 4 => {
                    // 4-byte UTF-8 code point
                    str_bytes.push(b1);
                    for _ in 0..3 {
                        str_bytes.push((self.get_byte(entropy)? & 0b0011_1111) | 0b1000_0000);
                    }
                }
                0b1110_0000..=0b1110_1111 if rem >= 3 => {
                    // 3-byte UTF-8 code point
                    str_bytes.push(b1);
                    for _ in 0..2 {
                        str_bytes.push((self.get_byte(entropy)? & 0b0011_1111) | 0b1000_0000);
                    }
                }
                0b1100_0000..=0b1101_1111 if rem >= 2 => {
                    // 2-byte UTF-8 code point
                    str_bytes.push(b1);
                    str_bytes.push((self.get_byte(entropy)? & 0b0011_1111) | 0b1000_0000);
                }
                // Ensure no null terminating bytes exist
                0 => str_bytes.push(b'A'),
                // FALL THROUGH: default to 1-byte UTF-8 (slightly favors these characters)
                _ => str_bytes.push(b1 & 0b0111_1111), // 1-byte UTF-8 code point
            }
        }

        str_bytes.push(0u8);

        CString::from_vec_with_nul(str_bytes).map_err(|_| EntropicError::Internal)
    }

    /// Converts the CString value into bytes and writes them to the sink.
    #[cfg(feature = "alloc")]
    #[inline]
    fn put_cstring<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        value: &CStr,
    ) -> Result<usize, EntropicError> {
        let mut len = self.put_unbounded_len(entropy, value.to_bytes().len())?;

        for b in value.to_bytes() {
            self.put_byte(entropy, *b)?;
        }

        len += value.to_bytes().len();
        Ok(len)
    }

    /// Constructs a byte value from entropy source bytes.
    #[inline]
    fn get_byte<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
    ) -> Result<u8, EntropicError> {
        entropy
            .next()
            .ok_or(EntropicError::InsufficientBytes)
            .copied()
    }

    /// Writes the byte value to the sink.
    #[inline]
    fn put_byte<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        value: u8,
    ) -> Result<usize, EntropicError> {
        match entropy.next() {
            Some(b) => {
                *b = value;
                Ok(1)
            }
            None => Err(EntropicError::InsufficientBytes),
        }
    }

    /// Constructs a slice of bytes from entropy source bytes.
    #[inline]
    fn get_slice<'a, I: Iterator<Item = &'a u8>>(
        &mut self,
        entropy: &mut I,
        slice: &mut [u8],
    ) -> Result<(), EntropicError> {
        for byte in slice.iter_mut() {
            *byte = match entropy.next() {
                Some(b) => *b,
                None => return Err(EntropicError::InsufficientBytes),
            }
        }
        Ok(())
    }

    /// Writes the slice of bytes to the sink.
    #[inline]
    fn put_slice<'a, I: Iterator<Item = &'a mut u8>>(
        &mut self,
        entropy: &mut I,
        slice: &[u8],
    ) -> Result<usize, EntropicError> {
        for byte in slice.iter() {
            match entropy.next() {
                Some(b) => *b = *byte,
                None => return Err(EntropicError::InsufficientBytes),
            }
        }
        Ok(slice.len())
    }

    /// Constructs an array of bytes from entropy source bytes.
    #[inline]
    fn get_bytearray<'a, I: Iterator<Item = &'a u8>, const T: usize>(
        &mut self,
        entropy: &mut I,
    ) -> Result<[u8; T], EntropicError> {
        let mut res = [0u8; T];
        for byte in res.iter_mut() {
            *byte = match entropy.next() {
                Some(b) => *b,
                None => return Err(EntropicError::InsufficientBytes),
            }
        }
        Ok(res)
    }

    /// Writes the array of bytes to the sink.
    #[inline]
    fn put_bytearray<'a, I: Iterator<Item = &'a mut u8>, const T: usize>(
        &mut self,
        entropy: &mut I,
        arr: [u8; T],
    ) -> Result<usize, EntropicError> {
        for byte in arr.iter() {
            match entropy.next() {
                Some(b) => *b = *byte,
                None => return Err(EntropicError::InsufficientBytes),
            }
        }
        Ok(T)
    }
}