use log::trace;
use crate::{
error::{Error, Result},
header::{HEADER_SIZE, MAX_FILLER_BYTES, RecordHeader},
meta::{MetaValue, SubKeyRef},
record_ref::RecordRef,
simd::fast_key_eq,
zset::{ZMemberKeyRef, ZScoreKeyRef, ZSetSubKeyCodec},
};
#[derive(Debug)]
pub struct RecordMut<'a> {
header: RecordHeader,
slice: &'a mut [u8],
}
impl<'a> RecordMut<'a> {
#[inline]
pub fn from_slice_mut(slice: &'a mut [u8]) -> Result<Self> {
let header = RecordHeader::from_slice(slice)?;
let phys_size = header
.checked_physical_size()
.ok_or(Error::RecordSizeOverflow)?;
if slice.len() < phys_size {
return Err(Error::BufferTooShort {
expected: phys_size,
actual: slice.len(),
});
}
let slice = &mut slice[..phys_size];
Ok(Self { header, slice })
}
#[inline]
pub fn split_from_slice_mut(slice: &'a mut [u8]) -> Result<(Self, &'a mut [u8])> {
let header = RecordHeader::from_slice(slice)?;
let phys_size = header
.checked_physical_size()
.ok_or(Error::RecordSizeOverflow)?;
if slice.len() < phys_size {
return Err(Error::BufferTooShort {
expected: phys_size,
actual: slice.len(),
});
}
let (rec_slice, rest) = slice.split_at_mut(phys_size);
Ok((
Self {
header,
slice: rec_slice,
},
rest,
))
}
#[inline]
pub const fn header(&self) -> &RecordHeader {
&self.header
}
#[inline]
const fn key_end(&self) -> usize {
HEADER_SIZE + self.header.key_len as usize
}
#[inline]
fn sync_word(&mut self) {
self.slice[0..8].copy_from_slice(&self.header.prev_address.to_le_bytes());
}
#[inline]
pub fn key(&self) -> &[u8] {
let key_end = self.key_end();
unsafe { self.slice.get_unchecked(HEADER_SIZE..key_end) }
}
#[inline]
pub fn matches_key(&self, target_key: &[u8]) -> bool {
fast_key_eq(self.key(), target_key)
}
#[inline]
pub fn value(&self) -> &[u8] {
let key_end = self.key_end();
let total_size = key_end + self.header.val_len as usize;
unsafe { self.slice.get_unchecked(key_end..total_size) }
}
#[inline]
pub fn value_mut(&mut self) -> &mut [u8] {
let key_end = self.key_end();
let total_size = key_end + self.header.val_len as usize;
unsafe { self.slice.get_unchecked_mut(key_end..total_size) }
}
#[inline]
pub fn update_value_in_place(&mut self, new_val: &[u8]) -> Result<()> {
let val_len = self.header.val_len as usize;
if new_val.len() != val_len {
return Err(Error::ValueLengthMismatch {
expected: val_len,
actual: new_val.len(),
});
}
let key_len = self.header.key_len;
trace!("原位更新记录值: key_len={key_len}, val_len={val_len}");
self.value_mut().copy_from_slice(new_val);
Ok(())
}
#[inline]
fn write_val_with_slack(&mut self, new_val: &[u8], clear_tombstone: bool) -> Result<()> {
let total_capacity = self.header.val_capacity();
if new_val.len() > total_capacity || (total_capacity - new_val.len()) > MAX_FILLER_BYTES {
return Err(Error::ValueLengthMismatch {
expected: total_capacity,
actual: new_val.len(),
});
}
let key_end = self.key_end();
let new_val_end = key_end + new_val.len();
self.slice[key_end..new_val_end].copy_from_slice(new_val);
let remaining_slack = total_capacity - new_val.len();
self.header.val_len = new_val.len() as u32;
self.header.set_filler_bytes(remaining_slack);
if clear_tombstone {
self.header.set_tombstone(false);
}
self.slice[0..HEADER_SIZE].copy_from_slice(&self.header.to_bytes());
let key_len = self.header.key_len;
let val_len = self.header.val_len;
trace!(
"原位更新记录值(动态松弛): key_len={key_len}, val_len={val_len}, filler_bytes={remaining_slack}, clear_tombstone={clear_tombstone}"
);
Ok(())
}
#[inline]
pub fn update_value_with_slack(&mut self, new_val: &[u8]) -> Result<()> {
self.write_val_with_slack(new_val, false)
}
#[inline]
pub fn revivify_with_slack(&mut self, new_val: &[u8]) -> Result<()> {
self.write_val_with_slack(new_val, true)
}
#[inline(always)]
pub const fn can_update_with_slack(&self, new_val_len: usize) -> bool {
self.header.can_update_with_slack(new_val_len)
}
#[inline]
pub fn set_prev_address(&mut self, prev_addr: u64) -> Result<()> {
self.header.set_address(prev_addr)?;
self.sync_word();
let is_tombstone = self.header.is_tombstone();
trace!("原位更新记录前驱地址: prev_addr={prev_addr:#x}, is_tombstone={is_tombstone}");
Ok(())
}
#[inline]
pub fn set_tombstone(&mut self, is_tombstone: bool) {
self.header.set_tombstone(is_tombstone);
self.sync_word();
let prev_addr = self.header.address();
trace!("原位修改记录墓碑标记: is_tombstone={is_tombstone}, prev_addr={prev_addr:#x}");
}
#[inline]
pub fn flip_tombstone(&mut self) -> bool {
let is_tombstone = self.header.flip_tombstone();
self.sync_word();
let prev_addr = self.header.address();
trace!("原位翻转记录墓碑标记: is_tombstone={is_tombstone}, prev_addr={prev_addr:#x}");
is_tombstone
}
#[inline(always)]
pub const fn can_update_in_place(&self, new_val_len: usize) -> bool {
self.header.can_update_in_place(new_val_len)
}
#[inline]
pub const fn prev_address(&self) -> u64 {
self.header.address()
}
#[inline(always)]
pub const fn is_tombstone(&self) -> bool {
self.header.is_tombstone()
}
#[inline(always)]
pub const fn is_modified(&self) -> bool {
self.header.is_modified()
}
#[inline]
pub fn set_modified(&mut self, modified: bool) {
self.header.set_modified(modified);
self.sync_word();
}
#[inline(always)]
pub const fn is_sealed(&self) -> bool {
self.header.is_sealed()
}
#[inline]
pub fn set_sealed(&mut self, sealed: bool) {
self.header.set_sealed(sealed);
self.sync_word();
}
#[inline(always)]
pub const fn is_in_new_version(&self) -> bool {
self.header.is_in_new_version()
}
#[inline]
pub fn set_in_new_version(&mut self, in_new_version: bool) {
self.header.set_in_new_version(in_new_version);
self.sync_word();
}
#[inline(always)]
pub const fn is_read_cache(&self) -> bool {
self.header.is_read_cache()
}
#[inline]
pub fn set_read_cache(&mut self, is_read_cache: bool) {
self.header.set_read_cache(is_read_cache);
self.sync_word();
}
#[inline]
pub const fn key_len(&self) -> u32 {
self.header.key_len
}
#[inline]
pub const fn val_len(&self) -> u32 {
self.header.val_len
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.slice
}
#[inline(always)]
pub const fn filler_words(&self) -> u8 {
self.header.filler_words()
}
#[inline(always)]
pub const fn filler_rem(&self) -> u8 {
self.header.filler_rem()
}
#[inline(always)]
pub const fn filler_bytes(&self) -> usize {
self.header.filler_bytes()
}
#[inline(always)]
pub const fn val_capacity(&self) -> usize {
self.header.val_capacity()
}
#[inline(always)]
pub const fn physical_size(&self) -> usize {
self.header.physical_size()
}
#[inline]
pub const fn total_size(&self) -> usize {
self.header.record_size()
}
#[inline]
pub fn as_ref(&self) -> RecordRef<'_> {
RecordRef {
header: self.header,
key: self.key(),
value: self.value(),
}
}
#[inline]
pub fn into_ref(self) -> RecordRef<'a> {
let key_len = self.header.key_len as usize;
let val_len = self.header.val_len as usize;
let key_end = HEADER_SIZE + key_len;
let total_size = key_end + val_len;
RecordRef {
header: self.header,
key: unsafe { self.slice.get_unchecked(HEADER_SIZE..key_end) },
value: unsafe { self.slice.get_unchecked(key_end..total_size) },
}
}
#[inline]
pub fn into_slice(self) -> &'a mut [u8] {
self.slice
}
#[inline]
pub fn meta_value(&self) -> Result<MetaValue> {
MetaValue::from_slice(self.value())
}
#[inline]
pub fn update_meta_value(&mut self, meta: &MetaValue) -> Result<()> {
self.update_value_in_place(&meta.to_bytes())
}
#[inline]
pub fn sub_key_ref(&self) -> Result<SubKeyRef<'_>> {
SubKeyRef::from_slice(self.key())
}
#[inline]
pub fn zmember_key_ref(&self) -> Result<ZMemberKeyRef<'_>> {
ZSetSubKeyCodec::decode_member_key(self.key())
}
#[inline]
pub fn zscore_key_ref(&self) -> Result<ZScoreKeyRef<'_>> {
ZSetSubKeyCodec::decode_score_key(self.key())
}
}
impl<'a> PartialEq for RecordMut<'a> {
fn eq(&self, other: &Self) -> bool {
self.header == other.header && self.slice as &[u8] == other.slice as &[u8]
}
}
impl<'a> Eq for RecordMut<'a> {}