1use super::*;
10use std::fmt::Formatter;
11use std::hash::Hash;
12
13#[derive(Debug, Clone, Eq, PartialEq, Hash)]
25pub struct Subspace {
26 prefix: Vec<u8>,
27 versionstamp_offset: VersionstampOffset,
28}
29
30impl Display for Subspace {
31 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
32 for &b in &self.prefix {
33 if (32..127).contains(&b) && b != b'\\' {
34 write!(f, "{}", b as char)?;
35 } else if b == b'\\' {
36 write!(f, "\\\\")?;
37 } else {
38 write!(f, "\\x{b:02x}")?;
39 }
40 }
41 Ok(())
42 }
43}
44
45impl<E: TuplePack> From<E> for Subspace {
46 fn from(e: E) -> Self {
47 let mut prefix = vec![];
48 let versionstamp_offset = pack_into(&e, &mut prefix);
49 Self {
50 prefix,
51 versionstamp_offset,
52 }
53 }
54}
55
56impl Subspace {
57 pub fn all() -> Self {
59 Self {
60 prefix: Vec::new(),
61 versionstamp_offset: VersionstampOffset::None { size: 0 },
62 }
63 }
64
65 pub fn from_bytes(prefix: impl Into<Vec<u8>>) -> Self {
67 let prefix = prefix.into();
68 Self {
69 versionstamp_offset: VersionstampOffset::None {
70 size: u32::try_from(prefix.len()).expect("data doesn't fit u32"),
71 },
72 prefix,
73 }
74 }
75
76 pub fn into_bytes(self) -> Vec<u8> {
78 self.prefix
79 }
80
81 pub fn subspace<T: TuplePack>(&self, t: &T) -> Self {
83 let mut prefix = self.prefix.clone();
84 let mut versionstamp_offset = self.versionstamp_offset;
85 versionstamp_offset += pack_into(t, &mut prefix);
86 Self {
87 prefix,
88 versionstamp_offset,
89 }
90 }
91
92 pub fn bytes(&self) -> &[u8] {
94 self.prefix.as_slice()
95 }
96
97 pub fn pack<T: TuplePack>(&self, t: &T) -> Vec<u8> {
100 let mut out = self.prefix.clone();
101 pack_into(t, &mut out);
102 out
103 }
104
105 pub fn pack_with_versionstamp<T: TuplePack>(&self, t: &T) -> Vec<u8> {
108 let mut output = self.prefix.clone();
109 let mut versionstamp_offset = self.versionstamp_offset;
110 versionstamp_offset += pack_into(t, &mut output);
111 match versionstamp_offset {
112 VersionstampOffset::OneIncomplete { offset } => {
113 output.extend_from_slice(&offset.to_le_bytes());
114 }
115 VersionstampOffset::MultipleIncomplete => {
116 panic!("Subspace cannot contain more than one incomplete versionstamp");
117 }
118 _ => {}
119 }
120 output
121 }
122
123 pub fn unpack<'de, T: TupleUnpack<'de>>(&self, key: &'de [u8]) -> PackResult<T> {
127 if !self.is_start_of(key) {
128 return Err(PackError::BadPrefix);
129 }
130 let key = &key[self.prefix.len()..];
131 unpack(key)
132 }
133
134 pub fn is_start_of(&self, key: &[u8]) -> bool {
137 key.starts_with(&self.prefix)
138 }
139
140 pub fn range(&self) -> (Vec<u8>, Vec<u8>) {
142 let mut begin = Vec::with_capacity(self.prefix.len() + 1);
143 begin.extend_from_slice(&self.prefix);
144 begin.push(0x00);
145
146 let mut end = Vec::with_capacity(self.prefix.len() + 1);
147 end.extend_from_slice(&self.prefix);
148 end.push(0xff);
149
150 (begin, end)
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use std::collections::HashMap;
158
159 #[test]
160 fn sub() {
161 let ss0: Subspace = 1.into();
162 let ss1 = ss0.subspace(&2);
163
164 let ss2: Subspace = (1, 2).into();
165
166 assert_eq!(ss1.bytes(), ss2.bytes());
167 }
168
169 #[test]
170 fn pack_unpack() {
171 let ss0: Subspace = 1.into();
172 let tup = (2, 3);
173
174 let packed = ss0.pack(&tup);
175 let expected = pack(&(1, 2, 3));
176 assert_eq!(expected, packed);
177
178 let tup_unpack: (i64, i64) = ss0.unpack(&packed).unwrap();
179 assert_eq!(tup, tup_unpack);
180
181 assert!(ss0.unpack::<(i64, i64, i64)>(&packed).is_err());
182 }
183
184 #[test]
185 fn subspace_pack_with_versionstamp() {
186 let subspace: Subspace = 1.into();
187 let tup = (Versionstamp::incomplete(0), 2);
188
189 let packed = subspace.pack_with_versionstamp(&tup);
190 let expected = pack_with_versionstamp(&(1, Versionstamp::incomplete(0), 2));
191 assert_eq!(expected, packed);
192 }
193
194 #[test]
195 fn subspace_unpack_with_versionstamp() {
196 let subspace: Subspace = 1.into();
199 let tup = (Versionstamp::complete([1; 10], 0), 2);
200 let packed = subspace.pack_with_versionstamp(&tup);
201 let tup_unpack: (Versionstamp, i64) = subspace.unpack(&packed).unwrap();
202 assert_eq!(tup, tup_unpack);
203
204 assert!(subspace
205 .unpack::<(i64, Versionstamp, i64)>(&packed)
206 .is_err());
207 }
208
209 #[test]
210 fn unpack_with_subspace_versionstamp() {
211 let subspace: Subspace = Versionstamp::complete([1; 10], 2).into();
212 let tup = (Versionstamp::complete([1; 10], 0), 2);
213 let packed = subspace.pack_with_versionstamp(&tup);
214 let tup_unpack: (Versionstamp, i64) = subspace.unpack(&packed).unwrap();
215 assert_eq!(tup, tup_unpack);
216 assert!(subspace
217 .unpack::<(Versionstamp, Versionstamp, i64)>(&packed)
218 .is_err());
219 }
220
221 #[test]
222 fn subspace_can_use_incomplete_versionstamp() {
223 let subspace: Subspace = Versionstamp::incomplete(0).into();
224 let tup = (1, 2);
225
226 let packed = subspace.pack_with_versionstamp(&tup);
227 let expected = pack_with_versionstamp(&(Versionstamp::incomplete(0), 1, 2));
228 assert_eq!(expected, packed);
229 }
230
231 #[test]
232 fn child_subspace_can_use_incomplete_versionstamp() {
233 let subspace: Subspace = 1.into();
234 let subspace = subspace.subspace(&Versionstamp::incomplete(0));
235 let tup = (1, 2);
236 let packed = subspace.pack_with_versionstamp(&tup);
237 let expected = pack_with_versionstamp(&(1, Versionstamp::incomplete(0), 1, 2));
238 assert_eq!(expected, packed);
239 }
240
241 #[test]
242 #[should_panic]
243 fn subspace_cannot_use_multiple_incomplete_versionstamps() {
244 let subspace: Subspace = Versionstamp::incomplete(0).into();
245 let subspace = subspace.subspace(&Versionstamp::incomplete(0));
246 subspace.pack_with_versionstamp(&1);
247 }
248
249 #[test]
250 #[should_panic]
251 fn subspace_cannot_pack_multiple_incomplete_versionstamps() {
252 let subspace: Subspace = Versionstamp::incomplete(0).into();
253 subspace.pack_with_versionstamp(&Versionstamp::incomplete(0));
254 }
255
256 #[test]
257 fn is_start_of() {
258 let ss0: Subspace = 1.into();
259 let ss1: Subspace = 2.into();
260 let tup = (2, 3);
261
262 assert!(ss0.is_start_of(&ss0.pack(&tup)));
263 assert!(!ss1.is_start_of(&ss0.pack(&tup)));
264 assert!(Subspace::from("start").is_start_of(&pack(&"start")));
265 assert!(Subspace::from("start").is_start_of(&pack(&"start".to_string())));
266 assert!(!Subspace::from("start").is_start_of(&pack(&"starting")));
267 assert!(Subspace::from("start").is_start_of(&pack(&("start", "end"))));
268 assert!(Subspace::from(("start", 42)).is_start_of(&pack(&("start", 42, "end"))));
269 }
270
271 #[test]
272 fn range() {
273 let ss: Subspace = 1.into();
274 let tup = (2, 3);
275 let packed = ss.pack(&tup);
276
277 let (begin, end) = ss.range();
278 assert!(packed >= begin && packed <= end);
279 }
280
281 #[test]
282 fn equality() {
283 let sub1 = Subspace::all().subspace(&"test");
284 let sub2 = Subspace::all().subspace(&"test");
285 let sub3 = Subspace::all().subspace(&"test2");
286 assert_eq!(sub1, sub2);
287 assert_ne!(sub1, sub3);
288 }
289
290 #[test]
291 fn hash() {
292 let sub1 = Subspace::all().subspace(&"test");
293 let sub2 = Subspace::all().subspace(&"test2");
294
295 let map: HashMap<Subspace, u8> = HashMap::from([(sub1, 1), (sub2, 2)]);
296
297 assert_eq!(map.get(&Subspace::all().subspace(&"test")).unwrap(), &1);
298 assert_eq!(map.get(&Subspace::all().subspace(&"test2")).unwrap(), &2);
299 }
300
301 #[test]
302 fn display() {
303 let sub1 = Subspace::all().subspace(&"test");
304 let sub2 = Subspace::all().subspace(&"test2").subspace(&"path1");
305 let sub3 = Subspace::all().subspace(&"test3").subspace(&"__커피__");
306 let sub4 = Subspace::all().subspace(&"test4\\path1\\path2");
307
308 assert_eq!(format!("{sub1}"), r#"\x02test\x00"#);
309 assert_eq!(format!("{sub2}"), r#"\x02test2\x00\x02path1\x00"#);
310 assert_eq!(
311 format!("{sub3}"),
312 r#"\x02test3\x00\x02__\xec\xbb\xa4\xed\x94\xbc__\x00"#
313 );
314 assert_eq!(format!("{sub4}"), r#"\x02test4\\path1\\path2\x00"#);
315 }
316}