Skip to main content

commonware_utils/sequence/
prefixed_u64.rs

1//! A `u64` array type with a prefix byte to allow for multiple key contexts.
2
3use crate::{Array, Span};
4use bytes::{Buf, BufMut};
5use commonware_codec::{Error as CodecError, FixedArray, FixedSize, Read, ReadExt, Write};
6use core::{
7    cmp::{Ord, PartialOrd},
8    fmt::{Debug, Display, Formatter},
9    hash::Hash,
10    ops::Deref,
11};
12use thiserror::Error;
13
14// Errors returned by `U64` functions.
15#[derive(Error, Debug, PartialEq)]
16pub enum Error {
17    #[error("invalid length")]
18    InvalidLength,
19}
20
21/// An `Array` implementation for prefixed `U64`
22#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, FixedArray)]
23#[fixed_array(infallible)]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[repr(transparent)]
26pub struct U64([u8; u64::SIZE + 1]);
27
28impl U64 {
29    pub const fn new(prefix: u8, value: u64) -> Self {
30        // TODO: #![feature(const_index)]
31        // https://github.com/rust-lang/rust/issues/143775
32        let [b0, b1, b2, b3, b4, b5, b6, b7] = value.to_be_bytes();
33        Self([prefix, b0, b1, b2, b3, b4, b5, b6, b7])
34    }
35
36    pub const fn prefix(&self) -> u8 {
37        self.0[0]
38    }
39
40    pub fn value(&self) -> u64 {
41        u64::from_be_bytes(self.0[1..].try_into().unwrap())
42    }
43}
44
45impl Write for U64 {
46    fn write(&self, buf: &mut impl BufMut) {
47        self.0.write(buf);
48    }
49}
50
51impl Read for U64 {
52    type Cfg = ();
53
54    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
55        <[u8; Self::SIZE]>::read(buf).map(Self)
56    }
57}
58
59impl FixedSize for U64 {
60    const SIZE: usize = u64::SIZE + 1;
61}
62
63impl Span for U64 {}
64
65impl Array for U64 {}
66
67impl AsRef<[u8]> for U64 {
68    fn as_ref(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73impl Deref for U64 {
74    type Target = [u8];
75    fn deref(&self) -> &[u8] {
76        &self.0
77    }
78}
79
80impl Debug for U64 {
81    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
82        write!(
83            f,
84            "{}:{}",
85            self.0[0],
86            u64::from_be_bytes(self.0[1..].try_into().unwrap())
87        )
88    }
89}
90
91impl Display for U64 {
92    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
93        Debug::fmt(self, f)
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use commonware_codec::{DecodeExt, Encode};
101
102    #[test]
103    fn test_prefixed_u64() {
104        let prefix = 69u8;
105        let value = 42u64;
106        let array = U64::new(prefix, value);
107        let decoded = U64::decode(array.as_ref()).unwrap();
108        assert_eq!(value, decoded.value());
109        assert_eq!(prefix, decoded.prefix());
110        let from = U64::from(array.0);
111        assert_eq!(value, from.value());
112        assert_eq!(prefix, from.prefix());
113
114        let vec = array.to_vec();
115        let from_vec = U64::decode(vec.as_ref()).unwrap();
116        assert_eq!(value, from_vec.value());
117        assert_eq!(prefix, from_vec.prefix());
118    }
119
120    #[test]
121    fn test_prefixed_u64_codec() {
122        let original = U64::new(69, 42u64);
123
124        let encoded = original.encode();
125        assert_eq!(encoded.len(), U64::SIZE);
126        assert_eq!(encoded, original.as_ref());
127
128        let decoded = U64::decode(encoded).unwrap();
129        assert_eq!(original, decoded);
130    }
131
132    #[cfg(feature = "arbitrary")]
133    mod conformance {
134        use super::*;
135        use commonware_codec::conformance::CodecConformance;
136
137        commonware_conformance::conformance_tests! {
138            CodecConformance<U64>,
139        }
140    }
141}