commonware_utils/array/
prefixed_u64.rs

1//! A `u64` array type with a prefix byte to allow for multiple key contexts.
2
3use crate::Array;
4use bytes::{Buf, BufMut};
5use commonware_codec::{Codec, Error as CodecError, SizedCodec};
6use std::{
7    cmp::{Ord, PartialOrd},
8    fmt::{Debug, Display},
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)]
23#[repr(transparent)]
24pub struct U64([u8; u64::LEN_ENCODED + 1]);
25
26impl U64 {
27    pub fn new(prefix: u8, value: u64) -> Self {
28        let mut arr = [0; u64::LEN_ENCODED + 1];
29        arr[0] = prefix;
30        arr[1..].copy_from_slice(&u64::to_be_bytes(value));
31
32        Self(arr)
33    }
34
35    pub fn to_u64(&self) -> u64 {
36        u64::from_be_bytes(self.0[1..].try_into().unwrap())
37    }
38
39    pub fn prefix(&self) -> u8 {
40        self.0[0]
41    }
42}
43
44impl Codec for U64 {
45    fn write(&self, buf: &mut impl BufMut) {
46        self.0.write(buf);
47    }
48
49    fn read(buf: &mut impl Buf) -> Result<Self, CodecError> {
50        <[u8; Self::LEN_ENCODED]>::read(buf).map(Self)
51    }
52
53    fn len_encoded(&self) -> usize {
54        Self::LEN_ENCODED
55    }
56}
57
58impl SizedCodec for U64 {
59    const LEN_ENCODED: usize = u64::LEN_ENCODED + 1;
60}
61
62impl Array for U64 {
63    type Error = Error;
64}
65
66impl From<[u8; U64::LEN_ENCODED]> for U64 {
67    fn from(value: [u8; U64::LEN_ENCODED]) -> Self {
68        Self(value)
69    }
70}
71
72impl TryFrom<&[u8]> for U64 {
73    type Error = Error;
74
75    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
76        if value.len() != U64::LEN_ENCODED {
77            return Err(Error::InvalidLength);
78        }
79        let array: [u8; U64::LEN_ENCODED] = value.try_into().map_err(|_| Error::InvalidLength)?;
80        Ok(Self(array))
81    }
82}
83
84impl TryFrom<&Vec<u8>> for U64 {
85    type Error = Error;
86
87    fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
88        Self::try_from(value.as_slice())
89    }
90}
91
92impl TryFrom<Vec<u8>> for U64 {
93    type Error = Error;
94
95    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
96        if value.len() != U64::LEN_ENCODED {
97            return Err(Error::InvalidLength);
98        }
99
100        // If the length is correct, we can safely convert the vector into a boxed slice without any
101        // copies.
102        let boxed_slice = value.into_boxed_slice();
103        let boxed_array: Box<[u8; U64::LEN_ENCODED]> =
104            boxed_slice.try_into().map_err(|_| Error::InvalidLength)?;
105        Ok(Self(*boxed_array))
106    }
107}
108
109impl AsRef<[u8]> for U64 {
110    fn as_ref(&self) -> &[u8] {
111        &self.0
112    }
113}
114
115impl Deref for U64 {
116    type Target = [u8];
117    fn deref(&self) -> &[u8] {
118        &self.0
119    }
120}
121
122impl Debug for U64 {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        write!(
125            f,
126            "{}:{}",
127            self.0[0],
128            u64::from_be_bytes(self.0[1..].try_into().unwrap())
129        )
130    }
131}
132
133impl Display for U64 {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        Debug::fmt(self, f)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_prefixed_u64() {
145        let prefix = 69u8;
146        let value = 42u64;
147        let array = U64::new(prefix, value);
148        let try_from = U64::try_from(array.as_ref()).unwrap();
149        assert_eq!(value, try_from.to_u64());
150        assert_eq!(prefix, try_from.prefix());
151        let from = U64::from(array.0);
152        assert_eq!(value, from.to_u64());
153        assert_eq!(prefix, from.prefix());
154
155        let vec = array.to_vec();
156        let from_vec = U64::try_from(&vec).unwrap();
157        assert_eq!(value, from_vec.to_u64());
158        assert_eq!(prefix, from_vec.prefix());
159        let from_vec = U64::try_from(vec).unwrap();
160        assert_eq!(value, from_vec.to_u64());
161        assert_eq!(prefix, from_vec.prefix());
162    }
163
164    #[test]
165    fn test_prefixed_u64_codec() {
166        let original = U64::new(69, 42u64);
167
168        let encoded = original.encode();
169        assert_eq!(encoded.len(), U64::LEN_ENCODED);
170        assert_eq!(encoded, original.as_ref());
171
172        let decoded = U64::decode(encoded).unwrap();
173        assert_eq!(original, decoded);
174    }
175}