Skip to content

Commit 11b0e1b

Browse files
authored
Merge pull request #71 from nrc/key-range
Refactor KvRange
2 parents cc19c91 + 8e0127d commit 11b0e1b

File tree

4 files changed

+178
-162
lines changed

4 files changed

+178
-162
lines changed

src/kv.rs

Lines changed: 142 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
22

3-
use std::ops::{
4-
Bound, Deref, DerefMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
5-
};
3+
use std::cmp::{Eq, PartialEq};
4+
use std::convert::TryFrom;
5+
use std::ops::{Bound, Deref, DerefMut, Range, RangeFrom, RangeInclusive};
66
use std::{fmt, str, u8};
77

88
use crate::{Error, Result};
@@ -68,6 +68,36 @@ impl Key {
6868
pub(crate) fn into_inner(self) -> Vec<u8> {
6969
self.0
7070
}
71+
72+
#[inline]
73+
fn zero_terminated(&self) -> bool {
74+
self.0.last().map(|i| *i == 0).unwrap_or(false)
75+
}
76+
77+
#[inline]
78+
fn push_zero(&mut self) {
79+
self.0.push(0)
80+
}
81+
82+
#[inline]
83+
fn into_lower_bound(mut self) -> Bound<Key> {
84+
if self.zero_terminated() {
85+
self.0.pop().unwrap();
86+
Bound::Excluded(self)
87+
} else {
88+
Bound::Included(self)
89+
}
90+
}
91+
92+
#[inline]
93+
fn into_upper_bound(mut self) -> Bound<Key> {
94+
if self.zero_terminated() {
95+
self.0.pop().unwrap();
96+
Bound::Included(self)
97+
} else {
98+
Bound::Excluded(self)
99+
}
100+
}
71101
}
72102

73103
impl From<Vec<u8>> for Key {
@@ -320,171 +350,180 @@ impl fmt::Debug for KvPair {
320350
}
321351
}
322352

323-
/// A convenience trait for expressing ranges.
353+
/// A struct for expressing ranges. This type is semi-opaque and is not really meant for users to
354+
/// deal with directly. Most functions which operate on ranges will accept any types which
355+
/// implement `Into<BoundRange>`.
324356
///
325357
/// In TiKV, keys are an ordered sequence of bytes. This means we can have ranges over those
326358
/// bytes. Eg `001` is before `010`.
327359
///
328-
/// This trait has implementations for common range types like `a..b`, `a..=b` where `a` and `b`
329-
/// `impl Into<Key>`. You could implement this for your own types.
360+
/// `Into<BoundRange>` has implementations for common range types like `a..b`, `a..=b` where `a` and `b`
361+
/// `impl Into<Key>`. You can implement `Into<BoundRange>` for your own types by using `try_from`.
362+
///
363+
/// Invariant: a range may not be unbounded below.
330364
///
331365
/// ```rust
332-
/// use tikv_client::{KeyRange, Key};
366+
/// use tikv_client::{BoundRange, Key};
333367
/// use std::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive, RangeFrom, RangeFull, Bound};
368+
/// # use std::convert::TryInto;
334369
///
335370
/// let explict_range: Range<Key> = Range { start: Key::from("Rust"), end: Key::from("TiKV") };
336-
/// let from_explict_range = explict_range.into_bounds();
371+
/// let from_explict_range: BoundRange = explict_range.into();
337372
///
338373
/// let range: Range<&str> = "Rust".."TiKV";
339-
/// let from_range = range.into_bounds();
374+
/// let from_range: BoundRange = range.into();
340375
/// assert_eq!(from_explict_range, from_range);
341376
///
342377
/// let range: RangeInclusive<&str> = "Rust"..="TiKV";
343-
/// let from_range = range.into_bounds();
378+
/// let from_range: BoundRange = range.into();
344379
/// assert_eq!(
380+
/// from_range,
345381
/// (Bound::Included(Key::from("Rust")), Bound::Included(Key::from("TiKV"))),
346-
/// from_range
347382
/// );
348383
///
349384
/// let range_from: RangeFrom<&str> = "Rust"..;
350-
/// let from_range_from = range_from.into_bounds();
385+
/// let from_range_from: BoundRange = range_from.into();
351386
/// assert_eq!(
352-
/// (Bound::Included(Key::from("Rust")), Bound::Unbounded),
353387
/// from_range_from,
354-
/// );
355-
///
356-
/// let range_to: RangeTo<&str> = .."TiKV";
357-
/// let from_range_to = range_to.into_bounds();
358-
/// assert_eq!(
359-
/// (Bound::Unbounded, Bound::Excluded(Key::from("TiKV"))),
360-
/// from_range_to,
361-
/// );
362-
///
363-
/// let range_to_inclusive: RangeToInclusive<&str> = ..="TiKV";
364-
/// let from_range_to_inclusive = range_to_inclusive.into_bounds();
365-
/// assert_eq!(
366-
/// (Bound::Unbounded, Bound::Included(Key::from("TiKV"))),
367-
/// from_range_to_inclusive,
368-
/// );
369-
///
370-
/// let range_full: RangeFull = ..;
371-
/// let from_range_full = range_full.into_bounds();
372-
/// assert_eq!(
373-
/// (Bound::Unbounded, Bound::Unbounded),
374-
/// from_range_full
388+
/// (Bound::Included(Key::from("Rust")), Bound::Unbounded),
375389
/// );
376390
/// ```
377391
///
378-
/// **But, you should not need to worry about all this:** Many functions accept a `impl KeyRange`
392+
/// **But, you should not need to worry about all this:** Most functions which operate
393+
/// on ranges will accept any types which implement `Into<BoundRange>`.
379394
/// which means all of the above types can be passed directly to those functions.
380-
pub trait KeyRange: Sized {
381-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>);
395+
#[derive(Clone, Debug, Eq, PartialEq)]
396+
pub struct BoundRange {
397+
from: Bound<Key>,
398+
to: Bound<Key>,
399+
}
400+
401+
impl BoundRange {
402+
/// Create a new BoundRange.
403+
///
404+
/// The caller must ensure that `from` is not `Unbounded`.
405+
fn new(from: Bound<Key>, to: Bound<Key>) -> BoundRange {
406+
// Debug assert because this function is private.
407+
debug_assert!(from != Bound::Unbounded);
408+
BoundRange { from, to }
409+
}
410+
382411
/// Ranges used in scanning TiKV have a particularity to them.
383412
///
384413
/// The **start** of a scan is inclusive, unless appended with an '\0', then it is exclusive.
385414
///
386415
/// The **end** of a scan is exclusive, unless appended with an '\0', then it is inclusive.
387416
///
388417
/// ```rust
389-
/// use tikv_client::{KeyRange, Key};
418+
/// use tikv_client::{BoundRange, Key};
390419
/// // Exclusive
391420
/// let range = "a".."z";
392421
/// assert_eq!(
393-
/// range.into_keys().unwrap(),
394-
/// (Key::from("a"), Some(Key::from("z")))
422+
/// BoundRange::from(range).into_keys(),
423+
/// (Key::from("a"), Some(Key::from("z"))),
395424
/// );
396425
/// // Inclusive
397426
/// let range = "a"..="z";
398427
/// assert_eq!(
399-
/// range.into_keys().unwrap(),
400-
/// (Key::from("a"), Some(Key::from("z\0")))
428+
/// BoundRange::from(range).into_keys(),
429+
/// (Key::from("a"), Some(Key::from("z\0"))),
401430
/// );
402431
/// // Open
403432
/// let range = "a"..;
404433
/// assert_eq!(
405-
/// range.into_keys().unwrap(),
406-
/// (Key::from("a"), None)
434+
/// BoundRange::from(range).into_keys(),
435+
/// (Key::from("a"), None),
407436
/// );
408437
// ```
409-
fn into_keys(self) -> Result<(Key, Option<Key>)> {
410-
range_to_keys(self.into_bounds())
411-
}
412-
}
413-
414-
fn range_to_keys(range: (Bound<Key>, Bound<Key>)) -> Result<(Key, Option<Key>)> {
415-
let start = match range.0 {
416-
Bound::Included(v) => v,
417-
Bound::Excluded(mut v) => {
418-
v.0.push(b"\0"[0]);
419-
v
420-
}
421-
Bound::Unbounded => Err(Error::invalid_key_range())?,
422-
};
423-
let end = match range.1 {
424-
Bound::Included(mut v) => {
425-
v.0.push(b"\0"[0]);
426-
Some(v)
427-
}
428-
Bound::Excluded(v) => Some(v),
429-
Bound::Unbounded => None,
430-
};
431-
Ok((start, end))
432-
}
433-
434-
impl<T: Into<Key>> KeyRange for Range<T> {
435-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
436-
(
437-
Bound::Included(self.start.into()),
438-
Bound::Excluded(self.end.into()),
438+
pub fn into_keys(self) -> (Key, Option<Key>) {
439+
let start = match self.from {
440+
Bound::Included(v) => v,
441+
Bound::Excluded(mut v) => {
442+
v.push_zero();
443+
v
444+
}
445+
Bound::Unbounded => unreachable!(),
446+
};
447+
let end = match self.to {
448+
Bound::Included(mut v) => {
449+
v.push_zero();
450+
Some(v)
451+
}
452+
Bound::Excluded(v) => Some(v),
453+
Bound::Unbounded => None,
454+
};
455+
(start, end)
456+
}
457+
}
458+
459+
impl<T: Into<Key> + Clone> PartialEq<(Bound<T>, Bound<T>)> for BoundRange {
460+
fn eq(&self, other: &(Bound<T>, Bound<T>)) -> bool {
461+
self.from == convert_to_bound_key(other.0.clone())
462+
&& self.to == convert_to_bound_key(other.1.clone())
463+
}
464+
}
465+
466+
impl<T: Into<Key>> From<Range<T>> for BoundRange {
467+
fn from(other: Range<T>) -> BoundRange {
468+
BoundRange::new(
469+
Bound::Included(other.start.into()),
470+
Bound::Excluded(other.end.into()),
439471
)
440472
}
441473
}
442474

443-
impl<T: Into<Key>> KeyRange for RangeFrom<T> {
444-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
445-
(Bound::Included(self.start.into()), Bound::Unbounded)
475+
impl<T: Into<Key>> From<RangeFrom<T>> for BoundRange {
476+
fn from(other: RangeFrom<T>) -> BoundRange {
477+
BoundRange::new(Bound::Included(other.start.into()), Bound::Unbounded)
446478
}
447479
}
448480

449-
impl KeyRange for RangeFull {
450-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
451-
(Bound::Unbounded, Bound::Unbounded)
481+
impl<T: Into<Key>> From<RangeInclusive<T>> for BoundRange {
482+
fn from(other: RangeInclusive<T>) -> BoundRange {
483+
let (start, end) = other.into_inner();
484+
BoundRange::new(Bound::Included(start.into()), Bound::Included(end.into()))
452485
}
453486
}
454487

455-
impl<T: Into<Key>> KeyRange for RangeInclusive<T> {
456-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
457-
let (start, end) = self.into_inner();
458-
(Bound::Included(start.into()), Bound::Included(end.into()))
459-
}
460-
}
488+
impl<T: Into<Key>> From<(T, Option<T>)> for BoundRange {
489+
fn from(other: (T, Option<T>)) -> BoundRange {
490+
let to = match other.1 {
491+
None => Bound::Unbounded,
492+
Some(to) => to.into().into_upper_bound(),
493+
};
461494

462-
impl<T: Into<Key>> KeyRange for RangeTo<T> {
463-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
464-
(Bound::Unbounded, Bound::Excluded(self.end.into()))
495+
BoundRange::new(other.0.into().into_lower_bound(), to)
465496
}
466497
}
467498

468-
impl<T: Into<Key>> KeyRange for RangeToInclusive<T> {
469-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
470-
(Bound::Unbounded, Bound::Included(self.end.into()))
499+
impl<T: Into<Key>> From<(T, T)> for BoundRange {
500+
fn from(other: (T, T)) -> BoundRange {
501+
BoundRange::new(
502+
other.0.into().into_lower_bound(),
503+
other.1.into().into_upper_bound(),
504+
)
471505
}
472506
}
473507

474-
impl<T: Into<Key>> KeyRange for (Bound<T>, Bound<T>) {
475-
fn into_bounds(self) -> (Bound<Key>, Bound<Key>) {
476-
(convert_to_bound_key(self.0), convert_to_bound_key(self.1))
508+
impl<T: Into<Key> + Eq> TryFrom<(Bound<T>, Bound<T>)> for BoundRange {
509+
type Error = Error;
510+
511+
fn try_from(bounds: (Bound<T>, Bound<T>)) -> Result<BoundRange> {
512+
if bounds.0 == Bound::Unbounded {
513+
Err(Error::invalid_key_range())
514+
} else {
515+
Ok(BoundRange::new(
516+
convert_to_bound_key(bounds.0),
517+
convert_to_bound_key(bounds.1),
518+
))
519+
}
477520
}
478521
}
479522

480-
fn convert_to_bound_key<K>(b: Bound<K>) -> Bound<Key>
481-
where
482-
K: Into<Key>,
483-
{
484-
use std::ops::Bound::*;
523+
fn convert_to_bound_key<K: Into<Key>>(b: Bound<K>) -> Bound<Key> {
485524
match b {
486-
Included(k) => Included(k.into()),
487-
Excluded(k) => Excluded(k.into()),
488-
Unbounded => Unbounded,
525+
Bound::Included(k) => Bound::Included(k.into()),
526+
Bound::Excluded(k) => Bound::Excluded(k.into()),
527+
Bound::Unbounded => Bound::Unbounded,
489528
}
490529
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,4 @@ pub use crate::errors::ErrorKind;
9191
#[doc(inline)]
9292
pub use crate::errors::Result;
9393
#[doc(inline)]
94-
pub use crate::kv::{Key, KeyRange, KvPair, Value};
94+
pub use crate::kv::{BoundRange, Key, KvPair, Value};

0 commit comments

Comments
 (0)