add key abstraction and prepare for MVCC (#28)
* add key abstraction and prepare for MVCC Signed-off-by: Alex Chi <iskyzh@gmail.com> * a little bit type exercise Signed-off-by: Alex Chi <iskyzh@gmail.com> * refactor tests Signed-off-by: Alex Chi <iskyzh@gmail.com> * fix clippy warnings Signed-off-by: Alex Chi <iskyzh@gmail.com> * refactor starter code Signed-off-by: Alex Chi <iskyzh@gmail.com> * final touch docs Signed-off-by: Alex Chi <iskyzh@gmail.com> --------- Signed-off-by: Alex Chi <iskyzh@gmail.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
use bytes::BufMut;
|
||||
|
||||
use crate::key::{KeySlice, KeyVec};
|
||||
|
||||
use super::{Block, SIZEOF_U16};
|
||||
|
||||
/// Builds a block.
|
||||
@@ -11,16 +13,16 @@ pub struct BlockBuilder {
|
||||
/// The expected block size.
|
||||
block_size: usize,
|
||||
/// The first key in the block
|
||||
first_key: Vec<u8>,
|
||||
first_key: KeyVec,
|
||||
}
|
||||
|
||||
fn compute_overlap(first_key: &[u8], key: &[u8]) -> usize {
|
||||
fn compute_overlap(first_key: KeySlice, key: KeySlice) -> usize {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
if i >= first_key.len() || i >= key.len() {
|
||||
break;
|
||||
}
|
||||
if first_key[i] != key[i] {
|
||||
if first_key.raw_ref()[i] != key.raw_ref()[i] {
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
@@ -35,7 +37,7 @@ impl BlockBuilder {
|
||||
offsets: Vec::new(),
|
||||
data: Vec::new(),
|
||||
block_size,
|
||||
first_key: Vec::new(),
|
||||
first_key: KeyVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +48,7 @@ impl BlockBuilder {
|
||||
|
||||
/// Adds a key-value pair to the block. Returns false when the block is full.
|
||||
#[must_use]
|
||||
pub fn add(&mut self, key: &[u8], value: &[u8]) -> bool {
|
||||
pub fn add(&mut self, key: KeySlice, value: &[u8]) -> bool {
|
||||
assert!(!key.is_empty(), "key must not be empty");
|
||||
if self.estimated_size() + key.len() + value.len() + SIZEOF_U16 * 3 /* key_len, value_len and offset */ > self.block_size
|
||||
&& !self.is_empty()
|
||||
@@ -55,20 +57,20 @@ impl BlockBuilder {
|
||||
}
|
||||
// Add the offset of the data into the offset array.
|
||||
self.offsets.push(self.data.len() as u16);
|
||||
let overlap = compute_overlap(&self.first_key, key);
|
||||
let overlap = compute_overlap(self.first_key.as_key_slice(), key);
|
||||
// Encode key overlap.
|
||||
self.data.put_u16(overlap as u16);
|
||||
// Encode key length.
|
||||
self.data.put_u16((key.len() - overlap) as u16);
|
||||
// Encode key content.
|
||||
self.data.put(&key[overlap..]);
|
||||
self.data.put(&key.raw_ref()[overlap..]);
|
||||
// Encode value length.
|
||||
self.data.put_u16(value.len() as u16);
|
||||
// Encode value content.
|
||||
self.data.put(value);
|
||||
|
||||
if self.first_key.is_empty() {
|
||||
self.first_key = key.to_vec();
|
||||
self.first_key = key.to_key_vec();
|
||||
}
|
||||
|
||||
true
|
||||
|
||||
@@ -2,7 +2,10 @@ use std::sync::Arc;
|
||||
|
||||
use bytes::Buf;
|
||||
|
||||
use crate::block::SIZEOF_U16;
|
||||
use crate::{
|
||||
block::SIZEOF_U16,
|
||||
key::{KeySlice, KeyVec},
|
||||
};
|
||||
|
||||
use super::Block;
|
||||
|
||||
@@ -11,22 +14,22 @@ pub struct BlockIterator {
|
||||
/// reference to the block
|
||||
block: Arc<Block>,
|
||||
/// the current key at the iterator position
|
||||
key: Vec<u8>,
|
||||
key: KeyVec,
|
||||
/// the value range from the block
|
||||
value_range: (usize, usize),
|
||||
/// the current index at the iterator position
|
||||
idx: usize,
|
||||
/// the first key in the block
|
||||
first_key: Vec<u8>,
|
||||
first_key: KeyVec,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
fn get_first_key(&self) -> Vec<u8> {
|
||||
fn get_first_key(&self) -> KeyVec {
|
||||
let mut buf = &self.data[..];
|
||||
buf.get_u16();
|
||||
let key_len = buf.get_u16();
|
||||
let key = &buf[..key_len as usize];
|
||||
key.to_vec()
|
||||
KeyVec::from_vec(key.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +38,7 @@ impl BlockIterator {
|
||||
Self {
|
||||
first_key: block.get_first_key(),
|
||||
block,
|
||||
key: Vec::new(),
|
||||
key: KeyVec::new(),
|
||||
value_range: (0, 0),
|
||||
idx: 0,
|
||||
}
|
||||
@@ -49,16 +52,16 @@ impl BlockIterator {
|
||||
}
|
||||
|
||||
/// Creates a block iterator and seek to the first key that >= `key`.
|
||||
pub fn create_and_seek_to_key(block: Arc<Block>, key: &[u8]) -> Self {
|
||||
pub fn create_and_seek_to_key(block: Arc<Block>, key: KeySlice) -> Self {
|
||||
let mut iter = Self::new(block);
|
||||
iter.seek_to_key(key);
|
||||
iter
|
||||
}
|
||||
|
||||
/// Returns the key of the current entry.
|
||||
pub fn key(&self) -> &[u8] {
|
||||
pub fn key(&self) -> KeySlice {
|
||||
debug_assert!(!self.key.is_empty(), "invalid iterator");
|
||||
&self.key
|
||||
self.key.as_key_slice()
|
||||
}
|
||||
|
||||
/// Returns the value of the current entry.
|
||||
@@ -103,11 +106,11 @@ impl BlockIterator {
|
||||
// we don't need to manually advance it
|
||||
let overlap_len = entry.get_u16() as usize;
|
||||
let key_len = entry.get_u16() as usize;
|
||||
let key = entry[..key_len].to_vec();
|
||||
entry.advance(key_len);
|
||||
let key = &entry[..key_len];
|
||||
self.key.clear();
|
||||
self.key.extend(&self.first_key[..overlap_len]);
|
||||
self.key.extend(key);
|
||||
self.key.append(&self.first_key.raw_ref()[..overlap_len]);
|
||||
self.key.append(key);
|
||||
entry.advance(key_len);
|
||||
let value_len = entry.get_u16() as usize;
|
||||
let value_offset_begin = offset + SIZEOF_U16 + SIZEOF_U16 + key_len + SIZEOF_U16;
|
||||
let value_offset_end = value_offset_begin + value_len;
|
||||
@@ -116,14 +119,14 @@ impl BlockIterator {
|
||||
}
|
||||
|
||||
/// Seek to the first key that is >= `key`.
|
||||
pub fn seek_to_key(&mut self, key: &[u8]) {
|
||||
pub fn seek_to_key(&mut self, key: KeySlice) {
|
||||
let mut low = 0;
|
||||
let mut high = self.block.offsets.len();
|
||||
while low < high {
|
||||
let mid = low + (high - low) / 2;
|
||||
self.seek_to(mid);
|
||||
assert!(self.is_valid());
|
||||
match self.key().cmp(key) {
|
||||
match self.key().cmp(&key) {
|
||||
std::cmp::Ordering::Less => low = mid + 1,
|
||||
std::cmp::Ordering::Greater => high = mid,
|
||||
std::cmp::Ordering::Equal => return,
|
||||
|
||||
Reference in New Issue
Block a user