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!(
205 subspace
206 .unpack::<(i64, Versionstamp, i64)>(&packed)
207 .is_err()
208 );
209 }
210
211 #[test]
212 fn unpack_with_subspace_versionstamp() {
213 let subspace: Subspace = Versionstamp::complete([1; 10], 2).into();
214 let tup = (Versionstamp::complete([1; 10], 0), 2);
215 let packed = subspace.pack_with_versionstamp(&tup);
216 let tup_unpack: (Versionstamp, i64) = subspace.unpack(&packed).unwrap();
217 assert_eq!(tup, tup_unpack);
218 assert!(
219 subspace
220 .unpack::<(Versionstamp, Versionstamp, i64)>(&packed)
221 .is_err()
222 );
223 }
224
225 #[test]
226 fn subspace_can_use_incomplete_versionstamp() {
227 let subspace: Subspace = Versionstamp::incomplete(0).into();
228 let tup = (1, 2);
229
230 let packed = subspace.pack_with_versionstamp(&tup);
231 let expected = pack_with_versionstamp(&(Versionstamp::incomplete(0), 1, 2));
232 assert_eq!(expected, packed);
233 }
234
235 #[test]
236 fn child_subspace_can_use_incomplete_versionstamp() {
237 let subspace: Subspace = 1.into();
238 let subspace = subspace.subspace(&Versionstamp::incomplete(0));
239 let tup = (1, 2);
240 let packed = subspace.pack_with_versionstamp(&tup);
241 let expected = pack_with_versionstamp(&(1, Versionstamp::incomplete(0), 1, 2));
242 assert_eq!(expected, packed);
243 }
244
245 #[test]
246 #[should_panic]
247 fn subspace_cannot_use_multiple_incomplete_versionstamps() {
248 let subspace: Subspace = Versionstamp::incomplete(0).into();
249 let subspace = subspace.subspace(&Versionstamp::incomplete(0));
250 subspace.pack_with_versionstamp(&1);
251 }
252
253 #[test]
254 #[should_panic]
255 fn subspace_cannot_pack_multiple_incomplete_versionstamps() {
256 let subspace: Subspace = Versionstamp::incomplete(0).into();
257 subspace.pack_with_versionstamp(&Versionstamp::incomplete(0));
258 }
259
260 #[test]
261 fn is_start_of() {
262 let ss0: Subspace = 1.into();
263 let ss1: Subspace = 2.into();
264 let tup = (2, 3);
265
266 assert!(ss0.is_start_of(&ss0.pack(&tup)));
267 assert!(!ss1.is_start_of(&ss0.pack(&tup)));
268 assert!(Subspace::from("start").is_start_of(&pack(&"start")));
269 assert!(Subspace::from("start").is_start_of(&pack(&"start".to_string())));
270 assert!(!Subspace::from("start").is_start_of(&pack(&"starting")));
271 assert!(Subspace::from("start").is_start_of(&pack(&("start", "end"))));
272 assert!(Subspace::from(("start", 42)).is_start_of(&pack(&("start", 42, "end"))));
273 }
274
275 #[test]
276 fn range() {
277 let ss: Subspace = 1.into();
278 let tup = (2, 3);
279 let packed = ss.pack(&tup);
280
281 let (begin, end) = ss.range();
282 assert!(packed >= begin && packed <= end);
283 }
284
285 #[test]
286 fn equality() {
287 let sub1 = Subspace::all().subspace(&"test");
288 let sub2 = Subspace::all().subspace(&"test");
289 let sub3 = Subspace::all().subspace(&"test2");
290 assert_eq!(sub1, sub2);
291 assert_ne!(sub1, sub3);
292 }
293
294 #[test]
295 fn hash() {
296 let sub1 = Subspace::all().subspace(&"test");
297 let sub2 = Subspace::all().subspace(&"test2");
298
299 let map: HashMap<Subspace, u8> = HashMap::from([(sub1, 1), (sub2, 2)]);
300
301 assert_eq!(map.get(&Subspace::all().subspace(&"test")).unwrap(), &1);
302 assert_eq!(map.get(&Subspace::all().subspace(&"test2")).unwrap(), &2);
303 }
304
305 #[test]
306 fn display() {
307 let sub1 = Subspace::all().subspace(&"test");
308 let sub2 = Subspace::all().subspace(&"test2").subspace(&"path1");
309 let sub3 = Subspace::all().subspace(&"test3").subspace(&"__커피__");
310 let sub4 = Subspace::all().subspace(&"test4\\path1\\path2");
311
312 assert_eq!(format!("{sub1}"), r#"\x02test\x00"#);
313 assert_eq!(format!("{sub2}"), r#"\x02test2\x00\x02path1\x00"#);
314 assert_eq!(
315 format!("{sub3}"),
316 r#"\x02test3\x00\x02__\xec\xbb\xa4\xed\x94\xbc__\x00"#
317 );
318 assert_eq!(format!("{sub4}"), r#"\x02test4\\path1\\path2\x00"#);
319 }
320}