Skip to main content

nimiq_database_value/
lib.rs

1use std::{borrow::Cow, ffi::CStr, mem, slice};
2
3pub trait IntoDatabaseValue {
4    fn database_byte_size(&self) -> usize;
5    fn copy_into_database(&self, bytes: &mut [u8]);
6}
7
8pub trait FromDatabaseBytes {
9    /// Reads the value from the database key bytes.
10    /// In the default implementation, this will also be used for the value bytes.
11    fn from_key_bytes(bytes: &[u8]) -> Self
12    where
13        Self: Sized;
14
15    /// Reads the value from the database bytes (in value encoding).
16    /// This is only necessary if the value needs to be sorted differently in a dup table.
17    /// DUP values use a lexicographic sort order.
18    fn from_value_bytes(bytes: &[u8]) -> Self
19    where
20        Self: Sized,
21    {
22        Self::from_key_bytes(bytes)
23    }
24}
25
26pub trait AsDatabaseBytes {
27    /// Returns the byte representation to be used in key encoding.
28    /// In the default implementation, this will also be used for the value bytes.
29    fn as_key_bytes(&self) -> Cow<'_, [u8]>;
30
31    /// Returns the byte representation to be used in value encoding.
32    /// This is used if the value needs to be sorted differently in a dup table.
33    /// DUP values use a lexicographic sort order.
34    fn as_value_bytes(&self) -> Cow<'_, [u8]> {
35        self.as_key_bytes()
36    }
37
38    /// Determines whether the values are of fixed size.
39    /// This will set the `DUP_FIXED_SIZE_VALUES` for dup tables flag.
40    const FIXED_SIZE: Option<usize> = None;
41}
42
43impl AsDatabaseBytes for () {
44    fn as_key_bytes(&self) -> Cow<'_, [u8]> {
45        Cow::Borrowed(&[])
46    }
47
48    const FIXED_SIZE: Option<usize> = Some(0);
49}
50
51impl FromDatabaseBytes for () {
52    fn from_key_bytes(bytes: &[u8]) -> Self
53    where
54        Self: Sized,
55    {
56        assert!(bytes.is_empty());
57    }
58}
59
60// Trait implementations
61impl IntoDatabaseValue for [u8] {
62    fn database_byte_size(&self) -> usize {
63        self.len()
64    }
65
66    fn copy_into_database(&self, bytes: &mut [u8]) {
67        bytes.copy_from_slice(self);
68    }
69}
70
71impl IntoDatabaseValue for str {
72    fn database_byte_size(&self) -> usize {
73        self.len()
74    }
75
76    fn copy_into_database(&self, bytes: &mut [u8]) {
77        bytes.copy_from_slice(self.as_bytes());
78    }
79}
80
81impl FromDatabaseBytes for String {
82    fn from_key_bytes(bytes: &[u8]) -> Self
83    where
84        Self: Sized,
85    {
86        String::from_utf8(bytes.to_vec()).unwrap()
87    }
88}
89
90impl FromDatabaseBytes for Vec<u8> {
91    fn from_key_bytes(bytes: &[u8]) -> Self
92    where
93        Self: Sized,
94    {
95        bytes.to_vec()
96    }
97}
98
99impl AsDatabaseBytes for Vec<u8> {
100    fn as_key_bytes(&self) -> Cow<'_, [u8]> {
101        Cow::Borrowed(&self[..])
102    }
103}
104
105impl AsDatabaseBytes for String {
106    fn as_key_bytes(&self) -> Cow<'_, [u8]> {
107        Cow::Borrowed(self.as_bytes())
108    }
109}
110
111impl AsDatabaseBytes for str {
112    fn as_key_bytes(&self) -> Cow<'_, [u8]> {
113        Cow::Borrowed(self.as_bytes())
114    }
115}
116
117impl AsDatabaseBytes for CStr {
118    fn as_key_bytes(&self) -> Cow<'_, [u8]> {
119        Cow::Borrowed(self.to_bytes())
120    }
121}
122
123macro_rules! impl_num_traits {
124    ($typ:ident) => {
125        impl FromDatabaseBytes for $typ {
126            fn from_key_bytes(bytes: &[u8]) -> Self
127            where
128                Self: Sized,
129            {
130                $typ::from_ne_bytes(bytes.try_into().expect("mismatch size"))
131            }
132
133            fn from_value_bytes(bytes: &[u8]) -> Self
134            where
135                Self: Sized,
136            {
137                // It is important to use to_be_bytes here for lexical ordering.
138                $typ::from_be_bytes(bytes.try_into().expect("mismatch size"))
139            }
140        }
141        impl AsDatabaseBytes for $typ {
142            fn as_key_bytes(&self) -> Cow<'_, [u8]> {
143                unsafe {
144                    #[allow(clippy::size_of_in_element_count)]
145                    Cow::Borrowed(slice::from_raw_parts(
146                        self as *const $typ as *const u8,
147                        mem::size_of::<$typ>(),
148                    ))
149                }
150            }
151
152            fn as_value_bytes(&self) -> Cow<'_, [u8]> {
153                // It is important to use to_be_bytes here for lexical ordering.
154                Cow::Owned(self.to_be_bytes().to_vec())
155            }
156
157            const FIXED_SIZE: Option<usize> = Some($typ::BITS as usize / 8);
158        }
159    };
160}
161
162impl_num_traits!(u16);
163impl_num_traits!(u32);
164impl_num_traits!(u64);