use integer_encoding::{VarInt, VarIntWriter};
use crate::error::{Error, Result};
use crate::vlog::{ValuePointer, VALUE_POINTER_SIZE};
use crate::{InternalKeyKind, Key, Value};
pub(crate) const MAX_BATCH_SIZE: u64 = 1 << 32;
pub(crate) const BATCH_VERSION: u8 = 1;
#[derive(Debug, Clone)]
pub(crate) struct BatchEntry {
pub kind: InternalKeyKind,
pub key: Key,
pub value: Option<Value>,
pub timestamp: u64,
}
#[derive(Debug, Clone)]
pub(crate) struct Batch {
pub(crate) version: u8,
pub(crate) entries: Vec<BatchEntry>,
pub(crate) valueptrs: Vec<Option<ValuePointer>>,
pub(crate) starting_seq_num: u64, pub(crate) size: u64, }
impl Default for Batch {
fn default() -> Self {
Self::new(0)
}
}
impl Batch {
pub(crate) fn new(starting_seq_num: u64) -> Self {
Self {
entries: Vec::new(),
valueptrs: Vec::new(),
version: BATCH_VERSION,
starting_seq_num,
size: 0,
}
}
pub(crate) fn grow(&mut self, record_size: u64) -> Result<()> {
if self.size + record_size > MAX_BATCH_SIZE {
return Err(Error::BatchTooLarge);
}
self.size += record_size;
self.entries.reserve(1);
self.valueptrs.reserve(1);
Ok(())
}
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut encoded = Vec::new();
encoded.push(self.version);
encoded.write_varint(self.starting_seq_num)?;
encoded.write_varint(self.entries.len() as u32)?;
for entry in &self.entries {
encoded.push(entry.kind as u8);
encoded.write_varint(entry.key.len() as u64)?;
encoded.extend_from_slice(&entry.key);
let value_len = entry.value.as_ref().map_or(0, |v| v.len());
encoded.write_varint(value_len as u64)?;
if let Some(value) = &entry.value {
encoded.extend_from_slice(value);
}
encoded.write_varint(entry.timestamp)?;
}
for valueptr in &self.valueptrs {
match valueptr {
Some(ptr) => {
encoded.push(1); encoded.extend_from_slice(&ptr.encode());
}
None => {
encoded.push(0); }
}
}
Ok(encoded)
}
#[cfg(test)]
pub(crate) fn set(&mut self, key: Key, value: Value, timestamp: u64) -> Result<()> {
self.add_record(InternalKeyKind::Set, key, Some(value), timestamp)
}
#[cfg(test)]
pub(crate) fn delete(&mut self, key: Key, timestamp: u64) -> Result<()> {
self.add_record(InternalKeyKind::Delete, key, None, timestamp)
}
fn add_record_internal(
&mut self,
kind: InternalKeyKind,
key: Key,
value: Option<Value>,
valueptr: Option<ValuePointer>,
timestamp: u64,
) -> Result<()> {
let key_len = key.len();
let value_len = value.as_ref().map_or(0, |v| v.len());
let record_size = 1u64 + (key_len as u64).required_space() as u64 +
key_len as u64 +
(value_len as u64).required_space() as u64 +
value_len as u64 +
8u64;
self.grow(record_size)?;
let entry = BatchEntry {
kind,
key,
value,
timestamp,
};
self.entries.push(entry);
self.valueptrs.push(valueptr);
Ok(())
}
pub(crate) fn add_record(
&mut self,
kind: InternalKeyKind,
key: Key,
value: Option<Value>,
timestamp: u64,
) -> Result<()> {
self.add_record_internal(kind, key, value, None, timestamp)
}
pub(crate) fn count(&self) -> u32 {
self.entries.len() as u32
}
pub(crate) fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[cfg(test)]
pub(crate) fn entries(&self) -> &[BatchEntry] {
&self.entries
}
pub(crate) fn set_starting_seq_num(&mut self, seq_num: u64) {
self.starting_seq_num = seq_num;
}
pub(crate) fn get_highest_seq_num(&self) -> u64 {
if self.entries.is_empty() {
self.starting_seq_num
} else {
self.starting_seq_num + (self.entries.len() - 1) as u64
}
}
pub(crate) fn entries_with_seq_nums(
&self,
) -> Result<impl Iterator<Item = (usize, &BatchEntry, u64, u64)>> {
Ok(self
.entries
.iter()
.enumerate()
.map(move |(i, entry)| (i, entry, self.starting_seq_num + i as u64, entry.timestamp)))
}
pub(crate) fn decode(data: &[u8]) -> Result<Self> {
if data.is_empty() {
return Err(Error::InvalidBatchRecord);
}
let mut pos = 0;
let version = data[pos];
pos += 1;
if version != BATCH_VERSION {
return Err(Error::InvalidBatchRecord);
}
let (seq_num, bytes_read) =
u64::decode_var(&data[pos..]).ok_or(Error::InvalidBatchRecord)?;
pos += bytes_read;
let (count, bytes_read) = u32::decode_var(&data[pos..]).ok_or(Error::InvalidBatchRecord)?;
pos += bytes_read;
let mut entries = Vec::with_capacity(count as usize);
for _ in 0..count {
let kind_byte = data[pos];
pos += 1;
let kind = InternalKeyKind::from(kind_byte);
if kind == InternalKeyKind::Invalid {
return Err(Error::InvalidBatchRecord);
}
let (key_len, bytes_read) =
u64::decode_var(&data[pos..]).ok_or(Error::InvalidBatchRecord)?;
pos += bytes_read;
let key = data[pos..pos + key_len as usize].to_vec();
pos += key_len as usize;
let (value_len, bytes_read) =
u64::decode_var(&data[pos..]).ok_or(Error::InvalidBatchRecord)?;
pos += bytes_read;
let value = if value_len > 0 {
let value_data = data[pos..pos + value_len as usize].to_vec();
pos += value_len as usize;
Some(value_data)
} else {
None
};
let (timestamp, bytes_read) =
u64::decode_var(&data[pos..]).ok_or(Error::InvalidBatchRecord)?;
pos += bytes_read;
entries.push(BatchEntry {
kind,
key,
value,
timestamp,
});
}
let mut valueptrs = Vec::with_capacity(count as usize);
for _ in 0..count {
let has_pointer = data[pos];
pos += 1;
let valueptr = if has_pointer == 1 {
let ptr_data = &data[pos..pos + VALUE_POINTER_SIZE];
pos += VALUE_POINTER_SIZE;
Some(ValuePointer::decode(ptr_data)?)
} else {
None
};
valueptrs.push(valueptr);
}
Ok(Self {
version,
entries,
valueptrs,
starting_seq_num: seq_num,
size: 0, })
}
}