Skip to content

Commit 9b72d47

Browse files
committed
Refactor KvRange
The end result is a concrete type `BoundRange` and using `Into<BoundRange>` where functions previously took `KeyRange`. This refactoring means that errors with invalid ranges are found earlier (in fact, they are mostly prevented statically, where they can still occur, they do so when ranges are created, not when used) and there is less conversion between range types in some cases. There is less `Result` handling, and we use a named struct instead of a tuple for ranges internally. Signed-off-by: Nick Cameron <nrc@ncameron.org>
1 parent aa3e1af commit 9b72d47

File tree

4 files changed

+177
-162
lines changed

4 files changed

+177
-162
lines changed

src/kv.rs

Lines changed: 141 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,179 @@ 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:** Many functions accept a `impl Into<BoundRange>`
379393
/// 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>);
394+
#[derive(Clone, Debug, Eq, PartialEq)]
395+
pub struct BoundRange {
396+
from: Bound<Key>,
397+
to: Bound<Key>,
398+
}
399+
400+
impl BoundRange {
401+
/// Create a new BoundRange.
402+
///
403+
/// The caller must ensure that `from` is not `Unbounded`.
404+
fn new(from: Bound<Key>, to: Bound<Key>) -> BoundRange {
405+
// Debug assert because this function is private.
406+
debug_assert!(from != Bound::Unbounded);
407+
BoundRange { from, to }
408+
}
409+
382410
/// Ranges used in scanning TiKV have a particularity to them.
383411
///
384412
/// The **start** of a scan is inclusive, unless appended with an '\0', then it is exclusive.
385413
///
386414
/// The **end** of a scan is exclusive, unless appended with an '\0', then it is inclusive.
387415
///
388416
/// ```rust
389-
/// use tikv_client::{KeyRange, Key};
417+
/// use tikv_client::{BoundRange, Key};
390418
/// // Exclusive
391419
/// let range = "a".."z";
392420
/// assert_eq!(
393-
/// range.into_keys().unwrap(),
394-
/// (Key::from("a"), Some(Key::from("z")))
421+
/// BoundRange::from(range).into_keys(),
422+
/// (Key::from("a"), Some(Key::from("z"))),
395423
/// );
396424
/// // Inclusive
397425
/// let range = "a"..="z";
398426
/// assert_eq!(
399-
/// range.into_keys().unwrap(),
400-
/// (Key::from("a"), Some(Key::from("z\0")))
427+
/// BoundRange::from(range).into_keys(),
428+
/// (Key::from("a"), Some(Key::from("z\0"))),
401429
/// );
402430
/// // Open
403431
/// let range = "a"..;
404432
/// assert_eq!(
405-
/// range.into_keys().unwrap(),
406-
/// (Key::from("a"), None)
433+
/// BoundRange::from(range).into_keys(),
434+
/// (Key::from("a"), None),
407435
/// );
408436
// ```
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()),
437+
pub fn into_keys(self) -> (Key, Option<Key>) {
438+
let start = match self.from {
439+
Bound::Included(v) => v,
440+
Bound::Excluded(mut v) => {
441+
v.push_zero();
442+
v
443+
}
444+
Bound::Unbounded => unreachable!(),
445+
};
446+
let end = match self.to {
447+
Bound::Included(mut v) => {
448+
v.push_zero();
449+
Some(v)
450+
}
451+
Bound::Excluded(v) => Some(v),
452+
Bound::Unbounded => None,
453+
};
454+
(start, end)
455+
}
456+
}
457+
458+
impl<T: Into<Key> + Clone> PartialEq<(Bound<T>, Bound<T>)> for BoundRange {
459+
fn eq(&self, other: &(Bound<T>, Bound<T>)) -> bool {
460+
self.from == convert_to_bound_key(other.0.clone())
461+
&& self.to == convert_to_bound_key(other.1.clone())
462+
}
463+
}
464+
465+
impl<T: Into<Key>> From<Range<T>> for BoundRange {
466+
fn from(other: Range<T>) -> BoundRange {
467+
BoundRange::new(
468+
Bound::Included(other.start.into()),
469+
Bound::Excluded(other.end.into()),
439470
)
440471
}
441472
}
442473

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)
474+
impl<T: Into<Key>> From<RangeFrom<T>> for BoundRange {
475+
fn from(other: RangeFrom<T>) -> BoundRange {
476+
BoundRange::new(Bound::Included(other.start.into()), Bound::Unbounded)
446477
}
447478
}
448479

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

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-
}
487+
impl<T: Into<Key>> From<(T, Option<T>)> for BoundRange {
488+
fn from(other: (T, Option<T>)) -> BoundRange {
489+
let to = match other.1 {
490+
None => Bound::Unbounded,
491+
Some(to) => to.into().into_upper_bound(),
492+
};
461493

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()))
494+
BoundRange::new(other.0.into().into_lower_bound(), to)
465495
}
466496
}
467497

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()))
498+
impl<T: Into<Key>> From<(T, T)> for BoundRange {
499+
fn from(other: (T, T)) -> BoundRange {
500+
BoundRange::new(
501+
other.0.into().into_lower_bound(),
502+
other.1.into().into_upper_bound(),
503+
)
471504
}
472505
}
473506

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))
507+
impl<T: Into<Key> + Eq> TryFrom<(Bound<T>, Bound<T>)> for BoundRange {
508+
type Error = Error;
509+
510+
fn try_from(bounds: (Bound<T>, Bound<T>)) -> Result<BoundRange> {
511+
if bounds.0 == Bound::Unbounded {
512+
Err(Error::invalid_key_range())
513+
} else {
514+
Ok(BoundRange::new(
515+
convert_to_bound_key(bounds.0),
516+
convert_to_bound_key(bounds.1),
517+
))
518+
}
477519
}
478520
}
479521

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

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)