1use std::fmt;
2
3#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
4pub struct PrimaryKey(Vec<u8>);
5
6#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
7pub struct SortKey(Vec<u8>);
8
9#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub struct DocumentKey {
11 pub pk: PrimaryKey,
12 pub sk: SortKey,
13}
14
15impl PrimaryKey {
16 pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
17 Self(bytes.into())
18 }
19
20 pub fn as_bytes(&self) -> &[u8] {
21 &self.0
22 }
23
24 pub fn into_bytes(self) -> Vec<u8> {
25 self.0
26 }
27}
28
29impl SortKey {
30 pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
31 Self(bytes.into())
32 }
33
34 pub fn as_bytes(&self) -> &[u8] {
35 &self.0
36 }
37
38 pub fn into_bytes(self) -> Vec<u8> {
39 self.0
40 }
41}
42
43impl DocumentKey {
44 pub fn new(pk: impl Into<Vec<u8>>, sk: impl Into<Vec<u8>>) -> Self {
45 Self {
46 pk: PrimaryKey::new(pk),
47 sk: SortKey::new(sk),
48 }
49 }
50
51 pub fn from_parts(pk: PrimaryKey, sk: SortKey) -> Self {
52 Self { pk, sk }
53 }
54
55 pub fn encode(&self) -> Vec<u8> {
61 let mut encoded = Vec::with_capacity(self.pk.0.len() + self.sk.0.len() + 4);
62 encode_component(&self.pk.0, &mut encoded);
63 encode_component(&self.sk.0, &mut encoded);
64 encoded
65 }
66
67 pub fn encoded_len(&self) -> usize {
69 encoded_component_len(&self.pk.0) + encoded_component_len(&self.sk.0)
70 }
71
72 pub fn decode(encoded: &[u8]) -> Result<Self, KeyCodecError> {
74 let (pk, next) = decode_component(encoded, 0)?;
75 let (sk, end) = decode_component(encoded, next)?;
76 if end != encoded.len() {
77 return Err(KeyCodecError::TrailingBytes { offset: end });
78 }
79 Ok(Self::from_parts(PrimaryKey::new(pk), SortKey::new(sk)))
80 }
81}
82
83impl fmt::Debug for PrimaryKey {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 formatter.debug_tuple("PrimaryKey").field(&self.0).finish()
86 }
87}
88
89impl fmt::Debug for SortKey {
90 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91 formatter.debug_tuple("SortKey").field(&self.0).finish()
92 }
93}
94
95impl fmt::Debug for DocumentKey {
96 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
97 formatter
98 .debug_struct("DocumentKey")
99 .field("pk", &self.pk)
100 .field("sk", &self.sk)
101 .finish()
102 }
103}
104
105#[derive(Clone, Debug, Eq, PartialEq)]
106pub enum KeyCodecError {
107 TruncatedEscape { offset: usize },
108 InvalidEscape { offset: usize, byte: u8 },
109 MissingComponent { offset: usize },
110 TrailingBytes { offset: usize },
111}
112
113impl fmt::Display for KeyCodecError {
114 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 Self::TruncatedEscape { offset } => write!(formatter, "truncated escape at {offset}"),
117 Self::InvalidEscape { offset, byte } => {
118 write!(formatter, "invalid escape byte {byte:#04x} at {offset}")
119 }
120 Self::MissingComponent { offset } => write!(formatter, "missing component at {offset}"),
121 Self::TrailingBytes { offset } => write!(formatter, "trailing bytes at {offset}"),
122 }
123 }
124}
125
126impl std::error::Error for KeyCodecError {}
127
128fn encode_component(bytes: &[u8], output: &mut Vec<u8>) {
129 for &byte in bytes {
130 if byte == 0 {
131 output.extend_from_slice(&[0, 0xff]);
132 } else {
133 output.push(byte);
134 }
135 }
136 output.extend_from_slice(&[0, 0]);
137}
138
139fn encoded_component_len(bytes: &[u8]) -> usize {
140 bytes.len() + bytes.iter().filter(|byte| **byte == 0).count() + 2
141}
142
143fn decode_component(encoded: &[u8], start: usize) -> Result<(Vec<u8>, usize), KeyCodecError> {
144 if start >= encoded.len() {
145 return Err(KeyCodecError::MissingComponent { offset: start });
146 }
147
148 let mut bytes = Vec::new();
149 let mut offset = start;
150 while offset < encoded.len() {
151 let byte = encoded[offset];
152 if byte != 0 {
153 bytes.push(byte);
154 offset += 1;
155 continue;
156 }
157
158 let Some(&escape) = encoded.get(offset + 1) else {
159 return Err(KeyCodecError::TruncatedEscape { offset });
160 };
161 match escape {
162 0 => return Ok((bytes, offset + 2)),
163 0xff => {
164 bytes.push(0);
165 offset += 2;
166 }
167 other => {
168 return Err(KeyCodecError::InvalidEscape {
169 offset,
170 byte: other,
171 });
172 }
173 }
174 }
175
176 Err(KeyCodecError::TruncatedEscape { offset })
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use proptest::prelude::*;
183
184 proptest! {
185 #[test]
186 fn round_trip_arbitrary_binary(pk in proptest::collection::vec(any::<u8>(), 0..80), sk in proptest::collection::vec(any::<u8>(), 0..80)) {
187 let key = DocumentKey::new(pk, sk);
188 let encoded = key.encode();
189 prop_assert_eq!(DocumentKey::decode(&encoded), Ok(key));
190 }
191
192 #[test]
193 fn encoding_preserves_document_order(
194 apk in proptest::collection::vec(any::<u8>(), 0..20),
195 ask in proptest::collection::vec(any::<u8>(), 0..20),
196 bpk in proptest::collection::vec(any::<u8>(), 0..20),
197 bsk in proptest::collection::vec(any::<u8>(), 0..20),
198 ) {
199 let left = DocumentKey::new(apk, ask);
200 let right = DocumentKey::new(bpk, bsk);
201 prop_assert_eq!(left.cmp(&right), left.encode().cmp(&right.encode()));
202 }
203 }
204
205 #[test]
206 fn malformed_encodings_are_rejected() {
207 for input in [
208 vec![],
209 vec![0],
210 vec![0, 1],
211 vec![0, 0],
212 vec![0, 0, 0],
213 vec![1, 0, 0, 0, 0, 9],
214 ] {
215 assert!(DocumentKey::decode(&input).is_err(), "accepted {input:?}");
216 }
217 assert!(DocumentKey::decode(&[0, 0, 0, 0]).is_ok());
218 }
219
220 #[test]
221 fn encoded_len_matches_canonical_encoding_and_counts_zero_escapes() {
222 let key = DocumentKey::new(vec![0; 1_994], Vec::new());
223 assert_eq!(key.encoded_len(), 3_992);
224 assert_eq!(key.encoded_len(), key.encode().len());
225 let oversized = DocumentKey::new(vec![0; 1_995], Vec::new());
226 assert_eq!(oversized.encoded_len(), 3_994);
227 }
228}