1use std::{cmp::Ordering, ops::Bound};
5
6use reifydb_codec::key::encoded::{EncodedKey, EncodedKeyRange};
7use smallvec::SmallVec;
8
9use crate::{
10 interface::catalog::object::ObjectId,
11 key::{
12 any::{Field, KeyFields, TaggedKey, Width},
13 tag::KeyTag,
14 },
15};
16
17pub type OwnedField = Field<'static>;
18
19pub fn object_fields(object: ObjectId) -> [OwnedField; 2] {
20 [Field::UAsc(Width::U8, object.type_tag() as u128), Field::UDesc(Width::U64, object.as_u64() as u128)]
21}
22
23#[derive(Debug, Clone)]
24pub enum TaggedKeyBound {
25 Kind(KeyTag),
26 KindEnd(KeyTag),
27 Prefix(KeyTag, SmallVec<[OwnedField; 6]>),
28 PrefixEnd(KeyTag, SmallVec<[OwnedField; 6]>),
29 Key(TaggedKey),
30}
31
32impl TaggedKeyBound {
33 pub fn prefix(kind: KeyTag, fields: impl IntoIterator<Item = OwnedField>) -> Self {
34 Self::Prefix(kind, fields.into_iter().collect())
35 }
36
37 pub fn prefix_end(kind: KeyTag, fields: impl IntoIterator<Item = OwnedField>) -> Self {
38 Self::PrefixEnd(kind, fields.into_iter().collect())
39 }
40
41 fn kind_byte(&self) -> u8 {
42 match self {
43 Self::Kind(kind) | Self::Prefix(kind, _) | Self::PrefixEnd(kind, _) => *kind as u8,
44 Self::KindEnd(kind) => (*kind as u8).wrapping_sub(1),
45 Self::Key(key) => key.kind() as u8,
46 }
47 }
48
49 fn sorts_after_its_extensions(&self) -> bool {
50 matches!(self, Self::PrefixEnd(..))
51 }
52
53 pub fn encode(&self) -> EncodedKey {
54 if let Self::Key(key) = self {
55 return key.encode();
56 }
57 let mut out = vec![!self.kind_byte()];
58 for field in self.bound_fields().iter() {
59 field.encode(&mut out);
60 }
61 if self.sorts_after_its_extensions() {
62 match out.iter().rposition(|byte| *byte != 0xff) {
63 Some(last) => {
64 out.truncate(last + 1);
65 out[last] += 1;
66 }
67 None => out.clear(),
68 }
69 }
70 EncodedKey::new(out)
71 }
72
73 fn bound_fields(&self) -> SmallVec<[Field<'_>; 6]> {
74 match self {
75 Self::Kind(_) | Self::KindEnd(_) => SmallVec::new(),
76 Self::Prefix(_, fields) | Self::PrefixEnd(_, fields) => fields.iter().cloned().collect(),
77 Self::Key(key) => key.fields(),
78 }
79 }
80
81 fn compare_fields(&self, other: &Self) -> Ordering {
82 let left = self.bound_fields();
83 let right = other.bound_fields();
84 for (index, (left_field, right_field)) in left.iter().zip(right.iter()).enumerate() {
85 let ordering = left_field.cmp(right_field);
86 if ordering == Ordering::Equal {
87 continue;
88 }
89
90 if index + 1 == left.len()
91 && self.sorts_after_its_extensions()
92 && left_field.is_truncation_of(right_field)
93 {
94 return Ordering::Greater;
95 }
96 if index + 1 == right.len()
97 && other.sorts_after_its_extensions()
98 && right_field.is_truncation_of(left_field)
99 {
100 return Ordering::Less;
101 }
102 return ordering;
103 }
104 match (
105 left.len().cmp(&right.len()),
106 self.sorts_after_its_extensions(),
107 other.sorts_after_its_extensions(),
108 ) {
109 (Ordering::Less, true, _) | (Ordering::Equal, true, false) => Ordering::Greater,
110 (Ordering::Greater, _, true) | (Ordering::Equal, false, true) => Ordering::Less,
111 (ordering, _, _) => ordering,
112 }
113 }
114}
115
116impl Ord for TaggedKeyBound {
117 fn cmp(&self, other: &Self) -> Ordering {
118 other.kind_byte().cmp(&self.kind_byte()).then_with(|| self.compare_fields(other))
119 }
120}
121
122impl PartialOrd for TaggedKeyBound {
123 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
124 Some(self.cmp(other))
125 }
126}
127
128impl PartialEq for TaggedKeyBound {
129 fn eq(&self, other: &Self) -> bool {
130 self.cmp(other) == Ordering::Equal
131 }
132}
133
134impl Eq for TaggedKeyBound {}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct TaggedKeyBoundRange {
138 pub start: Bound<TaggedKeyBound>,
139 pub end: Bound<TaggedKeyBound>,
140}
141
142impl TaggedKeyBoundRange {
143 pub fn start_end(start: TaggedKeyBound, end: TaggedKeyBound) -> Self {
144 Self {
145 start: Bound::Included(start),
146 end: Bound::Included(end),
147 }
148 }
149
150 pub fn prefix(kind: KeyTag, fields: impl IntoIterator<Item = OwnedField> + Clone) -> Self {
151 Self {
152 start: Bound::Included(TaggedKeyBound::prefix(kind, fields.clone())),
153 end: Bound::Excluded(TaggedKeyBound::prefix_end(kind, fields)),
154 }
155 }
156
157 pub fn kind(kind: KeyTag) -> Self {
158 Self::start_end(TaggedKeyBound::Kind(kind), TaggedKeyBound::KindEnd(kind))
159 }
160
161 pub fn resume_after(self, last: Option<&TaggedKey>) -> Self {
162 match last {
163 Some(last) => Self {
164 start: Bound::Excluded(TaggedKeyBound::Key(last.clone())),
165 end: self.end,
166 },
167 None => self,
168 }
169 }
170
171 pub fn resume_before(self, last: Option<&TaggedKey>) -> Self {
172 match last {
173 Some(last) => Self {
174 start: self.start,
175 end: Bound::Excluded(TaggedKeyBound::Key(last.clone())),
176 },
177 None => self,
178 }
179 }
180
181 pub fn empty(kind: KeyTag) -> Self {
182 Self {
183 start: Bound::Excluded(TaggedKeyBound::Kind(kind)),
184 end: Bound::Excluded(TaggedKeyBound::Kind(kind)),
185 }
186 }
187
188 pub fn all() -> Self {
189 Self {
190 start: Bound::Unbounded,
191 end: Bound::Unbounded,
192 }
193 }
194
195 pub fn contains(&self, bound: &TaggedKeyBound) -> bool {
196 let after_start = match &self.start {
197 Bound::Unbounded => true,
198 Bound::Included(start) => bound >= start,
199 Bound::Excluded(start) => bound > start,
200 };
201 let before_end = match &self.end {
202 Bound::Unbounded => true,
203 Bound::Included(end) => bound <= end,
204 Bound::Excluded(end) => bound < end,
205 };
206 after_start && before_end
207 }
208
209 pub fn encode(&self) -> EncodedKeyRange {
210 EncodedKeyRange::new(encode_bound(&self.start), encode_bound(&self.end))
211 }
212}
213
214fn encode_bound(bound: &Bound<TaggedKeyBound>) -> Bound<EncodedKey> {
215 match bound {
216 Bound::Unbounded => Bound::Unbounded,
217 Bound::Included(key) => Bound::Included(key.encode()),
218 Bound::Excluded(key) => {
219 let encoded = key.encode();
220 if encoded.is_empty() {
221 return Bound::Unbounded;
222 }
223 Bound::Excluded(encoded)
224 }
225 }
226}
227
228impl From<TaggedKey> for TaggedKeyBound {
229 fn from(key: TaggedKey) -> Self {
230 Self::Key(key)
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use std::ops::Bound;
237
238 use reifydb_codec::key::{
239 encoded::{EncodedKey, EncodedKeyRange},
240 serializer::KeySerializer,
241 };
242 use reifydb_value::value::row_number::RowNumber;
243
244 use super::{OwnedField, TaggedKeyBound, TaggedKeyBoundRange, object_fields};
245 use crate::{
246 interface::catalog::{id::TableId, object::ObjectId, storage::StorageId},
247 key::{
248 any::{Field, TaggedKey, Width},
249 catalog::{DictionaryKey, KeySerializerCatalogExt, TableKey},
250 row::RowKey,
251 tag::KeyTag,
252 },
253 };
254
255 fn rows() -> Vec<(TaggedKey, EncodedKey)> {
256 let mut out = Vec::new();
257 for storage in [1u64, 2, 3] {
258 for row in [1u64, 2, u64::MAX] {
259 let key = RowKey {
260 storage: StorageId::table(storage),
261 row: RowNumber(row),
262 };
263 let encoded = key.encode();
264 out.push((TaggedKey::from(key), encoded));
265 }
266 }
267 for table in [1u64, 2] {
268 let key = TableKey {
269 table: TableId(table),
270 };
271 let encoded = key.encode();
272 out.push((TaggedKey::from(key), encoded));
273 }
274 out
275 }
276
277 fn storage_start(storage: StorageId) -> TaggedKeyBound {
278 TaggedKeyBound::prefix(
279 KeyTag::Row,
280 [
281 OwnedField::UAsc(Width::U8, ObjectId::from(storage).type_tag() as u128),
282 Field::UDesc(Width::U64, ObjectId::from(storage).as_u64() as u128),
283 ],
284 )
285 }
286
287 fn storage_end(storage: StorageId) -> TaggedKeyBound {
288 let previous = ObjectId::from(storage).prev();
289 TaggedKeyBound::prefix(
290 KeyTag::Row,
291 [
292 OwnedField::UAsc(Width::U8, previous.type_tag() as u128),
293 Field::UDesc(Width::U64, previous.as_u64() as u128),
294 ],
295 )
296 }
297
298 #[test]
299 fn a_typed_key_bound_orders_exactly_like_its_encoding() {
300 let probes = rows();
301 for (left, left_bytes) in &probes {
302 for (right, right_bytes) in &probes {
303 assert_eq!(
304 TaggedKeyBound::Key(left.clone()).cmp(&TaggedKeyBound::Key(right.clone())),
305 left_bytes.cmp(right_bytes),
306 "{left:?} vs {right:?}"
307 );
308 }
309 }
310 }
311
312 #[test]
313 fn a_storage_prefix_selects_the_same_rows_as_the_encoded_range() {
314 for storage in [1u64, 2, 3] {
315 let storage = StorageId::table(storage);
316 let byte_start = RowKey::storage_start(storage);
317 let byte_end = RowKey::storage_end(storage);
318 let typed_start = storage_start(storage);
319 let typed_end = storage_end(storage);
320
321 let probes = rows();
322 let by_bytes: Vec<&TaggedKey> = probes
323 .iter()
324 .filter(|(_, bytes)| *bytes >= byte_start && *bytes <= byte_end)
325 .map(|(key, _)| key)
326 .collect();
327 let by_typed: Vec<&TaggedKey> = probes
328 .iter()
329 .filter(|(key, _)| {
330 let bound = TaggedKeyBound::Key((*key).clone());
331 bound >= typed_start && bound <= typed_end
332 })
333 .map(|(key, _)| key)
334 .collect();
335
336 assert!(!by_bytes.is_empty(), "storage {storage:?} selected nothing by bytes");
337 assert_eq!(by_bytes, by_typed, "storage {storage:?}");
338 }
339 }
340
341 #[test]
342 fn a_field_prefix_bound_encodes_to_the_bytes_its_byte_producer_writes() {
343 for storage in [1u64, 2, u64::MAX] {
347 let storage = StorageId::table(storage);
348 assert_eq!(storage_start(storage).encode(), RowKey::storage_start(storage), "{storage:?}");
349 assert_eq!(storage_end(storage).encode(), RowKey::storage_end(storage), "{storage:?}");
350 }
351 }
352
353 #[test]
354 fn a_kind_span_bound_encodes_the_kind_byte_and_its_predecessor() {
355 let mut start = KeySerializer::with_capacity(1);
358 start.extend_u8(DictionaryKey::TAG as u8);
359 let mut end = KeySerializer::with_capacity(1);
360 end.extend_u8(DictionaryKey::TAG as u8 - 1);
361
362 assert_eq!(TaggedKeyBound::Kind(KeyTag::Dictionary).encode(), start.to_encoded_key());
363 assert_eq!(TaggedKeyBound::KindEnd(KeyTag::Dictionary).encode(), end.to_encoded_key());
364 }
365
366 fn storage_fields(storage: StorageId) -> Vec<OwnedField> {
367 object_fields(ObjectId::from(storage)).to_vec()
368 }
369
370 #[test]
371 fn the_object_id_field_pair_encodes_to_what_extend_object_id_writes() {
372 for storage in [0u64, 1, 255, u64::MAX] {
375 let object = ObjectId::from(StorageId::table(storage));
376 let mut replayed = Vec::new();
377 for field in object_fields(object) {
378 field.encode(&mut replayed);
379 }
380 let mut expected = KeySerializer::with_capacity(9);
381 expected.extend_object_id(object);
382 assert_eq!(replayed.as_slice(), expected.to_encoded_key().as_slice(), "{object:?}");
383 }
384 }
385
386 #[test]
387 fn a_field_prefix_range_encodes_to_the_span_the_byte_prefix_helper_computes() {
388 for storage in [1u64, 2, 255, u64::MAX] {
392 let storage = StorageId::table(storage);
393 let typed = TaggedKeyBoundRange::prefix(KeyTag::Row, storage_fields(storage));
394 let bytes = EncodedKeyRange::prefix(RowKey::storage_start(storage).as_slice());
395 let encoded = typed.encode();
396 assert_eq!(encoded.start, bytes.start, "{storage:?} start");
397 assert_eq!(encoded.end, bytes.end, "{storage:?} end");
398 }
399 }
400
401 #[test]
402 fn a_prefix_range_selects_the_same_keys_typed_as_it_does_encoded() {
403 for storage in [1u64, 2, 3] {
404 let storage = StorageId::table(storage);
405 let typed = TaggedKeyBoundRange::prefix(KeyTag::Row, storage_fields(storage));
406 let bytes = EncodedKeyRange::prefix(RowKey::storage_start(storage).as_slice());
407 let (Bound::Included(typed_start), Bound::Excluded(typed_end)) =
408 (typed.start.clone(), typed.end.clone())
409 else {
410 panic!("a field prefix range is expected to be included-excluded");
411 };
412
413 let probes = rows();
414 let by_bytes: Vec<&TaggedKey> = probes
415 .iter()
416 .filter(|(_, encoded)| contains(&bytes, encoded))
417 .map(|(key, _)| key)
418 .collect();
419 let by_typed: Vec<&TaggedKey> = probes
420 .iter()
421 .filter(|(key, _)| {
422 let bound = TaggedKeyBound::Key((*key).clone());
423 bound >= typed_start && bound < typed_end
424 })
425 .map(|(key, _)| key)
426 .collect();
427
428 assert!(!by_bytes.is_empty(), "storage {storage:?} selected nothing by bytes");
429 assert_eq!(by_bytes, by_typed, "storage {storage:?}");
430 }
431 }
432
433 fn contains(range: &EncodedKeyRange, key: &EncodedKey) -> bool {
434 let after_start = match &range.start {
435 Bound::Unbounded => true,
436 Bound::Included(start) => key >= start,
437 Bound::Excluded(start) => key > start,
438 };
439 let before_end = match &range.end {
440 Bound::Unbounded => true,
441 Bound::Included(end) => key <= end,
442 Bound::Excluded(end) => key < end,
443 };
444 after_start && before_end
445 }
446
447 fn mixed_bounds() -> Vec<TaggedKeyBound> {
448 let mut out = vec![
449 TaggedKeyBound::Kind(KeyTag::Row),
450 TaggedKeyBound::KindEnd(KeyTag::Row),
451 TaggedKeyBound::Kind(KeyTag::Table),
452 TaggedKeyBound::KindEnd(KeyTag::Table),
453 ];
454 for storage in [1u64, 2, 3] {
455 let storage = StorageId::table(storage);
456 out.push(TaggedKeyBound::prefix(KeyTag::Row, storage_fields(storage)));
457 out.push(TaggedKeyBound::prefix_end(KeyTag::Row, storage_fields(storage)));
458 }
459 out.extend(rows().into_iter().map(|(key, _)| TaggedKeyBound::Key(key)));
460 out
461 }
462
463 #[test]
464 fn ordering_over_every_bound_shape_is_a_total_order() {
465 let bounds = mixed_bounds();
469 for left in &bounds {
470 for right in &bounds {
471 assert_eq!(
472 left.cmp(right),
473 right.cmp(left).reverse(),
474 "antisymmetry broken\n left = {left:?}\n right = {right:?}"
475 );
476 }
477 }
478 for left in &bounds {
479 for middle in &bounds {
480 for right in &bounds {
481 if left <= middle && middle <= right {
482 assert!(
483 left <= right,
484 "transitivity broken\n {left:?}\n {middle:?}\n {right:?}"
485 );
486 }
487 }
488 }
489 }
490 }
491
492 #[test]
493 fn a_prefix_end_sorts_above_every_key_that_extends_its_prefix() {
494 for storage in [1u64, 2, 3] {
495 let storage = StorageId::table(storage);
496 let end = TaggedKeyBound::prefix_end(KeyTag::Row, storage_fields(storage));
497 let start = TaggedKeyBound::prefix(KeyTag::Row, storage_fields(storage));
498 let mut extensions = 0;
499 for (key, _) in rows() {
500 let bound = TaggedKeyBound::Key(key.clone());
501 if bound >= start && bound < end {
502 extensions += 1;
503 assert!(end > bound, "{end:?} must sort above {bound:?}");
504 assert!(bound < end, "{bound:?} must sort below {end:?}");
505 }
506 }
507 assert!(extensions > 0, "storage {storage:?} has no extension to compare against");
508 }
509 }
510
511 #[test]
512 fn a_kind_range_brackets_the_whole_kind_inclusively() {
513 let mut start = KeySerializer::with_capacity(1);
514 start.extend_u8(DictionaryKey::TAG as u8);
515 let mut end = KeySerializer::with_capacity(1);
516 end.extend_u8(DictionaryKey::TAG as u8 - 1);
517
518 let encoded = TaggedKeyBoundRange::kind(KeyTag::Dictionary).encode();
519 assert_eq!(encoded.start, Bound::Included(start.to_encoded_key()));
520 assert_eq!(encoded.end, Bound::Included(end.to_encoded_key()));
521 }
522
523 #[test]
524 fn a_kind_span_brackets_every_key_of_that_kind_and_nothing_else() {
525 let start = TaggedKeyBound::Kind(KeyTag::Row);
526 let end = TaggedKeyBound::KindEnd(KeyTag::Row);
527 assert!(start < end, "the kind span must not be empty");
528 for (key, _) in rows() {
529 let bound = TaggedKeyBound::Key(key.clone());
530 let inside = bound >= start && bound <= end;
531 assert_eq!(inside, key.kind() == KeyTag::Row, "{key:?}");
532 }
533 }
534}
535
536#[cfg(test)]
537mod bound_order_matches_encoded_order {
538 use std::borrow::Cow;
539
540 use reifydb_codec::key::serializer::KeySerializer;
541 use smallvec::smallvec;
542
543 use super::*;
544 use crate::{
545 interface::catalog::{
546 id::{IndexId, TableId},
547 object::ObjectId,
548 },
549 key::{
550 any::{ByteEncoding, RawEncoding},
551 catalog::IndexEntryKey,
552 },
553 value::index::encoded::EncodedIndexKey,
554 };
555
556 fn table() -> ObjectId {
557 ObjectId::Table(TableId(1))
558 }
559
560 fn index() -> IndexId {
561 IndexId::primary(1u64)
562 }
563
564 fn entry(tail: &str) -> TaggedKeyBound {
567 let mut serializer = KeySerializer::new();
568 serializer.extend_str(tail);
569 TaggedKeyBound::Key(
570 IndexEntryKey::new(table(), index(), EncodedIndexKey::new(serializer.finish().as_slice()))
571 .into(),
572 )
573 }
574
575 fn tail_prefix(byte: u8) -> SmallVec<[OwnedField; 6]> {
576 object_fields(table())
577 .into_iter()
578 .chain([
579 Field::UAsc(Width::U8, 1),
580 Field::UDesc(Width::U64, index().as_u64() as u128),
581 Field::RawAsc(RawEncoding::Verbatim, Cow::Owned(vec![!byte])),
582 ])
583 .collect()
584 }
585
586 fn probes() -> Vec<TaggedKeyBound> {
587 vec![
588 TaggedKeyBound::Kind(KeyTag::IndexEntry),
589 TaggedKeyBound::Prefix(KeyTag::IndexEntry, tail_prefix(b'a')),
590 TaggedKeyBound::PrefixEnd(KeyTag::IndexEntry, tail_prefix(b'a')),
591 TaggedKeyBound::Prefix(KeyTag::IndexEntry, tail_prefix(b'b')),
592 TaggedKeyBound::PrefixEnd(KeyTag::IndexEntry, tail_prefix(b'b')),
593 entry("a"),
594 entry("a1"),
595 entry("a3"),
596 entry("aa"),
597 entry("az"),
598 entry("b"),
599 entry("b1"),
600 entry("b2"),
601 entry("c1"),
602 TaggedKeyBound::Prefix(
603 KeyTag::IndexEntry,
604 object_fields(table()).into_iter().collect::<SmallVec<[OwnedField; 6]>>(),
605 ),
606 TaggedKeyBound::PrefixEnd(
607 KeyTag::IndexEntry,
608 object_fields(table()).into_iter().collect::<SmallVec<[OwnedField; 6]>>(),
609 ),
610 TaggedKeyBound::Prefix(
611 KeyTag::IndexEntry,
612 smallvec![Field::BytesDesc(ByteEncoding::Fixed, Cow::Owned(vec![7, 7]))],
613 ),
614 ]
615 }
616
617 #[test]
618 fn a_bound_orders_against_a_key_the_way_their_bytes_do() {
619 let probes = probes();
627 for left in &probes {
628 for right in &probes {
629 if !matches!(left, TaggedKeyBound::Key(_)) && !matches!(right, TaggedKeyBound::Key(_)) {
630 continue;
631 }
632 let left_bytes = left.encode();
633 let right_bytes = right.encode();
634 assert_eq!(
635 left.cmp(right),
636 left_bytes.as_slice().cmp(right_bytes.as_slice()),
637 "bound order disagrees with encoded order\n a = {left:?}\n b = \
638 {right:?}\n a bytes = {:02x?}\n b bytes = {:02x?}",
639 left_bytes.as_slice(),
640 right_bytes.as_slice()
641 );
642 }
643 }
644 }
645
646 #[test]
647 fn every_bound_pair_spans_the_same_keys_typed_as_it_does_encoded() {
648 let probes = probes();
651 let keys: Vec<&TaggedKeyBound> =
652 probes.iter().filter(|bound| matches!(bound, TaggedKeyBound::Key(_))).collect();
653
654 for start in &probes {
655 for end in &probes {
656 let typed_start = Bound::Included(start.clone());
657 let typed_end = Bound::Excluded(end.clone());
658 let raw_start = Bound::Included(start.encode());
659 let raw_end = Bound::Excluded(end.encode());
660
661 for probe in &keys {
662 let bytes = probe.encode();
663 assert_eq!(
664 contains(&typed_start, &typed_end, probe),
665 contains_bytes(&raw_start, &raw_end, &bytes),
666 "typed and encoded spans disagree\n start = {start:?}\n end \
667 = {end:?}\n key = {probe:?}"
668 );
669 }
670 }
671 }
672 }
673
674 #[test]
675 fn a_prefix_range_contains_exactly_the_keys_its_encoded_form_contains() {
676 let range = IndexEntryKey::key_prefix_range(table(), index(), &[!b'a']);
680 let encoded = range.encode();
681
682 for tail in ["a", "a1", "a3", "aa", "az", "b", "b1", "c1"] {
683 let bound = entry(tail);
684 let TaggedKeyBound::Key(key) = &bound else {
685 unreachable!("entry builds a Key bound");
686 };
687 let bytes = key.encode();
688
689 let typed = contains(&range.start, &range.end, &bound);
690 let raw = contains_bytes(&encoded.start, &encoded.end, &bytes);
691
692 assert_eq!(typed, raw, "typed and encoded containment disagree for tail {tail}");
693 assert_eq!(typed, tail.starts_with('a'), "wrong containment verdict for tail {tail}");
694 }
695 }
696
697 fn contains(start: &Bound<TaggedKeyBound>, end: &Bound<TaggedKeyBound>, probe: &TaggedKeyBound) -> bool {
698 let lower = match start {
699 Bound::Included(bound) => probe >= bound,
700 Bound::Excluded(bound) => probe > bound,
701 Bound::Unbounded => true,
702 };
703 let upper = match end {
704 Bound::Included(bound) => probe <= bound,
705 Bound::Excluded(bound) => probe < bound,
706 Bound::Unbounded => true,
707 };
708 lower && upper
709 }
710
711 fn contains_bytes(start: &Bound<EncodedKey>, end: &Bound<EncodedKey>, probe: &EncodedKey) -> bool {
712 let lower = match start {
713 Bound::Included(key) => probe >= key,
714 Bound::Excluded(key) => probe > key,
715 Bound::Unbounded => true,
716 };
717 let upper = match end {
718 Bound::Included(key) => probe <= key,
719 Bound::Excluded(key) => probe < key,
720 Bound::Unbounded => true,
721 };
722 lower && upper
723 }
724}