use crate::{UserKey, UserValue, ValueType, value::InternalValue};
#[derive(Clone, Debug)]
enum WriteBatchEntry {
Insert { key: UserKey, value: UserValue },
Remove { key: UserKey },
RemoveWeak { key: UserKey },
Merge { key: UserKey, value: UserValue },
}
#[derive(Clone, Debug, Default)]
pub struct WriteBatch {
entries: Vec<WriteBatchEntry>,
}
impl WriteBatch {
#[must_use]
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
entries: Vec::with_capacity(capacity),
}
}
pub fn insert<K: Into<UserKey>, V: Into<UserValue>>(&mut self, key: K, value: V) {
self.entries.push(WriteBatchEntry::Insert {
key: key.into(),
value: value.into(),
});
}
pub fn remove<K: Into<UserKey>>(&mut self, key: K) {
self.entries
.push(WriteBatchEntry::Remove { key: key.into() });
}
pub fn remove_weak<K: Into<UserKey>>(&mut self, key: K) {
self.entries
.push(WriteBatchEntry::RemoveWeak { key: key.into() });
}
pub fn merge<K: Into<UserKey>, V: Into<UserValue>>(&mut self, key: K, value: V) {
self.entries.push(WriteBatchEntry::Merge {
key: key.into(),
value: value.into(),
});
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
#[doc(hidden)]
pub(crate) fn materialize(self, seqno: crate::SeqNo) -> crate::Result<Vec<InternalValue>> {
{
let mut seen: std::collections::HashMap<&[u8], ValueType, rustc_hash::FxBuildHasher> =
std::collections::HashMap::with_capacity_and_hasher(
self.entries.len(),
rustc_hash::FxBuildHasher,
);
for entry in &self.entries {
let (key_bytes, vtype): (&[u8], _) = match entry {
WriteBatchEntry::Insert { key, .. } => (key.as_ref(), ValueType::Value),
WriteBatchEntry::Remove { key } => (key.as_ref(), ValueType::Tombstone),
WriteBatchEntry::RemoveWeak { key } => (key.as_ref(), ValueType::WeakTombstone),
WriteBatchEntry::Merge { key, .. } => (key.as_ref(), ValueType::MergeOperand),
};
if let Some(&prev_type) = seen.get(key_bytes) {
if prev_type != vtype {
return Err(crate::Error::MixedOperationBatch);
}
} else {
seen.insert(key_bytes, vtype);
}
}
}
Ok(self
.entries
.into_iter()
.map(|entry| match entry {
WriteBatchEntry::Insert { key, value } => {
InternalValue::from_components(key, value, seqno, ValueType::Value)
}
WriteBatchEntry::Remove { key } => InternalValue::new_tombstone(key, seqno),
WriteBatchEntry::RemoveWeak { key } => {
InternalValue::new_weak_tombstone(key, seqno)
}
WriteBatchEntry::Merge { key, value } => {
InternalValue::new_merge_operand(key, value, seqno)
}
})
.collect())
}
}