foundationdb_tuple/
subspace.rs1use super::*;
10use std::hash::Hash;
11
12#[derive(Debug, Clone, Eq, PartialEq, Hash)]
24pub struct Subspace {
25 prefix: Vec<u8>,
26 versionstamp_offset: VersionstampOffset,
27}
28
29impl<E: TuplePack> From<E> for Subspace {
30 fn from(e: E) -> Self {
31 let mut prefix = vec![];
32 let versionstamp_offset = pack_into(&e, &mut prefix);
33 Self {
34 prefix,
35 versionstamp_offset,
36 }
37 }
38}
39
40impl Subspace {
41 pub fn all() -> Self {
43 Self {
44 prefix: Vec::new(),
45 versionstamp_offset: VersionstampOffset::None { size: 0 },
46 }
47 }
48
49 pub fn from_bytes(prefix: impl Into<Vec<u8>>) -> Self {
51 let prefix = prefix.into();
52 Self {
53 versionstamp_offset: VersionstampOffset::None {
54 size: u32::try_from(prefix.len()).expect("data doesn't fit u32"),
55 },
56 prefix,
57 }
58 }
59
60 pub fn into_bytes(self) -> Vec<u8> {
62 self.prefix
63 }
64
65 pub fn subspace<T: TuplePack>(&self, t: &T) -> Self {
67 let mut prefix = self.prefix.clone();
68 let mut versionstamp_offset = self.versionstamp_offset;
69 versionstamp_offset += pack_into(t, &mut prefix);
70 Self {
71 prefix,
72 versionstamp_offset,
73 }
74 }
75
76 pub fn bytes(&self) -> &[u8] {
78 self.prefix.as_slice()
79 }
80
81 pub fn pack<T: TuplePack>(&self, t: &T) -> Vec<u8> {
84 let mut out = self.prefix.clone();
85 pack_into(t, &mut out);
86 out
87 }
88
89 pub fn pack_with_versionstamp<T: TuplePack>(&self, t: &T) -> Vec<u8> {
92 let mut output = self.prefix.clone();
93 let mut versionstamp_offset = self.versionstamp_offset;
94 versionstamp_offset += pack_into(t, &mut output);
95 match versionstamp_offset {
96 VersionstampOffset::OneIncomplete { offset } => {
97 output.extend_from_slice(&offset.to_le_bytes());
98 }
99 VersionstampOffset::MultipleIncomplete => {
100 panic!("Subspace cannot contain more than one incomplete versionstamp");
101 }
102 _ => {}
103 }
104 output
105 }
106
107 pub fn unpack<'de, T: TupleUnpack<'de>>(&self, key: &'de [u8]) -> PackResult<T> {
111 if !self.is_start_of(key) {
112 return Err(PackError::BadPrefix);
113 }
114 let key = &key[self.prefix.len()..];
115 unpack(key)
116 }
117
118 pub fn is_start_of(&self, key: &[u8]) -> bool {
121 key.starts_with(&self.prefix)
122 }
123
124 pub fn range(&self) -> (Vec<u8>, Vec<u8>) {
126 let mut begin = Vec::with_capacity(self.prefix.len() + 1);
127 begin.extend_from_slice(&self.prefix);
128 begin.push(0x00);
129
130 let mut end = Vec::with_capacity(self.prefix.len() + 1);
131 end.extend_from_slice(&self.prefix);
132 end.push(0xff);
133
134 (begin, end)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use std::collections::HashMap;
142
143 #[test]
144 fn sub() {
145 let ss0: Subspace = 1.into();
146 let ss1 = ss0.subspace(&2);
147
148 let ss2: Subspace = (1, 2).into();
149
150 assert_eq!(ss1.bytes(), ss2.bytes());
151 }
152
153 #[test]
154 fn pack_unpack() {
155 let ss0: Subspace = 1.into();
156 let tup = (2, 3);
157
158 let packed = ss0.pack(&tup);
159 let expected = pack(&(1, 2, 3));
160 assert_eq!(expected, packed);
161
162 let tup_unpack: (i64, i64) = ss0.unpack(&packed).unwrap();
163 assert_eq!(tup, tup_unpack);
164
165 assert!(ss0.unpack::<(i64, i64, i64)>(&packed).is_err());
166 }
167
168 #[test]
169 fn subspace_pack_with_versionstamp() {
170 let subspace: Subspace = 1.into();
171 let tup = (Versionstamp::incomplete(0), 2);
172
173 let packed = subspace.pack_with_versionstamp(&tup);
174 let expected = pack_with_versionstamp(&(1, Versionstamp::incomplete(0), 2));
175 assert_eq!(expected, packed);
176 }
177
178 #[test]
179 fn subspace_unpack_with_versionstamp() {
180 let subspace: Subspace = 1.into();
183 let tup = (Versionstamp::complete([1; 10], 0), 2);
184 let packed = subspace.pack_with_versionstamp(&tup);
185 let tup_unpack: (Versionstamp, i64) = subspace.unpack(&packed).unwrap();
186 assert_eq!(tup, tup_unpack);
187
188 assert!(subspace
189 .unpack::<(i64, Versionstamp, i64)>(&packed)
190 .is_err());
191 }
192
193 #[test]
194 fn unpack_with_subspace_versionstamp() {
195 let subspace: Subspace = Versionstamp::complete([1; 10], 2).into();
196 let tup = (Versionstamp::complete([1; 10], 0), 2);
197 let packed = subspace.pack_with_versionstamp(&tup);
198 let tup_unpack: (Versionstamp, i64) = subspace.unpack(&packed).unwrap();
199 assert_eq!(tup, tup_unpack);
200 assert!(subspace
201 .unpack::<(Versionstamp, Versionstamp, i64)>(&packed)
202 .is_err());
203 }
204
205 #[test]
206 fn subspace_can_use_incomplete_versionstamp() {
207 let subspace: Subspace = Versionstamp::incomplete(0).into();
208 let tup = (1, 2);
209
210 let packed = subspace.pack_with_versionstamp(&tup);
211 let expected = pack_with_versionstamp(&(Versionstamp::incomplete(0), 1, 2));
212 assert_eq!(expected, packed);
213 }
214
215 #[test]
216 fn child_subspace_can_use_incomplete_versionstamp() {
217 let subspace: Subspace = 1.into();
218 let subspace = subspace.subspace(&Versionstamp::incomplete(0));
219 let tup = (1, 2);
220 let packed = subspace.pack_with_versionstamp(&tup);
221 let expected = pack_with_versionstamp(&(1, Versionstamp::incomplete(0), 1, 2));
222 assert_eq!(expected, packed);
223 }
224
225 #[test]
226 #[should_panic]
227 fn subspace_cannot_use_multiple_incomplete_versionstamps() {
228 let subspace: Subspace = Versionstamp::incomplete(0).into();
229 let subspace = subspace.subspace(&Versionstamp::incomplete(0));
230 subspace.pack_with_versionstamp(&1);
231 }
232
233 #[test]
234 #[should_panic]
235 fn subspace_cannot_pack_multiple_incomplete_versionstamps() {
236 let subspace: Subspace = Versionstamp::incomplete(0).into();
237 subspace.pack_with_versionstamp(&Versionstamp::incomplete(0));
238 }
239
240 #[test]
241 fn is_start_of() {
242 let ss0: Subspace = 1.into();
243 let ss1: Subspace = 2.into();
244 let tup = (2, 3);
245
246 assert!(ss0.is_start_of(&ss0.pack(&tup)));
247 assert!(!ss1.is_start_of(&ss0.pack(&tup)));
248 assert!(Subspace::from("start").is_start_of(&pack(&"start")));
249 assert!(Subspace::from("start").is_start_of(&pack(&"start".to_string())));
250 assert!(!Subspace::from("start").is_start_of(&pack(&"starting")));
251 assert!(Subspace::from("start").is_start_of(&pack(&("start", "end"))));
252 assert!(Subspace::from(("start", 42)).is_start_of(&pack(&("start", 42, "end"))));
253 }
254
255 #[test]
256 fn range() {
257 let ss: Subspace = 1.into();
258 let tup = (2, 3);
259 let packed = ss.pack(&tup);
260
261 let (begin, end) = ss.range();
262 assert!(packed >= begin && packed <= end);
263 }
264
265 #[test]
266 fn equality() {
267 let sub1 = Subspace::all().subspace(&"test");
268 let sub2 = Subspace::all().subspace(&"test");
269 let sub3 = Subspace::all().subspace(&"test2");
270 assert_eq!(sub1, sub2);
271 assert_ne!(sub1, sub3);
272 }
273
274 #[test]
275 fn hash() {
276 let sub1 = Subspace::all().subspace(&"test");
277 let sub2 = Subspace::all().subspace(&"test2");
278
279 let map: HashMap<Subspace, u8> = HashMap::from([(sub1, 1), (sub2, 2)]);
280
281 assert_eq!(map.get(&Subspace::all().subspace(&"test")).unwrap(), &1);
282 assert_eq!(map.get(&Subspace::all().subspace(&"test2")).unwrap(), &2);
283 }
284}