ark-serialize 0.2.0

A library for serializing types in the arkworks ecosystem
Documentation
/// Represents metadata to be appended to an object's serialization. For
/// example, when serializing elliptic curve points, one can
/// use a `Flag` to represent whether the serialization is the point
/// at infinity, or whether the `y` coordinate is positive or not.
/// These bits will be appended to the end of the point's serialization,
/// or included in a new byte, depending on space available.
///
/// This is meant to be provided to `CanonicalSerializeWithFlags` and
/// `CanonicalDeserializeWithFlags`
pub trait Flags: Default + Clone + Copy + Sized {
    /// The number of bits required to encode `Self`.
    /// This should be at most 8.
    const BIT_SIZE: usize;

    // Returns a bit mask corresponding to `self`.
    // For example, if `Self` contains two variants, there are just two possible
    // bit masks: `0` and `1 << 7`.
    fn u8_bitmask(&self) -> u8;

    // Tries to read `Self` from `value`. Should return `None` if the `Self::BIT_SIZE`
    // most-significant bits of `value` do not correspond to those generated by
    // `u8_bitmask`.
    //
    // That is, this method ignores all but the top `Self::BIT_SIZE` bits, and
    // decides whether these top bits correspond to a bitmask output by `u8_bitmask`.
    fn from_u8(value: u8) -> Option<Self>;

    // Convenience method that reads `Self` from `value`, just like `Self::from_u8`, but
    // additionally zeroes out the bits corresponding to the resulting flag in `value`.
    // If `Self::from_u8(*value)` would return `None`, then this method should
    // *not* modify `value`.
    fn from_u8_remove_flags(value: &mut u8) -> Option<Self> {
        let flags = Self::from_u8(*value);
        if let Some(f) = flags {
            *value &= !f.u8_bitmask();
        }
        flags
    }
}

/// Flags to be encoded into the serialization.
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub struct EmptyFlags;

impl Flags for EmptyFlags {
    const BIT_SIZE: usize = 0;

    #[inline]
    fn u8_bitmask(&self) -> u8 {
        0
    }

    #[inline]
    fn from_u8(value: u8) -> Option<Self> {
        if (value >> 7) == 0 {
            Some(EmptyFlags)
        } else {
            None
        }
    }
}

/// Flags to be encoded into the serialization.
/// The default flags (empty) should not change the binary representation.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SWFlags {
    Infinity,
    PositiveY,
    NegativeY,
}

impl SWFlags {
    #[inline]
    pub fn infinity() -> Self {
        SWFlags::Infinity
    }

    #[inline]
    pub fn from_y_sign(is_positive: bool) -> Self {
        if is_positive {
            SWFlags::PositiveY
        } else {
            SWFlags::NegativeY
        }
    }

    #[inline]
    pub fn is_infinity(&self) -> bool {
        matches!(self, SWFlags::Infinity)
    }

    #[inline]
    pub fn is_positive(&self) -> Option<bool> {
        match self {
            SWFlags::Infinity => None,
            SWFlags::PositiveY => Some(true),
            SWFlags::NegativeY => Some(false),
        }
    }
}

impl Default for SWFlags {
    #[inline]
    fn default() -> Self {
        // NegativeY doesn't change the serialization
        SWFlags::NegativeY
    }
}

impl Flags for SWFlags {
    const BIT_SIZE: usize = 2;

    #[inline]
    fn u8_bitmask(&self) -> u8 {
        let mut mask = 0;
        match self {
            SWFlags::Infinity => mask |= 1 << 6,
            SWFlags::PositiveY => mask |= 1 << 7,
            _ => (),
        }
        mask
    }

    #[inline]
    fn from_u8(value: u8) -> Option<Self> {
        let x_sign = (value >> 7) & 1 == 1;
        let is_infinity = (value >> 6) & 1 == 1;
        match (x_sign, is_infinity) {
            // This is invalid because we only want *one* way to serialize
            // the point at infinity.
            (true, true) => None,
            (false, true) => Some(SWFlags::Infinity),
            (true, false) => Some(SWFlags::PositiveY),
            (false, false) => Some(SWFlags::NegativeY),
        }
    }
}

/// Flags to be encoded into the serialization.
/// The default flags (empty) should not change the binary representation.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum EdwardsFlags {
    PositiveY,
    NegativeY,
}

impl EdwardsFlags {
    #[inline]
    pub fn from_y_sign(is_positive: bool) -> Self {
        if is_positive {
            EdwardsFlags::PositiveY
        } else {
            EdwardsFlags::NegativeY
        }
    }

    #[inline]
    pub fn is_positive(&self) -> bool {
        match self {
            EdwardsFlags::PositiveY => true,
            EdwardsFlags::NegativeY => false,
        }
    }
}

impl Default for EdwardsFlags {
    #[inline]
    fn default() -> Self {
        // NegativeY doesn't change the serialization
        EdwardsFlags::NegativeY
    }
}

impl Flags for EdwardsFlags {
    const BIT_SIZE: usize = 1;

    #[inline]
    fn u8_bitmask(&self) -> u8 {
        let mut mask = 0;
        if let Self::PositiveY = self {
            mask |= 1 << 7;
        }
        mask
    }

    #[inline]
    fn from_u8(value: u8) -> Option<Self> {
        let x_sign = (value >> 7) & 1 == 1;
        if x_sign {
            Some(Self::PositiveY)
        } else {
            Some(Self::NegativeY)
        }
    }
}