Skip to content

Commit

Permalink
Add InternalKey and some test on dbformat (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunchao authored Jan 7, 2018
1 parent d401cd2 commit f60104d
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 26 deletions.
142 changes: 127 additions & 15 deletions src/dbformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,19 @@ pub type SequenceNumber = u64;
/// The last eight bits are reserved for value type.
pub const MAX_SEQUENCE_NUMBER: u64 = (0x1u64 << 56) - 1;

#[derive(PartialEq, PartialOrd)]
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
pub enum ValueType {
DELETION = 0x0,
VALUE = 0x1
}

impl From<u8> for ValueType {
fn from(v: u8) -> ValueType {
impl TryFrom<u8> for ValueType {
type Error = super::result::Error;
fn try_from(v: u8) -> Result<ValueType> {
match v {
0x00 => ValueType::DELETION,
0x01 => ValueType::VALUE,
_ => panic!("Undefined value for ValueType: {}", v)
0x00 => Ok(ValueType::DELETION),
0x01 => Ok(ValueType::VALUE),
_ => LEVELDB_ERR!(InvalidArgument, "undefined value for ValueType")
}
}
}
Expand All @@ -53,7 +54,6 @@ impl From<u8> for ValueType {
// `ValueType`, not the lowest).
pub const VALUE_TYPE_FOR_SEEK: ValueType = ValueType::VALUE;

#[allow(dead_code)]
pub struct LookupKey {
// Layout:
// key_length varint32 <- data
Expand All @@ -66,8 +66,8 @@ pub struct LookupKey {

// These two fields are never used: they are here to keep the
// `data` pointer alive.
space: [u8; 200], // for short keys - allocated on stack.
vec: Option<Vec<u8>> // keep heap allocated memory alive.
_space: [u8; 200], // for short keys - allocated on stack.
_vec: Option<Vec<u8>> // keep heap allocated memory alive.
}

impl LookupKey {
Expand Down Expand Up @@ -95,8 +95,8 @@ impl LookupKey {
data: ptr,
kstart: kstart,
size: offset,
space: space,
vec: vec
_space: space,
_vec: vec
}
}

Expand Down Expand Up @@ -143,6 +143,10 @@ impl InternalKeyComparator {
}
}

pub fn compare_key(&self, a: &InternalKey, b: &InternalKey) -> Ordering {
self.compare(&a.encode(), &b.encode())
}

pub fn user_comparator(&self) -> &Rc<Comparator<Slice>> {
&self.user_comparator
}
Expand Down Expand Up @@ -171,6 +175,55 @@ impl Comparator<Slice> for InternalKeyComparator {
}
}

pub struct InternalKey {
rep: Vec<u8>
}

impl InternalKey {
pub fn new(user_key: Slice, s: SequenceNumber, t: ValueType) -> Self {
let mut v = Vec::new();
let k = ParsedInternalKey::new(user_key, s, t);
append_internal_key(&mut v, &k);
Self {
rep: v
}
}

pub fn encode(&self) -> Slice {
Slice::from(&self.rep[..])
}

pub fn decode_from(&mut self, s: &Slice) {
self.rep.clear();
self.rep.extend_from_slice(&s.data());
}

pub fn user_key(&self) -> Slice {
let s = Slice::from(&self.rep[..]);
extract_user_key(&s)
}

pub fn set_from(&mut self, p: &ParsedInternalKey) {
self.rep.clear();
append_internal_key(&mut self.rep, p);
}

pub fn clear(&mut self) {
self.rep.clear();
}
}

fn append_internal_key(result: &mut Vec<u8>, key: &ParsedInternalKey) {
result.extend_from_slice(&key.user_key.data());
let key_len = result.len();
unsafe {
result.reserve(8);
result.set_len(key_len + 8);
}
coding::encode_fixed_64(
&mut result[key_len..], pack_sequence_and_type(key.seqno, key.value_type));
}

fn extract_user_key(internal_key: &Slice) -> Slice {
assert!(internal_key.size() >= 8);
Slice::new(internal_key.raw_data(), internal_key.size() - 8)
Expand Down Expand Up @@ -200,14 +253,73 @@ impl ParsedInternalKey {
impl<'a> TryFrom<&'a Slice> for ParsedInternalKey {
type Error = super::result::Error;

/// Attempt to parse an internal key from `internal_key`. On success, return `Some`
/// with the parsed data. Otherwise, return `None`.
fn try_from(s: &Slice) -> Result<ParsedInternalKey> {
let n = s.size();
if n < 8 {
return LEVELDB_ERR!(Corruption);
return LEVELDB_ERR!(InvalidArgument, "Invalid Slice size");
}
let num = coding::decode_fixed_64(&s.data()[n-8..]);
let c: u8 = (num & 0xff) as u8;
Ok(ParsedInternalKey::new(
Slice::new(s.raw_data(), n - 8), num >> 8, ValueType::from(c)))
let vt = ValueType::try_from((num & 0xff) as u8)?;
Ok(ParsedInternalKey::new(Slice::new(s.raw_data(), n - 8), num >> 8, vt))
}
}

#[cfg(test)]
mod tests {
use super::*;

fn ikey(user_key: &'static str, seq: u64, vt: ValueType) -> Vec<u8> {
let mut encoded = Vec::new();
let parsed_key = ParsedInternalKey::new(Slice::from(user_key), seq, vt);
append_internal_key(&mut encoded, &parsed_key);
encoded
}

fn test_key(key: &'static str, seq: u64, vt: ValueType) {
let encoded = ikey(key, seq, vt);
let input = Slice::from(&encoded[..]);
let decoded = ParsedInternalKey::try_from(&input).expect("try_from() should be OK");
assert_eq!(key, decoded.user_key.as_str());
assert_eq!(seq, decoded.seqno);
assert_eq!(vt, decoded.value_type);
assert!(ParsedInternalKey::try_from(&Slice::from("bar")).is_err());
}

#[test]
fn lookup_key() {
let short_key = Slice::from("hello");
let mut key = LookupKey::new(&short_key, 0);
let mut v = [0; 4];
let len = coding::encode_varint_32(&mut v, short_key.size() as u32);
assert_eq!(len, key.kstart);
assert_eq!(
short_key,
Slice::new(unsafe { key.data.offset(len as isize) }, short_key.size()));
assert_eq!(len + short_key.size() + 8, key.size);
assert_eq!(None, key._vec);

let v = (0..256).map(|_| 'a' as u8).collect::<Vec<u8>>();
let long_key = Slice::from(&v[..]);
key = LookupKey::new(&long_key, 1);
assert!(key._vec.is_some());
}

#[test]
fn internal_key_encode_decode() {
let keys = ["", "k", "hello", "longggggggggggggggggggggg"];
let seq = [
1, 2, 3,
(1u64 << 8) - 1, 1u64 << 8, (1u64 << 8) + 1,
(1u64 << 16) - 1, 1u64 << 16, (1u64 << 16) + 1,
(1u64 << 32) - 1, 1u64 << 32, (1u64 << 32) + 1
];
for k in 0..keys.len() {
for s in 0..seq.len() {
test_key(keys[k], seq[s], ValueType::VALUE);
test_key("hello", 1, ValueType::DELETION);
}
}
}
}
3 changes: 2 additions & 1 deletion src/memtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::{mem, slice};
use std::rc::Rc;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::convert::TryFrom;

use super::skiplist::{SkipList, SkipListIterator};
use dbformat::{SequenceNumber, ValueType, LookupKey, InternalKeyComparator};
Expand Down Expand Up @@ -131,7 +132,7 @@ impl MemTable {
&Slice::from(user_key), &key.user_key()) == Ordering::Equal {
// Correct user key
let tag = coding::decode_fixed_64(&entry[(key_length-8)..key_length]);
match ValueType::from((tag & 0xff) as u8) {
match ValueType::try_from((tag & 0xff) as u8).expect("invalid tag") {
ValueType::VALUE => {
let v = get_length_prefixed_slice(&entry[key_length..]);
return Some(Ok(get_string_from_slice(&v)))
Expand Down
22 changes: 13 additions & 9 deletions src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ use std::ops::Index;

use util::bit;

/// Just like Rust's slice, except there's no borrowing.
/// Instead, the user needs to guarantee that
/// the instances of this struct should not live longer than
/// the memory that `data` points to.
#[derive(Clone)]
/// Just like Rust's slice, except there's no borrowing. Instead, the user needs to
/// guarantee that the instances of this struct should not live longer than the memory
/// that `data` points to.
#[derive(Clone, Debug)]
pub struct Slice {
data: *const u8,
size: usize
Expand All @@ -34,10 +33,7 @@ pub struct Slice {
impl Slice {
/// Create an empty slice
pub fn new_empty() -> Self {
Self {
data: ptr::null(),
size: 0
}
Self::new(ptr::null(), 0)
}

/// Create a slice that refers to `d`
Expand Down Expand Up @@ -158,3 +154,11 @@ impl From<String> for Slice {
Slice::new(s.as_ptr(), s.len())
}
}

impl PartialEq for Slice {
fn eq(&self, other: &Slice) -> bool {
self.compare(other) == Ordering::Equal
}
}

impl Eq for Slice { }
3 changes: 2 additions & 1 deletion src/write_batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// under the License.

use std::rc::Rc;
use std::convert::TryFrom;

use util::coding;
use slice::Slice;
Expand Down Expand Up @@ -101,7 +102,7 @@ impl WriteBatch {
found += 1;
let tag: u8 = input[0];
input.remove_prefix(1);
match ValueType::from(tag) {
match ValueType::try_from(tag).expect("invalid tag") {
ValueType::VALUE => {
let key = coding::decode_length_prefixed_slice(&mut input);
let value = coding::decode_length_prefixed_slice(&mut input);
Expand Down

0 comments on commit f60104d

Please sign in to comment.