Skip to content

Commit 571eb1c

Browse files
yjh0502bluejekyll
authored andcommitted
add Subspace, #36
1 parent 51e3eb4 commit 571eb1c

File tree

5 files changed

+180
-16
lines changed

5 files changed

+180
-16
lines changed

foundationdb/src/bin/bindingtester.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl Instr {
191191

192192
let code = match cmd {
193193
"PUSH" => {
194-
let data = Single::encode_to_vec(&tup.0[1]).unwrap();
194+
let data = Single::encode_to_vec(&tup.0[1]);
195195
Push(data)
196196
}
197197
"DUP" => Dup,
@@ -280,13 +280,13 @@ impl StackItem {
280280

281281
//TODO: wait
282282
match self.fut.unwrap().wait() {
283-
Ok((_trx, data)) => Single::encode_to_vec(&data).unwrap(),
283+
Ok((_trx, data)) => Single::encode_to_vec(&data),
284284
Err(e) => {
285285
let code = format!("{}", e.code());
286286
let tup = (b"ERROR".to_vec(), code.into_bytes());
287287
debug!("ERROR: {:?}", e);
288-
let bytes = Tuple::encode_to_vec(&tup).expect("failed to encode");
289-
Single::encode_to_vec(&bytes).unwrap()
288+
let bytes = Tuple::encode_to_vec(&tup);
289+
Single::encode_to_vec(&bytes)
290290
}
291291
}
292292
}
@@ -406,7 +406,7 @@ impl StackMachine {
406406
where
407407
S: Single,
408408
{
409-
let data = Single::encode_to_vec(s).expect("failed to encode");
409+
let data = Single::encode_to_vec(s);
410410
self.push(number, data);
411411
}
412412

@@ -723,7 +723,7 @@ impl StackMachine {
723723

724724
while !data.is_empty() {
725725
let (val, offset): (SingleValue, _) = Single::decode(data).unwrap();
726-
let bytes = Single::encode_to_vec(&val).unwrap();
726+
let bytes = Single::encode_to_vec(&val);
727727
self.push_single(number, &bytes);
728728
data = &data[offset..];
729729
}

foundationdb/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub mod network;
129129
/// Generated configuration types for use with the various `set_option` functions
130130
#[allow(missing_docs)]
131131
pub mod options;
132+
pub mod subspace;
132133
pub mod transaction;
133134
#[allow(missing_docs)]
134135
pub mod tuple;

foundationdb/src/subspace.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Copyright 2018 foundationdb-rs developers, https://github.com/bluejekyll/foundationdb-rs/graphs/contributors
2+
// Copyright 2013-2018 Apple, Inc and the FoundationDB project authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5+
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6+
// http://opensource.org/licenses/MIT>, at your option. This file may not be
7+
// copied, modified, or distributed except according to those terms.
8+
9+
//! subspace provides a convenient way to use FoundationDB tuples to define namespaces for
10+
//! different categories of data. The namespace is specified by a prefix tuple which is prepended
11+
//! to all tuples packed by the subspace. When unpacking a key with the subspace, the prefix tuple
12+
//! will be removed from the result.
13+
//!
14+
//! As a best practice, API clients should use at least one subspace for application data. For
15+
//! general guidance on subspace usage, see the Subspaces section of the Developer Guide
16+
//! (https://apple.github.io/foundationdb/developer-guide.html#subspaces).
17+
18+
use tuple::{Tuple, TupleError};
19+
20+
/// Subspace represents a well-defined region of keyspace in a FoundationDB database.
21+
#[derive(Debug, Clone)]
22+
pub struct Subspace {
23+
prefix: Vec<u8>,
24+
}
25+
26+
impl Subspace {
27+
/// `all` returns the Subspace corresponding to all keys in a FoundationDB database.
28+
pub fn all() -> Subspace {
29+
Self { prefix: Vec::new() }
30+
}
31+
32+
/// `from_bytes` returns a new Subspace from the provided bytes.
33+
pub fn from_bytes(bytes: &[u8]) -> Self {
34+
Self {
35+
prefix: bytes.to_vec(),
36+
}
37+
}
38+
39+
/// `from_bytes` returns a new Subspace from the provided Tuple.
40+
pub fn new<T: Tuple>(t: &T) -> Self {
41+
let prefix = Tuple::encode_to_vec(t);
42+
Self { prefix }
43+
}
44+
45+
/// `subspace` returns a new Subspace whose prefix extends this Subspace with a given tuple.
46+
pub fn subspace<T: Tuple>(&self, t: &T) -> Self {
47+
Self {
48+
prefix: self.pack(t),
49+
}
50+
}
51+
52+
/// `bytes` returns the literal bytes of the prefix of this Subspace.
53+
pub fn bytes(&self) -> &[u8] {
54+
self.prefix.as_slice()
55+
}
56+
57+
/// `pack` returns the key encoding the specified Tuple with the prefix of this Subspace
58+
/// prepended.
59+
pub fn pack<T: Tuple>(&self, t: &T) -> Vec<u8> {
60+
let mut packed = Tuple::encode_to_vec(t);
61+
let mut out = Vec::with_capacity(self.prefix.len() + packed.len());
62+
out.extend_from_slice(&self.prefix);
63+
out.append(&mut packed);
64+
out
65+
}
66+
67+
/// `unpack` returns the Tuple encoded by the given key with the prefix of this Subspace
68+
/// removed. `unpack` will return an error if the key is not in this Subspace or does not
69+
/// encode a well-formed Tuple.
70+
pub fn unpack<T: Tuple>(&self, key: &[u8]) -> Result<T, TupleError> {
71+
if !self.contains(key) {
72+
return Err(TupleError::InvalidData);
73+
}
74+
let key = &key[self.prefix.len()..];
75+
Tuple::decode(&key)
76+
}
77+
78+
/// `contains` returns true if the provided key starts with the prefix of this Subspace,
79+
/// indicating that the Subspace logically contains the key.
80+
pub fn contains(&self, key: &[u8]) -> bool {
81+
key.starts_with(&self.prefix)
82+
}
83+
84+
/// `range` returns first and last key of given Subspace
85+
pub fn range(&self) -> (Vec<u8>, Vec<u8>) {
86+
let mut begin = self.prefix.clone();
87+
begin.push(0x00);
88+
89+
let mut end = self.prefix.clone();
90+
end.push(0xff);
91+
92+
(begin, end)
93+
}
94+
}
95+
96+
#[cfg(test)]
97+
mod tests {
98+
use super::*;
99+
use tuple;
100+
101+
#[test]
102+
fn sub() {
103+
let ss0 = Subspace::new(&(1,));
104+
let ss1 = ss0.subspace(&(2,));
105+
106+
let ss2 = Subspace::new(&(1, 2));
107+
108+
assert_eq!(ss1.bytes(), ss2.bytes());
109+
}
110+
111+
#[test]
112+
fn pack_unpack() {
113+
let ss0 = Subspace::new(&(1,));
114+
let tup = (2, 3);
115+
116+
let packed = ss0.pack(&tup);
117+
let expected = Tuple::encode_to_vec(&(1, 2, 3));
118+
assert_eq!(expected, packed);
119+
120+
let tup_unpack: (i64, i64) = ss0.unpack(&packed).unwrap();
121+
assert_eq!(tup, tup_unpack);
122+
123+
assert!(ss0.unpack::<(i64, i64, i64)>(&packed).is_err());
124+
}
125+
126+
#[test]
127+
fn contains() {
128+
let ss0 = Subspace::new(&(1,));
129+
let ss1 = Subspace::new(&(2,));
130+
let tup = (2, 3);
131+
132+
assert!(ss0.contains(&ss0.pack(&tup)));
133+
assert!(!ss1.contains(&ss0.pack(&tup)));
134+
}
135+
136+
#[test]
137+
fn unpack_malformed() {
138+
let ss0 = Subspace::new(&((),));
139+
140+
let malformed = {
141+
let mut v = ss0.bytes().to_vec();
142+
v.push(0xff);
143+
v
144+
};
145+
146+
assert!(ss0.unpack::<tuple::TupleValue>(&malformed).is_err());
147+
}
148+
149+
#[test]
150+
fn range() {
151+
let ss = Subspace::new(&(1,));
152+
let tup = (2, 3);
153+
let packed = ss.pack(&tup);
154+
155+
let (begin, end) = ss.range();
156+
assert!(packed >= begin && packed <= end);
157+
}
158+
}

foundationdb/src/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl RangeOptionBuilder {
102102
where
103103
T: tuple::Tuple,
104104
{
105-
let bytes = tuple::Tuple::encode_to_vec(tup).expect("failed to encode tuple");
105+
let bytes = tuple::Tuple::encode_to_vec(tup);
106106
let mut begin = bytes.clone();
107107
begin.push(0x00);
108108

foundationdb/src/tuple.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,11 @@ impl SingleType for u8 {
9090

9191
pub trait Single: Sized {
9292
fn encode<W: Write>(&self, _w: &mut W) -> std::io::Result<()>;
93-
fn encode_to_vec(&self) -> std::io::Result<Vec<u8>> {
93+
fn encode_to_vec(&self) -> Vec<u8> {
9494
let mut v = Vec::new();
95-
self.encode(&mut v)?;
96-
Ok(v)
95+
// `self.encode` should not fail because undering `Write` does not return error.
96+
self.encode(&mut v).unwrap();
97+
v
9798
}
9899

99100
fn decode(buf: &[u8]) -> Result<(Self, usize)>;
@@ -395,6 +396,9 @@ impl Single for i64 {
395396
}
396397

397398
fn decode(buf: &[u8]) -> Result<(Self, usize)> {
399+
if buf.is_empty() {
400+
return Err(TupleError::EOF);
401+
}
398402
let header = buf[0];
399403
if header < 0x0c || header > 0x1c {
400404
return Err(TupleError::InvalidType { value: header });
@@ -431,10 +435,10 @@ impl Single for i64 {
431435

432436
pub trait Tuple: Sized {
433437
fn encode<W: Write>(&self, _w: &mut W) -> std::io::Result<()>;
434-
fn encode_to_vec(&self) -> std::io::Result<Vec<u8>> {
438+
fn encode_to_vec(&self) -> Vec<u8> {
435439
let mut v = Vec::new();
436-
self.encode(&mut v)?;
437-
Ok(v)
440+
self.encode(&mut v).unwrap();
441+
v
438442
}
439443

440444
fn decode(buf: &[u8]) -> Result<Self>;
@@ -560,7 +564,8 @@ impl Single for SingleValue {
560564
let (v, offset) = Single::decode(buf)?;
561565
Ok((SingleValue::Int(v), offset))
562566
} else {
563-
unimplemented!("unknown code: 0x{:02x}", val);
567+
//TODO: Versionstamp, ...
568+
Err(TupleError::InvalidData)
564569
}
565570
}
566571
}
@@ -599,7 +604,7 @@ mod tests {
599604
S: Single + std::fmt::Debug + PartialEq,
600605
{
601606
assert_eq!(val, Single::decode_full(buf).unwrap());
602-
assert_eq!(buf, Single::encode_to_vec(&val).unwrap().as_slice());
607+
assert_eq!(buf, Single::encode_to_vec(&val).as_slice());
603608
}
604609

605610
#[test]
@@ -713,7 +718,7 @@ mod tests {
713718

714719
assert_eq!(
715720
&[2, 104, 101, 108, 108, 111, 0, 1, 119, 111, 114, 108, 100, 0],
716-
Tuple::encode_to_vec(&tup).unwrap().as_slice()
721+
Tuple::encode_to_vec(&tup).as_slice()
717722
);
718723
}
719724

0 commit comments

Comments
 (0)