reifydb_core/value/encoded/
key.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use std::{
5	collections::{
6		Bound,
7		Bound::{Excluded, Included, Unbounded},
8	},
9	ops::{Deref, RangeBounds},
10};
11
12use serde::{Deserialize, Serialize};
13
14use crate::util::{CowVec, encoding::binary::decode_binary};
15
16#[derive(Debug, Clone, PartialOrd, Ord, Hash, PartialEq, Eq, Serialize, Deserialize)]
17pub struct EncodedKey(pub CowVec<u8>);
18
19impl Deref for EncodedKey {
20	type Target = CowVec<u8>;
21
22	fn deref(&self) -> &Self::Target {
23		&self.0
24	}
25}
26
27impl EncodedKey {
28	pub fn new(key: impl Into<Vec<u8>>) -> Self {
29		Self(CowVec::new(key.into()))
30	}
31
32	pub fn as_bytes(&self) -> &[u8] {
33		self.0.as_ref()
34	}
35
36	pub fn as_slice(&self) -> &[u8] {
37		self.0.as_ref()
38	}
39}
40
41#[derive(Clone, Debug)]
42pub struct EncodedKeyRange {
43	pub start: Bound<EncodedKey>,
44	pub end: Bound<EncodedKey>,
45}
46
47impl EncodedKeyRange {
48	pub fn new(start: Bound<EncodedKey>, end: Bound<EncodedKey>) -> Self {
49		Self {
50			start,
51			end,
52		}
53	}
54
55	/// Generates a key range for a key prefix, used e.g. for prefix scans.
56	///
57	/// The exclusive end bound is generated by adding 1 to the value of the
58	/// last byte. If the last byte(s) is 0xff (so adding 1 would
59	/// saturation), we instead find the latest non-0xff byte, increment
60	/// that, and truncate the rest. If all bytes are 0xff, we scan to the
61	/// end of the range, since there can't be other prefixes after it.
62	pub fn prefix(prefix: &[u8]) -> Self {
63		let start = Bound::Included(EncodedKey::new(prefix));
64		let end = match prefix.iter().rposition(|&b| b != 0xff) {
65			Some(i) => Bound::Excluded(EncodedKey::new(
66				prefix.iter()
67					.take(i)
68					.copied()
69					.chain(std::iter::once(prefix[i] + 1))
70					.collect::<Vec<_>>(),
71			)),
72			None => Bound::Unbounded,
73		};
74		Self {
75			start,
76			end,
77		}
78	}
79
80	pub fn with_prefix(&self, prefix: EncodedKey) -> Self {
81		let start = match self.start_bound() {
82			Included(key) => {
83				let mut prefixed = Vec::with_capacity(prefix.len() + key.len());
84				prefixed.extend_from_slice(prefix.as_ref());
85				prefixed.extend_from_slice(key.as_ref());
86				Included(EncodedKey::new(prefixed))
87			}
88			Excluded(key) => {
89				let mut prefixed = Vec::with_capacity(prefix.len() + key.len());
90				prefixed.extend_from_slice(prefix.as_ref());
91				prefixed.extend_from_slice(key.as_ref());
92				Excluded(EncodedKey::new(prefixed))
93			}
94			Unbounded => Included(prefix.clone()),
95		};
96
97		let end = match self.end_bound() {
98			Included(key) => {
99				let mut prefixed = Vec::with_capacity(prefix.len() + key.len());
100				prefixed.extend_from_slice(prefix.as_ref());
101				prefixed.extend_from_slice(key.as_ref());
102				Included(EncodedKey::new(prefixed))
103			}
104			Excluded(key) => {
105				let mut prefixed = Vec::with_capacity(prefix.len() + key.len());
106				prefixed.extend_from_slice(prefix.as_ref());
107				prefixed.extend_from_slice(key.as_ref());
108				Excluded(EncodedKey::new(prefixed))
109			}
110			Unbounded => match prefix.as_ref().iter().rposition(|&b| b != 0xff) {
111				Some(i) => {
112					let mut next_prefix = prefix.as_ref()[..=i].to_vec();
113					next_prefix[i] += 1;
114					Excluded(EncodedKey::new(next_prefix))
115				}
116				None => Unbounded,
117			},
118		};
119
120		EncodedKeyRange::new(start, end)
121	}
122
123	/// Constructs a key range from an optional inclusive start key to an
124	/// optional inclusive end key.
125	///
126	/// - `start`: If provided, marks the inclusive lower bound of the range. If `None`, the range is unbounded
127	///   below.
128	/// - `end`: If provided, marks the inclusive upper bound of the range. If `None`, the range is unbounded above.
129	///
130	/// This function does not modify the input keys and assumes they are
131	/// already exact keys (not prefixes). If you need to scan all keys
132	/// with a given prefix, use [`EncodedKeyRange::prefix`] instead.
133	///
134	/// Useful for scanning between two explicit keys in a sorted key-value
135	/// store.
136	pub fn start_end(start: Option<EncodedKey>, end: Option<EncodedKey>) -> Self {
137		let start = match start {
138			Some(s) => Bound::Included(s),
139			None => Bound::Unbounded,
140		};
141
142		let end = match end {
143			Some(e) => Bound::Included(e),
144			None => Bound::Unbounded,
145		};
146
147		Self {
148			start,
149			end,
150		}
151	}
152
153	/// Constructs a key range that fragments the entire keyspace.
154	///
155	/// This range has no lower or upper bounds, making it suitable for full
156	/// scans over all keys in a sorted key-value store.
157	///
158	/// Equivalent to: `..` (in Rust range syntax)
159	pub fn all() -> Self {
160		Self {
161			start: Bound::Unbounded,
162			end: Bound::Unbounded,
163		}
164	}
165
166	/// Parses a human-readable range string into a `KeyRange`.
167	///
168	/// The expected format is `<start>..[=]<end>`, where:
169	/// - `<start>` is the inclusive lower bound (optional),
170	/// - `..` separates the bounds,
171	/// - `=` after `..` makes the upper bound inclusive,
172	/// - `<end>` is the upper bound (optional).
173	///
174	/// Examples:
175	/// - `"a..z"`       => start = Included("a"), end = Excluded("z")
176	/// - `"a..=z"`      => start = Included("a"), end = Included("z")
177	/// - `"..z"`        => start = Unbounded,     end = Excluded("z")
178	/// - `"a.."`        => start = Included("a"), end = Unbounded
179	///
180	/// If parsing fails, it defaults to a degenerate range from `0xff` to
181	/// `0xff` (empty).
182	pub fn parse(str: &str) -> Self {
183		let (mut start, mut end) = (Bound::<EncodedKey>::Unbounded, Bound::<EncodedKey>::Unbounded);
184
185		// Find the ".." separator
186		if let Some(dot_pos) = str.find("..") {
187			let start_part = &str[..dot_pos];
188			let end_part = &str[dot_pos + 2..];
189
190			// Parse start bound
191			if !start_part.is_empty() {
192				start = Bound::Included(EncodedKey(decode_binary(start_part)));
193			}
194
195			// Parse end bound - check for inclusive marker "="
196			if let Some(end_str) = end_part.strip_prefix('=') {
197				// Inclusive end: "..="
198				if !end_str.is_empty() {
199					end = Bound::Included(EncodedKey(decode_binary(end_str)));
200				}
201			} else {
202				// Exclusive end: ".."
203				if !end_part.is_empty() {
204					end = Bound::Excluded(EncodedKey(decode_binary(end_part)));
205				}
206			}
207
208			Self {
209				start,
210				end,
211			}
212		} else {
213			// Invalid format - return degenerate range
214			Self {
215				start: Bound::Included(EncodedKey::new([0xff])),
216				end: Bound::Excluded(EncodedKey::new([0xff])),
217			}
218		}
219	}
220}
221
222impl RangeBounds<EncodedKey> for EncodedKeyRange {
223	fn start_bound(&self) -> Bound<&EncodedKey> {
224		self.start.as_ref()
225	}
226
227	fn end_bound(&self) -> Bound<&EncodedKey> {
228		self.end.as_ref()
229	}
230}
231
232#[cfg(test)]
233mod tests {
234	use std::collections::Bound;
235
236	use super::EncodedKey;
237
238	macro_rules! as_key {
239		($key:expr) => {{ EncodedKey::new(keycode::serialize(&$key)) }};
240	}
241
242	mod prefix {
243		use std::ops::Bound;
244
245		use crate::value::encoded::key::{
246			EncodedKeyRange,
247			tests::{excluded, included},
248		};
249
250		#[test]
251		fn test_simple() {
252			let range = EncodedKeyRange::prefix(&[0x12, 0x34]);
253			assert_eq!(range.start, included(&[0x12, 0x34]));
254			assert_eq!(range.end, excluded(&[0x12, 0x35]));
255		}
256
257		#[test]
258		fn test_with_trailing_ff() {
259			let range = EncodedKeyRange::prefix(&[0x12, 0xff]);
260			assert_eq!(range.start, included(&[0x12, 0xff]));
261			assert_eq!(range.end, excluded(&[0x13]));
262		}
263
264		#[test]
265		fn test_with_multiple_trailing_ff() {
266			let range = EncodedKeyRange::prefix(&[0x12, 0xff, 0xff]);
267			assert_eq!(range.start, included(&[0x12, 0xff, 0xff]));
268			assert_eq!(range.end, excluded(&[0x13]));
269		}
270
271		#[test]
272		fn test_all_ff() {
273			let range = EncodedKeyRange::prefix(&[0xff, 0xff]);
274			assert_eq!(range.start, included(&[0xff, 0xff]));
275			assert_eq!(range.end, Bound::Unbounded);
276		}
277
278		#[test]
279		fn test_empty() {
280			let range = EncodedKeyRange::prefix(&[]);
281			assert_eq!(range.start, included(&[]));
282			assert_eq!(range.end, Bound::Unbounded);
283		}
284
285		#[test]
286		fn test_mid_increment() {
287			let range = EncodedKeyRange::prefix(&[0x12, 0x00, 0xff]);
288			assert_eq!(range.start, included(&[0x12, 0x00, 0xff]));
289			assert_eq!(range.end, excluded(&[0x12, 0x01]));
290		}
291	}
292
293	mod start_end {
294		use std::ops::Bound;
295
296		use crate::{
297			EncodedKey,
298			util::encoding::keycode,
299			value::encoded::key::{EncodedKeyRange, tests::included},
300		};
301
302		#[test]
303		fn test_start_and_end() {
304			let range = EncodedKeyRange::start_end(Some(as_key!(1)), Some(as_key!(2)));
305			assert_eq!(range.start, included(&as_key!(1)));
306			assert_eq!(range.end, included(&as_key!(2)));
307		}
308
309		#[test]
310		fn test_start_only() {
311			let range = EncodedKeyRange::start_end(Some(as_key!(1)), None);
312			assert_eq!(range.start, included(&as_key!(1)));
313			assert_eq!(range.end, Bound::Unbounded);
314		}
315
316		#[test]
317		fn test_end_only() {
318			let range = EncodedKeyRange::start_end(None, Some(as_key!(2)));
319			assert_eq!(range.start, Bound::Unbounded);
320			assert_eq!(range.end, included(&as_key!(2)));
321		}
322
323		#[test]
324		fn test_unbounded_range() {
325			let range = EncodedKeyRange::start_end(None, None);
326			assert_eq!(range.start, Bound::Unbounded);
327			assert_eq!(range.end, Bound::Unbounded);
328		}
329
330		#[test]
331		fn test_full_byte_range() {
332			let range = EncodedKeyRange::start_end(Some(as_key!(0x00)), Some(as_key!(0xff)));
333			assert_eq!(range.start, included(&as_key!(0x00)));
334			assert_eq!(range.end, included(&as_key!(0xff)));
335		}
336
337		#[test]
338		fn test_identical_bounds() {
339			let range = EncodedKeyRange::start_end(Some(as_key!(0x42)), Some(as_key!(0x42)));
340			assert_eq!(range.start, included(&as_key!(0x42)));
341			assert_eq!(range.end, included(&as_key!(0x42)));
342		}
343	}
344
345	mod all {
346		use std::ops::Bound;
347
348		use crate::value::encoded::key::EncodedKeyRange;
349
350		#[test]
351		fn test_is_unbounded() {
352			let range = EncodedKeyRange::all();
353			assert_eq!(range.start, Bound::Unbounded);
354			assert_eq!(range.end, Bound::Unbounded);
355		}
356	}
357
358	mod parse {
359		use std::ops::Bound;
360
361		use crate::value::encoded::key::{
362			EncodedKey, EncodedKeyRange,
363			tests::{excluded, included},
364		};
365
366		#[test]
367		fn test_full_range() {
368			let r = EncodedKeyRange::parse("a..z");
369			assert_eq!(r.start, included(b"a"));
370			assert_eq!(r.end, excluded(b"z"));
371		}
372
373		#[test]
374		fn test_inclusive_end() {
375			let r = EncodedKeyRange::parse("a..=z");
376			assert_eq!(r.start, included(b"a"));
377			assert_eq!(r.end, included(b"z"));
378		}
379
380		#[test]
381		fn test_unbounded_start() {
382			let r = EncodedKeyRange::parse("..z");
383			assert_eq!(r.start, Bound::Unbounded);
384			assert_eq!(r.end, excluded(b"z"));
385		}
386
387		#[test]
388		fn test_unbounded_end() {
389			let r = EncodedKeyRange::parse("a..");
390			assert_eq!(r.start, included(b"a"));
391			assert_eq!(r.end, Bound::Unbounded);
392		}
393
394		#[test]
395		fn test_inclusive_only() {
396			let r = EncodedKeyRange::parse("..=z");
397			assert_eq!(r.start, Bound::Unbounded);
398			assert_eq!(r.end, included(b"z"));
399		}
400
401		#[test]
402		fn test_invalid_string_returns_degenerate_range() {
403			let r = EncodedKeyRange::parse("not a range");
404			let expected = EncodedKey::new([0xff]);
405			assert_eq!(r.start, Bound::Included(expected.clone()));
406			assert_eq!(r.end, Bound::Excluded(expected));
407		}
408
409		#[test]
410		fn test_empty_string_returns_degenerate_range() {
411			let r = EncodedKeyRange::parse("");
412			let expected = EncodedKey::new([0xff]);
413			assert_eq!(r.start, Bound::Included(expected.clone()));
414			assert_eq!(r.end, Bound::Excluded(expected));
415		}
416
417		#[test]
418		fn test_binary_encoded_values() {
419			let r = EncodedKeyRange::parse("0101..=0aff");
420			// decode_binary("0101") = [0x01, 0x01]
421			assert_eq!(r.start, included(b"0101"));
422			// decode_binary("0aff") = [0x0a, 0xff]
423			assert_eq!(r.end, included(b"0aff"));
424		}
425	}
426
427	fn included(key: &[u8]) -> Bound<EncodedKey> {
428		Bound::Included(EncodedKey::new(key))
429	}
430
431	fn excluded(key: &[u8]) -> Bound<EncodedKey> {
432		Bound::Excluded(EncodedKey::new(key))
433	}
434}