use noxu_latch::SharedLatch;
use noxu_util::{Lsn, NULL_LSN};
use std::cmp::Ordering as CmpOrdering;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum InError {
#[error("Node is full (should have been split): entries={0}, max={1}")]
NodeFull(usize, usize),
#[error("Invalid slot index: {0}, entries={1}")]
InvalidIndex(usize, usize),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Deserialization error: {0}")]
Deserialization(String),
}
pub const DBMAP_LEVEL: i32 = 0x20000;
pub const MAIN_LEVEL: i32 = 0x10000;
pub const LEVEL_MASK: i32 = 0x0ffff;
pub const MIN_LEVEL: i32 = -1;
pub const BIN_LEVEL: i32 = MAIN_LEVEL | 1;
pub const EXACT_MATCH: i32 = 1 << 16;
pub const INSERT_SUCCESS: i32 = 1 << 17;
const IN_DIRTY_BIT: u32 = 0x1;
const IN_RECALC_TOGGLE_BIT: u32 = 0x2;
const IN_IS_ROOT_BIT: u32 = 0x4;
const IN_HAS_CACHED_CHILDREN_BIT: u32 = 0x8;
const IN_PRI2_LRU_BIT: u32 = 0x10;
const IN_DELTA_BIT: u32 = 0x20;
const IN_FETCHED_COLD_BIT: u32 = 0x40;
const IN_RESIDENT_BIT: u32 = 0x80;
const IN_PROHIBIT_NEXT_DELTA_BIT: u32 = 0x100;
const IN_EXPIRATION_IN_HOURS: u32 = 0x200;
const IN_SUBTREE_SLOTS_REFLECT_LEAST_VALUE: u32 = 0x400;
pub mod entry_states {
pub const KNOWN_DELETED_BIT: u8 = 0x01;
pub const DIRTY_BIT: u8 = 0x02;
pub const MIGRATE_BIT: u8 = 0x04;
pub const PENDING_DELETED_BIT: u8 = 0x08;
pub const EMBEDDED_LN_BIT: u8 = 0x10;
pub const NO_DATA_LN_BIT: u8 = 0x20;
pub const UPDATE_KEY_WHEN_LOGGED: u8 = 0x40;
pub const TOMBSTONE_BIT: u8 = 0x80;
pub const TRANSIENT_BITS: u8 = MIGRATE_BIT | UPDATE_KEY_WHEN_LOGGED;
}
pub const DEFAULT_MAX_ENTRIES: usize = 128;
pub struct InNode {
node_id: i64,
latch: SharedLatch,
flags: u32,
last_full_lsn: Lsn,
last_delta_lsn: Lsn,
level: i32,
identifier_key: Option<Vec<u8>>,
n_entries: usize,
max_entries: usize,
entry_keys: Vec<Option<Vec<u8>>>,
entry_lsns: Vec<Lsn>,
entry_states: Vec<u8>,
database_id: u64,
in_list_resident: bool,
in_memory_size: usize,
accumulated_delta: i64,
generation: u64,
pin_count: u32,
}
impl InNode {
pub fn new(database_id: u64, level: i32, max_entries: usize) -> Self {
let exclusive_only = (level & LEVEL_MASK) == 1;
Self {
node_id: 0, latch: SharedLatch::named("InNode", exclusive_only),
flags: 0,
last_full_lsn: NULL_LSN,
last_delta_lsn: NULL_LSN,
level,
identifier_key: None,
n_entries: 0,
max_entries,
entry_keys: vec![None; max_entries],
entry_lsns: vec![NULL_LSN; max_entries],
entry_states: vec![0; max_entries],
database_id,
in_list_resident: false,
in_memory_size: 0,
accumulated_delta: 0,
generation: 0,
pin_count: 0,
}
}
#[inline]
pub fn node_id(&self) -> i64 {
self.node_id
}
#[inline]
pub fn set_node_id(&mut self, node_id: i64) {
self.node_id = node_id;
}
#[inline]
pub fn level(&self) -> i32 {
self.level
}
#[inline]
pub fn normalized_level(&self) -> i32 {
self.level & LEVEL_MASK
}
#[inline]
pub fn is_bin(&self) -> bool {
self.normalized_level() == 1
}
#[inline]
pub fn is_upper_in(&self) -> bool {
!self.is_bin()
}
#[inline]
pub fn is_dbmap_level(&self) -> bool {
(self.level & DBMAP_LEVEL) != 0
}
#[inline]
pub fn is_dirty(&self) -> bool {
(self.flags & IN_DIRTY_BIT) != 0
}
#[inline]
pub fn set_dirty(&mut self, dirty: bool) {
if dirty {
self.flags |= IN_DIRTY_BIT;
} else {
self.flags &= !IN_DIRTY_BIT;
}
}
#[inline]
pub fn clear_dirty(&mut self) {
self.set_dirty(false);
}
#[inline]
pub fn is_root(&self) -> bool {
(self.flags & IN_IS_ROOT_BIT) != 0
}
#[inline]
pub fn set_is_root(&mut self, is_root: bool) {
if is_root {
self.flags |= IN_IS_ROOT_BIT;
} else {
self.flags &= !IN_IS_ROOT_BIT;
}
}
#[inline]
pub fn is_bin_delta(&self) -> bool {
(self.flags & IN_DELTA_BIT) != 0
}
#[inline]
pub fn set_bin_delta(&mut self, is_delta: bool) {
if is_delta {
self.flags |= IN_DELTA_BIT;
} else {
self.flags &= !IN_DELTA_BIT;
}
}
#[inline]
pub fn has_cached_children(&self) -> bool {
(self.flags & IN_HAS_CACHED_CHILDREN_BIT) != 0
}
#[inline]
pub fn set_has_cached_children(&mut self, has: bool) {
if has {
self.flags |= IN_HAS_CACHED_CHILDREN_BIT;
} else {
self.flags &= !IN_HAS_CACHED_CHILDREN_BIT;
}
}
#[inline]
pub fn is_in_pri2_lru(&self) -> bool {
(self.flags & IN_PRI2_LRU_BIT) != 0
}
#[inline]
pub fn set_in_pri2_lru(&mut self, value: bool) {
if value {
self.flags |= IN_PRI2_LRU_BIT;
} else {
self.flags &= !IN_PRI2_LRU_BIT;
}
}
#[inline]
pub fn get_fetched_cold(&self) -> bool {
(self.flags & IN_FETCHED_COLD_BIT) != 0
}
#[inline]
pub fn set_fetched_cold(&mut self, val: bool) {
if val {
self.flags |= IN_FETCHED_COLD_BIT;
} else {
self.flags &= !IN_FETCHED_COLD_BIT;
}
}
#[inline]
pub fn get_prohibit_next_delta(&self) -> bool {
(self.flags & IN_PROHIBIT_NEXT_DELTA_BIT) != 0
}
#[inline]
pub fn set_prohibit_next_delta(&mut self, val: bool) {
if !self.is_bin() {
return;
}
if val {
self.flags |= IN_PROHIBIT_NEXT_DELTA_BIT;
} else {
self.flags &= !IN_PROHIBIT_NEXT_DELTA_BIT;
}
}
#[inline]
pub fn is_expiration_in_hours(&self) -> bool {
(self.flags & IN_EXPIRATION_IN_HOURS) != 0
}
#[inline]
pub fn set_expiration_in_hours(&mut self, hours: bool) {
if hours {
self.flags |= IN_EXPIRATION_IN_HOURS;
} else {
self.flags &= !IN_EXPIRATION_IN_HOURS;
}
}
#[inline]
pub fn get_in_list_resident(&self) -> bool {
self.in_list_resident
}
#[inline]
pub fn set_in_list_resident(&mut self, resident: bool) {
self.in_list_resident = resident;
if resident {
self.flags |= IN_RESIDENT_BIT;
} else {
self.flags &= !IN_RESIDENT_BIT;
}
}
#[inline]
pub fn last_full_lsn(&self) -> Lsn {
self.last_full_lsn
}
#[inline]
pub fn set_last_full_lsn(&mut self, lsn: Lsn) {
self.last_full_lsn = lsn;
}
#[inline]
pub fn last_delta_lsn(&self) -> Lsn {
self.last_delta_lsn
}
#[inline]
pub fn set_last_delta_lsn(&mut self, lsn: Lsn) {
self.last_delta_lsn = lsn;
}
#[inline]
pub fn n_entries(&self) -> usize {
self.n_entries
}
#[inline]
pub fn max_entries(&self) -> usize {
self.max_entries
}
#[inline]
pub fn get_key(&self, index: usize) -> &[u8] {
assert!(
index < self.n_entries,
"index {} >= n_entries {}",
index,
self.n_entries
);
self.entry_keys[index].as_ref().expect("key should exist")
}
#[inline]
pub fn get_lsn(&self, index: usize) -> Lsn {
assert!(
index < self.n_entries,
"index {} >= n_entries {}",
index,
self.n_entries
);
self.entry_lsns[index]
}
#[inline]
pub fn get_state(&self, index: usize) -> u8 {
assert!(
index < self.n_entries,
"index {} >= n_entries {}",
index,
self.n_entries
);
self.entry_states[index]
}
#[inline]
pub fn set_key(&mut self, index: usize, key: Vec<u8>) {
assert!(
index < self.max_entries,
"index {} >= max_entries {}",
index,
self.max_entries
);
self.entry_keys[index] = Some(key);
}
#[inline]
pub fn set_lsn(&mut self, index: usize, lsn: Lsn) {
assert!(
index < self.max_entries,
"index {} >= max_entries {}",
index,
self.max_entries
);
self.entry_lsns[index] = lsn;
}
#[inline]
pub fn set_state(&mut self, index: usize, state: u8) {
assert!(
index < self.max_entries,
"index {} >= max_entries {}",
index,
self.max_entries
);
self.entry_states[index] = state;
}
#[inline]
pub fn identifier_key(&self) -> Option<&[u8]> {
self.identifier_key.as_deref()
}
#[inline]
pub fn set_identifier_key(&mut self, key: Vec<u8>) {
self.identifier_key = Some(key);
}
#[inline]
pub fn is_entry_known_deleted(&self, index: usize) -> bool {
Self::is_state_known_deleted(self.get_state(index))
}
#[inline]
pub fn is_entry_pending_deleted(&self, index: usize) -> bool {
Self::is_state_pending_deleted(self.get_state(index))
}
#[inline]
pub fn is_entry_embedded_ln(&self, index: usize) -> bool {
Self::is_state_embedded_ln(self.get_state(index))
}
#[inline]
pub fn is_entry_no_data_ln(&self, index: usize) -> bool {
Self::is_state_no_data_ln(self.get_state(index))
}
#[inline]
pub fn is_entry_dirty(&self, index: usize) -> bool {
Self::is_state_dirty(self.get_state(index))
}
#[inline]
pub fn is_state_known_deleted(state: u8) -> bool {
(state & entry_states::KNOWN_DELETED_BIT) != 0
}
#[inline]
pub fn is_state_pending_deleted(state: u8) -> bool {
(state & entry_states::PENDING_DELETED_BIT) != 0
}
#[inline]
pub fn is_state_embedded_ln(state: u8) -> bool {
(state & entry_states::EMBEDDED_LN_BIT) != 0
}
#[inline]
pub fn is_state_no_data_ln(state: u8) -> bool {
(state & entry_states::NO_DATA_LN_BIT) != 0
}
#[inline]
pub fn is_state_dirty(state: u8) -> bool {
(state & entry_states::DIRTY_BIT) != 0
}
pub fn set_known_deleted(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] |= entry_states::KNOWN_DELETED_BIT;
self.entry_states[index] &= !entry_states::PENDING_DELETED_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn clear_known_deleted(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] &= !entry_states::KNOWN_DELETED_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn set_pending_deleted(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] |= entry_states::PENDING_DELETED_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn clear_pending_deleted(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] &= !entry_states::PENDING_DELETED_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn set_embedded_ln(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] |= entry_states::EMBEDDED_LN_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn clear_embedded_ln(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] &= !entry_states::EMBEDDED_LN_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn set_no_data_ln(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] |= entry_states::NO_DATA_LN_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
pub fn clear_no_data_ln(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] &= !entry_states::NO_DATA_LN_BIT;
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
#[inline]
pub fn set_entry_dirty(&mut self, index: usize) {
self.entry_states[index] |= entry_states::DIRTY_BIT;
}
#[inline]
pub fn clear_entry_dirty(&mut self, index: usize) {
self.entry_states[index] &= !entry_states::DIRTY_BIT;
}
#[inline]
pub fn is_tombstone(&self, index: usize) -> bool {
debug_assert!(index < self.n_entries);
(self.entry_states[index] & entry_states::TOMBSTONE_BIT) != 0
}
pub fn set_tombstone(&mut self, index: usize, tombstone: bool) {
debug_assert!(index < self.n_entries);
if tombstone {
self.entry_states[index] |= entry_states::TOMBSTONE_BIT;
} else {
self.entry_states[index] &= !entry_states::TOMBSTONE_BIT;
}
self.entry_states[index] |= entry_states::DIRTY_BIT;
self.set_dirty(true);
}
#[inline]
pub fn is_update_key_when_logged(&self, index: usize) -> bool {
debug_assert!(index < self.n_entries);
(self.entry_states[index] & entry_states::UPDATE_KEY_WHEN_LOGGED) != 0
}
pub fn set_update_key_when_logged(&mut self, index: usize) {
debug_assert!(index < self.n_entries);
self.entry_states[index] |= entry_states::UPDATE_KEY_WHEN_LOGGED;
}
#[inline]
pub fn have_embedded_data(&self, index: usize) -> bool {
self.is_entry_embedded_ln(index) && !self.is_entry_no_data_ln(index)
}
pub fn get_num_embedded_lns(&self) -> usize {
(0..self.n_entries).filter(|&i| self.is_entry_embedded_ln(i)).count()
}
pub fn pin(&mut self) {
self.pin_count += 1;
}
pub fn unpin(&mut self) {
debug_assert!(self.pin_count > 0, "unpin called with pin_count == 0");
self.pin_count = self.pin_count.saturating_sub(1);
}
#[inline]
pub fn is_pinned(&self) -> bool {
self.pin_count > 0
}
#[inline]
pub fn is_evictable(&self) -> bool {
true
}
#[inline]
pub fn get_in_memory_size(&self) -> usize {
self.in_memory_size
}
#[inline]
pub fn set_in_memory_size(&mut self, size: usize) {
self.in_memory_size = size;
}
#[inline]
pub fn get_budgeted_memory_size(&self) -> i64 {
self.in_memory_size as i64 - self.accumulated_delta
}
pub fn reset_and_get_memory_size(&mut self) -> usize {
self.accumulated_delta = 0;
self.in_memory_size
}
pub fn is_valid_for_delete(&self) -> bool {
if self.is_bin_delta() {
return false;
}
let num_valid = (0..self.n_entries)
.filter(|&i| !self.is_entry_known_deleted(i))
.count();
num_valid == 0
}
pub fn validate_subtree_before_delete(&self, index: usize) -> bool {
if index >= self.n_entries {
return true;
}
if self.is_bin() {
return true;
}
self.n_entries <= 1
}
pub fn insert_must_mutate_to_full_bin(
&self,
key: &[u8],
blind_insertion: bool,
) -> bool {
if self.n_entries >= self.max_entries {
return true;
}
if blind_insertion {
return false;
}
!self.is_bin_delta() || self.find_entry(key, false, false) < 0
}
pub fn find_entry(
&self,
key: &[u8],
indicate_if_duplicate: bool,
exact: bool,
) -> i32 {
let mut high = self.n_entries as i32 - 1;
let mut low: i32 = 0;
let mut middle: i32;
let entry_zero_special_compare =
self.is_upper_in() && !exact && !indicate_if_duplicate;
while low <= high {
middle = (high + low) / 2;
let cmp = if middle == 0 && entry_zero_special_compare {
CmpOrdering::Greater
} else {
Self::compare_keys(key, self.get_key(middle as usize))
};
match cmp {
CmpOrdering::Less => {
high = middle - 1;
}
CmpOrdering::Greater => {
low = middle + 1;
}
CmpOrdering::Equal => {
let ret = if indicate_if_duplicate {
middle | EXACT_MATCH
} else {
middle
};
if ret >= 0
&& exact
&& self.is_entry_known_deleted((ret & 0xffff) as usize)
{
return -1;
} else {
return ret;
}
}
}
}
if exact { -1 } else { high }
}
fn compare_keys(key1: &[u8], key2: &[u8]) -> CmpOrdering {
let min_len = key1.len().min(key2.len());
for i in 0..min_len {
match key1[i].cmp(&key2[i]) {
CmpOrdering::Less => return CmpOrdering::Less,
CmpOrdering::Greater => return CmpOrdering::Greater,
CmpOrdering::Equal => continue,
}
}
key1.len().cmp(&key2.len())
}
pub fn insert_entry(
&mut self,
key: Vec<u8>,
lsn: Lsn,
state: u8,
) -> Result<i32, InError> {
let index = self.find_entry(&key, true, false);
if index >= 0 && (index & EXACT_MATCH) != 0 {
return Ok(index & !EXACT_MATCH);
}
if self.n_entries >= self.max_entries {
return Err(InError::NodeFull(self.n_entries, self.max_entries));
}
let insert_index = (index + 1) as usize;
if insert_index < self.n_entries {
self.shift_entries_right(insert_index);
} else {
self.n_entries += 1;
}
self.entry_keys[insert_index] = Some(key);
self.entry_lsns[insert_index] = lsn;
self.entry_states[insert_index] = state;
self.set_dirty(true);
Ok(insert_index as i32 | INSERT_SUCCESS)
}
fn shift_entries_right(&mut self, index: usize) {
for i in (index..self.n_entries).rev() {
self.entry_keys[i + 1] = self.entry_keys[i].take();
self.entry_lsns[i + 1] = self.entry_lsns[i];
self.entry_states[i + 1] = self.entry_states[i];
}
self.n_entries += 1;
}
pub fn delete_entry(&mut self, index: usize) -> bool {
if index >= self.n_entries {
return false;
}
for i in index..self.n_entries - 1 {
self.entry_keys[i] = self.entry_keys[i + 1].take();
self.entry_lsns[i] = self.entry_lsns[i + 1];
self.entry_states[i] = self.entry_states[i + 1];
}
let last_idx = self.n_entries - 1;
self.entry_keys[last_idx] = None;
self.entry_lsns[last_idx] = NULL_LSN;
self.entry_states[last_idx] = 0;
self.n_entries -= 1;
self.set_dirty(true);
true
}
#[inline]
pub fn update_entry_lsn(&mut self, index: usize, lsn: Lsn) {
assert!(
index < self.n_entries,
"index {} >= n_entries {}",
index,
self.n_entries
);
self.entry_lsns[index] = lsn;
self.set_dirty(true);
}
#[inline]
pub fn generation(&self) -> u64 {
self.generation
}
#[inline]
pub fn set_generation(&mut self, generation: u64) {
self.generation = generation;
}
#[inline]
pub fn bump_generation(&mut self) -> u64 {
self.generation += 1;
self.generation
}
pub fn get_memory_size(&self) -> usize {
let mut size: usize = 128;
if let Some(ref k) = self.identifier_key {
size += k.len();
}
size += self.max_entries * (24 + 8 + 1);
for i in 0..self.n_entries {
if let Some(ref k) = self.entry_keys[i] {
size += k.len();
}
}
size
}
#[inline]
pub fn split_index(&self) -> usize {
self.n_entries / 2
}
pub fn get_split_key(&self, split_idx: usize) -> Option<Vec<u8>> {
if split_idx < self.n_entries {
self.entry_keys[split_idx].clone()
} else {
None
}
}
#[inline]
pub fn latch(&self) -> &SharedLatch {
&self.latch
}
pub fn log_size(&self) -> usize {
let mut size = 8 + 8 + 4 + 8 + 8 + 2;
size += 2; if let Some(ref id_key) = self.identifier_key {
size += id_key.len();
}
for i in 0..self.n_entries {
size += 2; if let Some(ref key) = self.entry_keys[i] {
size += key.len();
}
size += 8; size += 1; }
size
}
pub fn write_to_log(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self.node_id.to_be_bytes());
buf.extend_from_slice(&self.database_id.to_be_bytes());
buf.extend_from_slice(&self.level.to_be_bytes());
buf.extend_from_slice(&self.last_full_lsn.as_u64().to_be_bytes());
buf.extend_from_slice(&self.last_delta_lsn.as_u64().to_be_bytes());
buf.extend_from_slice(&(self.n_entries as u16).to_be_bytes());
if let Some(ref id_key) = self.identifier_key {
buf.extend_from_slice(&(id_key.len() as u16).to_be_bytes());
buf.extend_from_slice(id_key);
} else {
buf.extend_from_slice(&0u16.to_be_bytes());
}
for i in 0..self.n_entries {
if let Some(ref key) = self.entry_keys[i] {
buf.extend_from_slice(&(key.len() as u16).to_be_bytes());
buf.extend_from_slice(key);
} else {
buf.extend_from_slice(&0u16.to_be_bytes());
}
buf.extend_from_slice(&self.entry_lsns[i].as_u64().to_be_bytes());
let persistent_state =
self.entry_states[i] & !entry_states::TRANSIENT_BITS;
buf.push(persistent_state);
}
}
pub fn read_from_log(buf: &[u8], _level: i32) -> Result<Self, InError> {
if buf.len() < 38 {
return Err(InError::Deserialization(format!(
"buffer too small: {} bytes",
buf.len()
)));
}
let mut offset = 0;
let node_id =
i64::from_be_bytes(buf[offset..offset + 8].try_into().map_err(
|e| InError::Deserialization(format!("node_id: {}", e)),
)?);
offset += 8;
let database_id =
u64::from_be_bytes(buf[offset..offset + 8].try_into().map_err(
|e| InError::Deserialization(format!("database_id: {}", e)),
)?);
offset += 8;
let level_val =
i32::from_be_bytes(buf[offset..offset + 4].try_into().map_err(
|e| InError::Deserialization(format!("level: {}", e)),
)?);
offset += 4;
let last_full_lsn = Lsn::from_u64(u64::from_be_bytes(
buf[offset..offset + 8].try_into().map_err(|e| {
InError::Deserialization(format!("last_full_lsn: {}", e))
})?,
));
offset += 8;
let last_delta_lsn = Lsn::from_u64(u64::from_be_bytes(
buf[offset..offset + 8].try_into().map_err(|e| {
InError::Deserialization(format!("last_delta_lsn: {}", e))
})?,
));
offset += 8;
let n_entries =
u16::from_be_bytes(buf[offset..offset + 2].try_into().map_err(
|e| InError::Deserialization(format!("n_entries: {}", e)),
)?) as usize;
offset += 2;
let id_key_len =
u16::from_be_bytes(buf[offset..offset + 2].try_into().map_err(
|e| InError::Deserialization(format!("id_key_len: {}", e)),
)?) as usize;
offset += 2;
let identifier_key = if id_key_len > 0 {
if offset + id_key_len > buf.len() {
return Err(InError::Deserialization(
"id_key extends past buffer".into(),
));
}
let key = buf[offset..offset + id_key_len].to_vec();
offset += id_key_len;
Some(key)
} else {
None
};
let max_entries = n_entries.max(DEFAULT_MAX_ENTRIES);
let mut in_node = Self::new(database_id, level_val, max_entries);
in_node.node_id = node_id;
in_node.last_full_lsn = last_full_lsn;
in_node.last_delta_lsn = last_delta_lsn;
in_node.identifier_key = identifier_key;
in_node.n_entries = n_entries;
for i in 0..n_entries {
if offset + 2 > buf.len() {
return Err(InError::Deserialization(format!(
"entry {} key_len past buffer",
i
)));
}
let key_len = u16::from_be_bytes(
buf[offset..offset + 2].try_into().map_err(|e| {
InError::Deserialization(format!(
"entry {} key_len: {}",
i, e
))
})?,
) as usize;
offset += 2;
let key = if key_len > 0 {
if offset + key_len > buf.len() {
return Err(InError::Deserialization(format!(
"entry {} key past buffer",
i
)));
}
let k = buf[offset..offset + key_len].to_vec();
offset += key_len;
Some(k)
} else {
None
};
if offset + 8 > buf.len() {
return Err(InError::Deserialization(format!(
"entry {} lsn past buffer",
i
)));
}
let lsn = Lsn::from_u64(u64::from_be_bytes(
buf[offset..offset + 8].try_into().map_err(|e| {
InError::Deserialization(format!("entry {} lsn: {}", i, e))
})?,
));
offset += 8;
if offset + 1 > buf.len() {
return Err(InError::Deserialization(format!(
"entry {} state past buffer",
i
)));
}
let state = buf[offset];
offset += 1;
in_node.entry_keys[i] = key;
in_node.entry_lsns[i] = lsn;
in_node.entry_states[i] = state;
}
Ok(in_node)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_in_node() {
let in_node = InNode::new(42, BIN_LEVEL, 128);
assert_eq!(in_node.database_id, 42);
assert_eq!(in_node.level(), BIN_LEVEL);
assert_eq!(in_node.n_entries(), 0);
assert_eq!(in_node.max_entries(), 128);
assert!(in_node.is_bin());
assert!(!in_node.is_upper_in());
assert!(!in_node.is_dirty());
assert!(!in_node.is_root());
}
#[test]
fn test_level_queries() {
let bin = InNode::new(1, BIN_LEVEL, 128);
assert!(bin.is_bin());
assert!(!bin.is_upper_in());
assert_eq!(bin.normalized_level(), 1);
let upper = InNode::new(1, MAIN_LEVEL | 2, 128);
assert!(!upper.is_bin());
assert!(upper.is_upper_in());
assert_eq!(upper.normalized_level(), 2);
let dbmap = InNode::new(1, DBMAP_LEVEL | 1, 128);
assert!(dbmap.is_dbmap_level());
}
#[test]
fn test_flag_operations() {
let mut in_node = InNode::new(1, BIN_LEVEL, 128);
assert!(!in_node.is_dirty());
in_node.set_dirty(true);
assert!(in_node.is_dirty());
in_node.clear_dirty();
assert!(!in_node.is_dirty());
assert!(!in_node.is_root());
in_node.set_is_root(true);
assert!(in_node.is_root());
in_node.set_is_root(false);
assert!(!in_node.is_root());
assert!(!in_node.is_bin_delta());
in_node.set_bin_delta(true);
assert!(in_node.is_bin_delta());
}
#[test]
fn test_insert_and_find() {
let mut in_node = InNode::new(1, BIN_LEVEL, 128);
let result = in_node.insert_entry(
b"banana".to_vec(),
Lsn::from_u64(100),
entry_states::DIRTY_BIT,
);
assert!(result.is_ok());
assert_eq!(result.unwrap() & !INSERT_SUCCESS, 0);
let result = in_node.insert_entry(
b"apple".to_vec(),
Lsn::from_u64(200),
entry_states::DIRTY_BIT,
);
assert!(result.is_ok());
assert_eq!(result.unwrap() & !INSERT_SUCCESS, 0);
let result = in_node.insert_entry(
b"cherry".to_vec(),
Lsn::from_u64(300),
entry_states::DIRTY_BIT,
);
assert!(result.is_ok());
assert_eq!(result.unwrap() & !INSERT_SUCCESS, 2);
assert_eq!(in_node.n_entries(), 3);
let idx = in_node.find_entry(b"apple", true, true);
assert_eq!(idx & 0xffff, 0);
assert_eq!(idx & EXACT_MATCH, EXACT_MATCH);
let idx = in_node.find_entry(b"banana", true, true);
assert_eq!(idx & 0xffff, 1);
assert_eq!(idx & EXACT_MATCH, EXACT_MATCH);
let idx = in_node.find_entry(b"cherry", true, true);
assert_eq!(idx & 0xffff, 2);
assert_eq!(idx & EXACT_MATCH, EXACT_MATCH);
let idx = in_node.find_entry(b"dog", false, true);
assert_eq!(idx, -1);
let idx = in_node.find_entry(b"blueberry", false, false);
assert_eq!(idx, 1); }
#[test]
fn test_insert_maintains_order() {
let mut in_node = InNode::new(1, BIN_LEVEL, 128);
in_node
.insert_entry(
b"zebra".to_vec(),
Lsn::from_u64(100),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"apple".to_vec(),
Lsn::from_u64(200),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"monkey".to_vec(),
Lsn::from_u64(300),
entry_states::DIRTY_BIT,
)
.unwrap();
assert_eq!(in_node.n_entries(), 3);
assert_eq!(in_node.get_key(0), b"apple");
assert_eq!(in_node.get_key(1), b"monkey");
assert_eq!(in_node.get_key(2), b"zebra");
assert_eq!(in_node.get_lsn(0), Lsn::from_u64(200));
assert_eq!(in_node.get_lsn(1), Lsn::from_u64(300));
assert_eq!(in_node.get_lsn(2), Lsn::from_u64(100));
}
#[test]
fn test_delete_entry() {
let mut in_node = InNode::new(1, BIN_LEVEL, 128);
in_node
.insert_entry(
b"apple".to_vec(),
Lsn::from_u64(100),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"banana".to_vec(),
Lsn::from_u64(200),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"cherry".to_vec(),
Lsn::from_u64(300),
entry_states::DIRTY_BIT,
)
.unwrap();
assert_eq!(in_node.n_entries(), 3);
assert!(in_node.delete_entry(1));
assert_eq!(in_node.n_entries(), 2);
assert_eq!(in_node.get_key(0), b"apple");
assert_eq!(in_node.get_key(1), b"cherry");
assert_eq!(in_node.get_lsn(0), Lsn::from_u64(100));
assert_eq!(in_node.get_lsn(1), Lsn::from_u64(300));
}
#[test]
fn test_find_entry_exact_and_inexact() {
let mut in_node = InNode::new(1, BIN_LEVEL, 128);
in_node
.insert_entry(
b"b".to_vec(),
Lsn::from_u64(100),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"d".to_vec(),
Lsn::from_u64(200),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"f".to_vec(),
Lsn::from_u64(300),
entry_states::DIRTY_BIT,
)
.unwrap();
let idx = in_node.find_entry(b"d", true, true);
assert_eq!(idx & 0xffff, 1);
assert_eq!(idx & EXACT_MATCH, EXACT_MATCH);
let idx = in_node.find_entry(b"d", false, true);
assert_eq!(idx, 1);
let idx = in_node.find_entry(b"a", false, false);
assert_eq!(idx, -1);
let idx = in_node.find_entry(b"c", false, false);
assert_eq!(idx, 0);
let idx = in_node.find_entry(b"e", false, false);
assert_eq!(idx, 1);
let idx = in_node.find_entry(b"z", false, false);
assert_eq!(idx, 2); }
#[test]
fn test_slot_state_operations() {
let mut in_node = InNode::new(1, BIN_LEVEL, 128);
in_node.insert_entry(b"key1".to_vec(), Lsn::from_u64(100), 0).unwrap();
assert!(!in_node.is_entry_known_deleted(0));
in_node.set_known_deleted(0);
assert!(in_node.is_entry_known_deleted(0));
in_node.clear_known_deleted(0);
assert!(!in_node.is_entry_known_deleted(0));
assert!(!in_node.is_entry_pending_deleted(0));
in_node.set_pending_deleted(0);
assert!(in_node.is_entry_pending_deleted(0));
in_node.clear_pending_deleted(0);
assert!(!in_node.is_entry_pending_deleted(0));
assert!(!in_node.is_entry_embedded_ln(0));
in_node.set_embedded_ln(0);
assert!(in_node.is_entry_embedded_ln(0));
in_node.clear_embedded_ln(0);
assert!(!in_node.is_entry_embedded_ln(0));
in_node.set_entry_dirty(0);
assert!(in_node.is_entry_dirty(0));
in_node.clear_entry_dirty(0);
assert!(!in_node.is_entry_dirty(0));
}
#[test]
fn test_serialization_roundtrip() {
let mut in_node = InNode::new(42, BIN_LEVEL, 128);
in_node.set_node_id(1234);
in_node.set_last_full_lsn(Lsn::from_u64(5000));
in_node.set_last_delta_lsn(Lsn::from_u64(5100));
in_node.set_identifier_key(b"id_key".to_vec());
in_node
.insert_entry(
b"apple".to_vec(),
Lsn::from_u64(100),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"banana".to_vec(),
Lsn::from_u64(200),
entry_states::KNOWN_DELETED_BIT,
)
.unwrap();
in_node
.insert_entry(
b"cherry".to_vec(),
Lsn::from_u64(300),
entry_states::PENDING_DELETED_BIT,
)
.unwrap();
let mut buf = Vec::with_capacity(in_node.log_size());
in_node.write_to_log(&mut buf);
let restored = InNode::read_from_log(&buf, BIN_LEVEL).unwrap();
assert_eq!(restored.node_id(), 1234);
assert_eq!(restored.database_id, 42);
assert_eq!(restored.level(), BIN_LEVEL);
assert_eq!(restored.last_full_lsn(), Lsn::from_u64(5000));
assert_eq!(restored.last_delta_lsn(), Lsn::from_u64(5100));
assert_eq!(restored.identifier_key(), Some(b"id_key".as_slice()));
assert_eq!(restored.n_entries(), 3);
assert_eq!(restored.get_key(0), b"apple");
assert_eq!(restored.get_lsn(0), Lsn::from_u64(100));
assert!(InNode::is_state_dirty(restored.get_state(0)));
assert_eq!(restored.get_key(1), b"banana");
assert_eq!(restored.get_lsn(1), Lsn::from_u64(200));
assert!(InNode::is_state_known_deleted(restored.get_state(1)));
assert_eq!(restored.get_key(2), b"cherry");
assert_eq!(restored.get_lsn(2), Lsn::from_u64(300));
assert!(InNode::is_state_pending_deleted(restored.get_state(2)));
}
#[test]
fn test_compare_keys() {
use CmpOrdering::*;
assert_eq!(InNode::compare_keys(b"apple", b"banana"), Less);
assert_eq!(InNode::compare_keys(b"banana", b"apple"), Greater);
assert_eq!(InNode::compare_keys(b"apple", b"apple"), Equal);
assert_eq!(InNode::compare_keys(b"app", b"apple"), Less);
assert_eq!(InNode::compare_keys(b"apple", b"app"), Greater);
assert_eq!(InNode::compare_keys(b"", b"apple"), Less);
assert_eq!(InNode::compare_keys(b"apple", b""), Greater);
assert_eq!(InNode::compare_keys(b"", b""), Equal);
}
#[test]
fn test_node_full_error() {
let mut in_node = InNode::new(1, BIN_LEVEL, 2);
in_node
.insert_entry(
b"a".to_vec(),
Lsn::from_u64(100),
entry_states::DIRTY_BIT,
)
.unwrap();
in_node
.insert_entry(
b"b".to_vec(),
Lsn::from_u64(200),
entry_states::DIRTY_BIT,
)
.unwrap();
let result = in_node.insert_entry(
b"c".to_vec(),
Lsn::from_u64(300),
entry_states::DIRTY_BIT,
);
assert!(result.is_err());
match result {
Err(InError::NodeFull(n, max)) => {
assert_eq!(n, 2);
assert_eq!(max, 2);
}
_ => panic!("Expected NodeFull error"),
}
}
#[test]
fn test_latch_operations() {
let in_node = InNode::new(1, BIN_LEVEL, 128);
{
let _guard =
in_node.latch().acquire_exclusive().expect("acquire_exclusive");
}
{
let _guard =
in_node.latch().acquire_shared().expect("acquire_shared");
}
}
#[test]
fn test_update_entry_lsn() {
let mut node = InNode::new(1, BIN_LEVEL, 128);
node.insert_entry(b"key".to_vec(), Lsn::from_u64(100), 0).unwrap();
assert_eq!(node.get_lsn(0), Lsn::from_u64(100));
node.clear_dirty();
assert!(!node.is_dirty());
node.update_entry_lsn(0, Lsn::from_u64(999));
assert_eq!(node.get_lsn(0), Lsn::from_u64(999));
assert!(node.is_dirty(), "update_entry_lsn should mark node dirty");
}
#[test]
#[should_panic]
fn test_update_entry_lsn_out_of_bounds_panics() {
let mut node = InNode::new(1, BIN_LEVEL, 128);
node.update_entry_lsn(0, Lsn::from_u64(1));
}
#[test]
fn test_get_memory_size_empty_node() {
let node = InNode::new(1, BIN_LEVEL, 128);
let size = node.get_memory_size();
assert!(size > 0);
}
#[test]
fn test_get_memory_size_grows_with_keys() {
let mut node_small = InNode::new(1, BIN_LEVEL, 32);
let mut node_large = InNode::new(1, BIN_LEVEL, 32);
node_small.insert_entry(b"k".to_vec(), Lsn::from_u64(1), 0).unwrap();
node_large
.insert_entry(b"very_long_key_here".to_vec(), Lsn::from_u64(1), 0)
.unwrap();
assert!(
node_large.get_memory_size() > node_small.get_memory_size(),
"larger key should produce larger memory estimate"
);
}
#[test]
fn test_get_memory_size_with_identifier_key() {
let mut node_no_id = InNode::new(1, BIN_LEVEL, 8);
let mut node_with_id = InNode::new(1, BIN_LEVEL, 8);
node_with_id.set_identifier_key(b"identifier_key_bytes".to_vec());
assert!(
node_with_id.get_memory_size() > node_no_id.get_memory_size(),
"identifier key should increase memory estimate"
);
let _ = &mut node_no_id;
}
#[test]
fn test_insert_into_empty_node() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
let result = node.insert_entry(b"only".to_vec(), Lsn::from_u64(1), 0);
assert!(result.is_ok());
let idx = result.unwrap();
assert_ne!(idx & INSERT_SUCCESS, 0);
assert_eq!(node.n_entries(), 1);
}
#[test]
fn test_insert_duplicate_returns_index_without_success_flag() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"dup".to_vec(), Lsn::from_u64(1), 0).unwrap();
let result = node.insert_entry(b"dup".to_vec(), Lsn::from_u64(2), 0);
assert!(result.is_ok());
let idx = result.unwrap();
assert_eq!(idx & INSERT_SUCCESS, 0);
assert_eq!(node.n_entries(), 1);
}
#[test]
fn test_delete_only_entry() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"solo".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert_eq!(node.n_entries(), 1);
assert!(node.delete_entry(0));
assert_eq!(node.n_entries(), 0);
}
#[test]
fn test_delete_out_of_bounds_returns_false() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert!(!node.delete_entry(0));
assert!(!node.delete_entry(999));
}
#[test]
fn test_find_entry_empty_node() {
let node = InNode::new(1, BIN_LEVEL, 4);
let result = node.find_entry(b"any", false, false);
assert_eq!(result, -1);
let result = node.find_entry(b"any", false, true);
assert_eq!(result, -1);
}
#[test]
fn test_find_entry_known_deleted_exact_returns_minus_one() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"ghost".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.set_known_deleted(0);
let result = node.find_entry(b"ghost", true, true);
assert_eq!(result, -1);
}
#[test]
fn test_upper_in_entry_zero_virtual_key() {
let mut node = InNode::new(1, MAIN_LEVEL | 2, 8);
node.insert_entry(b"aaa".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.insert_entry(b"mmm".to_vec(), Lsn::from_u64(2), 0).unwrap();
node.insert_entry(b"zzz".to_vec(), Lsn::from_u64(3), 0).unwrap();
let result = node.find_entry(b"nnn", false, false);
assert_eq!(result, 1);
let result2 = node.find_entry(b"bbb", false, false);
assert_eq!(result2, 0);
let result3 = node.find_entry(b"zzz", true, false);
assert_eq!(result3 & !EXACT_MATCH, 2);
assert_ne!(result3 & EXACT_MATCH, 0);
}
#[test]
fn test_split_index_and_split_key() {
let mut node = InNode::new(1, BIN_LEVEL, 8);
for i in 0u8..6 {
node.insert_entry(vec![b'a' + i], Lsn::from_u64(i as u64), 0)
.unwrap();
}
assert_eq!(node.split_index(), 3);
let split_key = node.get_split_key(3).unwrap();
assert_eq!(split_key, node.get_key(3).to_vec());
assert!(node.get_split_key(node.n_entries()).is_none());
}
#[test]
fn test_in_list_resident_and_generation() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert!(!node.in_list_resident);
node.in_list_resident = true;
assert!(node.in_list_resident);
assert_eq!(node.generation, 0);
node.generation = 42;
assert_eq!(node.generation, 42);
}
#[test]
fn test_set_lsn_and_state_direct() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"k".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.set_lsn(0, Lsn::from_u64(777));
assert_eq!(node.get_lsn(0), Lsn::from_u64(777));
node.set_state(0, entry_states::KNOWN_DELETED_BIT);
assert_eq!(node.get_state(0), entry_states::KNOWN_DELETED_BIT);
}
#[test]
fn test_identifier_key_roundtrip() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert!(node.identifier_key().is_none());
node.set_identifier_key(b"myid".to_vec());
assert_eq!(node.identifier_key(), Some(b"myid".as_ref()));
}
#[test]
fn test_has_cached_children_flag() {
let mut node = InNode::new(1, MAIN_LEVEL | 2, 4);
assert!(!node.has_cached_children());
node.set_has_cached_children(true);
assert!(node.has_cached_children());
node.set_has_cached_children(false);
assert!(!node.has_cached_children());
}
#[test]
fn test_last_full_and_delta_lsn() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert!(node.last_full_lsn().is_null());
assert!(node.last_delta_lsn().is_null());
node.set_last_full_lsn(Lsn::new(1, 100));
node.set_last_delta_lsn(Lsn::new(1, 200));
assert_eq!(node.last_full_lsn(), Lsn::new(1, 100));
assert_eq!(node.last_delta_lsn(), Lsn::new(1, 200));
}
#[test]
fn test_no_data_ln_entry_state() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"k".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert!(!node.is_entry_no_data_ln(0));
node.entry_states[0] |= entry_states::NO_DATA_LN_BIT;
assert!(node.is_entry_no_data_ln(0));
assert!(InNode::is_state_no_data_ln(entry_states::NO_DATA_LN_BIT));
}
#[test]
fn test_je_find_entry_empty_in_returns_minus_one() {
let node = InNode::new(1, BIN_LEVEL, 6);
let zero_key = [0x00u8; 3];
let max_key = [0xFFu8; 3];
assert_eq!(node.find_entry(&zero_key, false, false), -1);
assert_eq!(node.find_entry(&max_key, false, false), -1);
assert_eq!(node.find_entry(&zero_key, false, true), -1);
assert_eq!(node.find_entry(&max_key, false, true), -1);
assert_eq!(node.find_entry(&zero_key, true, false), -1);
assert_eq!(node.find_entry(&max_key, true, false), -1);
assert_eq!(node.find_entry(&zero_key, true, true), -1);
assert_eq!(node.find_entry(&max_key, true, true), -1);
}
#[test]
fn test_je_find_entry_after_sequential_inserts() {
const CAP: usize = 6;
let mut node = InNode::new(1, BIN_LEVEL, CAP);
let zero_key = [0x00u8; 3];
let max_key = [0xFFu8; 3];
for i in 0u8..CAP as u8 {
let key_bytes = vec![0x01u8, i, 0x10u8];
let result =
node.insert_entry(key_bytes, Lsn::from_u64(i as u64), 0);
assert!(result.is_ok());
let flags = result.unwrap();
assert_ne!(flags & INSERT_SUCCESS, 0, "INSERT_SUCCESS must be set");
assert_eq!(
(flags & !INSERT_SUCCESS) as u8,
i,
"slot index must equal i"
);
assert_eq!(node.find_entry(&zero_key, false, false), -1);
assert_eq!(node.find_entry(&max_key, false, false), i as i32);
assert_eq!(node.find_entry(&zero_key, false, true), -1);
assert_eq!(node.find_entry(&max_key, false, true), -1);
for j in 0..=i as usize {
let k = node.get_key(j);
let idx_inexact = node.find_entry(k, false, false);
assert_eq!(
idx_inexact, j as i32,
"inexact: key at {} must return {}",
j, j
);
let idx_exact = node.find_entry(k, false, true);
assert_eq!(
idx_exact, j as i32,
"exact: key at {} must return {}",
j, j
);
let idx_dup = node.find_entry(k, true, false);
assert_eq!(
idx_dup & !EXACT_MATCH,
j as i32,
"indicate_dup: slot must be {}",
j
);
assert_ne!(idx_dup & EXACT_MATCH, 0, "EXACT_MATCH must be set");
let idx_both = node.find_entry(k, true, true);
assert_eq!(idx_both & !EXACT_MATCH, j as i32);
assert_ne!(idx_both & EXACT_MATCH, 0);
}
}
}
#[test]
fn test_je_unsigned_byte_key_ordering() {
let mut node = InNode::new(1, BIN_LEVEL, 8);
node.insert_entry(vec![0x7Fu8; 3], Lsn::from_u64(1), 0).unwrap();
let high_key = [0xFFu8; 3];
let idx = node.find_entry(&high_key, false, false);
assert_eq!(idx, 0, "0xFF key must sort after 0x7F (slot 0)");
node.insert_entry(vec![0xFFu8; 3], Lsn::from_u64(2), 0).unwrap();
assert_eq!(node.n_entries(), 2);
assert_eq!(node.get_key(0), &[0x7Fu8; 3]);
assert_eq!(node.get_key(1), &[0xFFu8; 3]);
}
#[test]
fn test_je_delete_entry_fill_then_empty() {
const CAP: usize = 6;
let mut node = InNode::new(1, BIN_LEVEL, CAP);
let keys: Vec<Vec<u8>> =
(0..CAP as u8).map(|i| vec![0x01u8, i, 0x10u8]).collect();
for k in &keys {
node.insert_entry(k.clone(), Lsn::from_u64(0), 0).unwrap();
}
assert_eq!(node.n_entries(), CAP);
while node.n_entries() > 1 {
let n = node.n_entries();
let last_key = node.get_key(n - 1).to_vec();
let idx = node.find_entry(&last_key, false, true);
assert!(idx >= 0, "must find key before deleting");
assert!(node.delete_entry(idx as usize));
assert_eq!(node.n_entries(), n - 1);
}
assert_eq!(node.n_entries(), 1);
assert_eq!(node.get_key(0), keys[0].as_slice());
assert!(node.delete_entry(0));
assert_eq!(node.n_entries(), 0);
assert!(!node.delete_entry(0));
}
#[test]
fn test_je_level_constants() {
assert_eq!(
BIN_LEVEL,
MAIN_LEVEL | 1,
"BIN_LEVEL must equal MAIN_LEVEL | 1"
);
let bin = InNode::new(1, BIN_LEVEL, 4);
assert!(bin.is_bin(), "BIN_LEVEL node must be is_bin()");
assert!(!bin.is_upper_in(), "BIN_LEVEL node must not be is_upper_in()");
assert_eq!(bin.normalized_level(), 1);
let upper = InNode::new(1, MAIN_LEVEL | 2, 4);
assert!(!upper.is_bin(), "upper IN must not be is_bin()");
assert!(upper.is_upper_in(), "upper IN must be is_upper_in()");
assert_eq!(upper.normalized_level(), 2);
let dbmap = InNode::new(1, DBMAP_LEVEL | 1, 4);
assert!(
dbmap.is_dbmap_level(),
"DBMAP_LEVEL node must be is_dbmap_level()"
);
}
#[test]
fn test_je_upper_in_virtual_slot0_routing() {
let mut node = InNode::new(1, MAIN_LEVEL | 2, 8);
node.insert_entry(b"bbb".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.insert_entry(b"ddd".to_vec(), Lsn::from_u64(2), 0).unwrap();
node.insert_entry(b"fff".to_vec(), Lsn::from_u64(3), 0).unwrap();
let idx_below = node.find_entry(b"aaa", false, false);
assert_eq!(
idx_below, 0,
"key < first real key must route to slot 0 (virtual)"
);
let idx_mid = node.find_entry(b"ccc", false, false);
assert_eq!(
idx_mid, 0,
"key between slot-0 and slot-1 must stay at slot 0"
);
let idx_hi = node.find_entry(b"eee", false, false);
assert_eq!(
idx_hi, 1,
"key between slot-1 and slot-2 must route to slot 1"
);
let idx_max = node.find_entry(b"zzz", false, false);
assert_eq!(
idx_max, 2,
"key greater than all entries must route to last slot"
);
let idx_dup = node.find_entry(b"bbb", true, false);
assert_eq!(
idx_dup & !EXACT_MATCH,
0,
"indicate_dup: exact match at slot 0 must return slot 0"
);
assert_ne!(
idx_dup & EXACT_MATCH,
0,
"indicate_dup: EXACT_MATCH must be set for 'bbb'"
);
}
#[test]
fn test_je_insert_entry_node_full_returns_error() {
const CAP: usize = 4;
let mut node = InNode::new(1, BIN_LEVEL, CAP);
for i in 0..CAP as u8 {
node.insert_entry(vec![i], Lsn::from_u64(i as u64), 0).unwrap();
}
assert_eq!(node.n_entries(), CAP);
let result =
node.insert_entry(b"overflow".to_vec(), Lsn::from_u64(99), 0);
assert!(
matches!(result, Err(InError::NodeFull(n, m)) if n == CAP && m == CAP),
"inserting into a full node must return InError::NodeFull"
);
}
#[test]
fn test_set_prohibit_next_delta_on_non_bin_is_noop() {
let mut upper = InNode::new(1, MAIN_LEVEL | 2, 8);
upper.set_prohibit_next_delta(true);
assert!(!upper.get_prohibit_next_delta());
let mut bin = InNode::new(1, BIN_LEVEL, 8);
bin.set_prohibit_next_delta(true);
assert!(bin.get_prohibit_next_delta());
bin.set_prohibit_next_delta(false);
assert!(!bin.get_prohibit_next_delta());
}
#[test]
fn test_is_valid_for_delete_bin_delta_returns_false() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"k".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.set_known_deleted(0);
assert!(
node.is_valid_for_delete(),
"all KD without delta flag => true"
);
node.set_bin_delta(true);
assert!(!node.is_valid_for_delete(), "bin-delta flag => always false");
}
#[test]
fn test_validate_subtree_before_delete_paths() {
let node = InNode::new(1, BIN_LEVEL, 4);
assert!(node.validate_subtree_before_delete(0));
let mut bin = InNode::new(1, BIN_LEVEL, 4);
bin.insert_entry(b"a".to_vec(), Lsn::from_u64(1), 0).unwrap();
bin.insert_entry(b"b".to_vec(), Lsn::from_u64(2), 0).unwrap();
assert!(bin.validate_subtree_before_delete(0));
assert!(bin.validate_subtree_before_delete(1));
let mut upper1 = InNode::new(1, MAIN_LEVEL | 2, 8);
upper1.insert_entry(b"x".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert!(upper1.validate_subtree_before_delete(0));
let mut upper2 = InNode::new(1, MAIN_LEVEL | 2, 8);
upper2.insert_entry(b"x".to_vec(), Lsn::from_u64(1), 0).unwrap();
upper2.insert_entry(b"y".to_vec(), Lsn::from_u64(2), 0).unwrap();
assert!(!upper2.validate_subtree_before_delete(0));
}
#[test]
fn test_insert_must_mutate_to_full_bin() {
let mut node = InNode::new(1, BIN_LEVEL, 2);
node.insert_entry(b"a".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.insert_entry(b"b".to_vec(), Lsn::from_u64(2), 0).unwrap();
assert!(node.insert_must_mutate_to_full_bin(b"c", true));
assert!(node.insert_must_mutate_to_full_bin(b"c", false));
let node2 = InNode::new(1, BIN_LEVEL, 8);
assert!(!node2.insert_must_mutate_to_full_bin(b"x", true));
let mut node3 = InNode::new(1, BIN_LEVEL, 8);
node3.insert_entry(b"m".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert!(node3.insert_must_mutate_to_full_bin(b"z", false));
}
#[test]
fn test_set_in_list_resident_false_clears_resident_bit() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.set_in_list_resident(true);
assert!(node.get_in_list_resident());
assert!((node.flags & 0x80) != 0, "RESIDENT bit should be set");
node.set_in_list_resident(false);
assert!(!node.get_in_list_resident());
assert!((node.flags & 0x80) == 0, "RESIDENT bit should be cleared");
}
#[test]
fn test_in_pri2_lru_and_fetched_cold_and_expiration_in_hours() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert!(!node.is_in_pri2_lru());
node.set_in_pri2_lru(true);
assert!(node.is_in_pri2_lru());
node.set_in_pri2_lru(false);
assert!(!node.is_in_pri2_lru());
assert!(!node.get_fetched_cold());
node.set_fetched_cold(true);
assert!(node.get_fetched_cold());
node.set_fetched_cold(false);
assert!(!node.get_fetched_cold());
assert!(!node.is_expiration_in_hours());
node.set_expiration_in_hours(true);
assert!(node.is_expiration_in_hours());
node.set_expiration_in_hours(false);
assert!(!node.is_expiration_in_hours());
}
#[test]
fn test_have_embedded_data_and_get_num_embedded_lns() {
let mut node = InNode::new(1, BIN_LEVEL, 8);
node.insert_entry(b"k1".to_vec(), Lsn::from_u64(1), 0).unwrap();
node.insert_entry(b"k2".to_vec(), Lsn::from_u64(2), 0).unwrap();
assert_eq!(node.get_num_embedded_lns(), 0);
assert!(!node.have_embedded_data(0));
node.set_embedded_ln(0);
assert!(node.have_embedded_data(0));
assert_eq!(node.get_num_embedded_lns(), 1);
node.set_embedded_ln(1);
node.set_no_data_ln(1);
assert!(!node.have_embedded_data(1));
assert_eq!(node.get_num_embedded_lns(), 2);
node.clear_no_data_ln(1);
assert!(node.have_embedded_data(1));
node.clear_embedded_ln(0);
assert!(!node.have_embedded_data(0));
assert_eq!(node.get_num_embedded_lns(), 1);
}
#[test]
fn test_tombstone_operations() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"t".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert!(!node.is_tombstone(0));
node.set_tombstone(0, true);
assert!(node.is_tombstone(0));
assert!(node.is_entry_dirty(0));
node.set_tombstone(0, false);
assert!(!node.is_tombstone(0));
}
#[test]
fn test_update_key_when_logged_flag() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"k".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert!(!node.is_update_key_when_logged(0));
node.set_update_key_when_logged(0);
assert!(node.is_update_key_when_logged(0));
}
#[test]
fn test_pin_unpin_is_pinned() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert!(!node.is_pinned());
assert_eq!(node.pin_count, 0);
node.pin();
assert!(node.is_pinned());
assert_eq!(node.pin_count, 1);
node.pin();
assert_eq!(node.pin_count, 2);
node.unpin();
assert!(node.is_pinned());
assert_eq!(node.pin_count, 1);
node.unpin();
assert!(!node.is_pinned());
assert_eq!(node.pin_count, 0);
}
#[test]
fn test_is_evictable_always_true() {
let node = InNode::new(1, MAIN_LEVEL | 2, 4);
assert!(node.is_evictable());
let bin = InNode::new(1, BIN_LEVEL, 4);
assert!(bin.is_evictable());
}
#[test]
fn test_memory_accounting() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"k".to_vec(), Lsn::from_u64(1), 0).unwrap();
assert_eq!(node.get_in_memory_size(), 0);
node.set_in_memory_size(512);
assert_eq!(node.get_in_memory_size(), 512);
assert_eq!(node.get_budgeted_memory_size(), 512);
node.accumulated_delta = 100;
assert_eq!(node.get_budgeted_memory_size(), 412);
let sz = node.reset_and_get_memory_size();
assert_eq!(sz, 512);
assert_eq!(node.accumulated_delta, 0);
}
#[test]
fn test_bump_generation() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
assert_eq!(node.generation(), 0);
let g1 = node.bump_generation();
assert_eq!(g1, 1);
assert_eq!(node.generation(), 1);
node.set_generation(99);
assert_eq!(node.generation(), 99);
let g2 = node.bump_generation();
assert_eq!(g2, 100);
}
#[test]
fn test_read_from_log_small_buffer_error() {
let buf = vec![0u8; 10];
let result = InNode::read_from_log(&buf, BIN_LEVEL);
assert!(result.is_err());
match result {
Err(InError::Deserialization(msg)) => {
assert!(msg.contains("buffer too small"), "msg={}", msg);
}
_ => panic!("Expected Deserialization error"),
}
}
#[test]
fn test_read_from_log_id_key_past_buffer_error() {
let mut in_node = InNode::new(42, BIN_LEVEL, 4);
in_node.set_node_id(1);
let mut buf = Vec::new();
in_node.write_to_log(&mut buf);
buf[38] = 0xFF;
buf[39] = 0xFF;
buf.truncate(40);
let result = InNode::read_from_log(&buf, BIN_LEVEL);
assert!(result.is_err());
}
#[test]
fn test_read_from_log_entry_key_past_buffer_error() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"hello".to_vec(), Lsn::from_u64(10), 0).unwrap();
let mut buf = Vec::new();
node.write_to_log(&mut buf);
buf[40] = 0xFF;
buf[41] = 0x00;
buf.truncate(44);
let result = InNode::read_from_log(&buf, BIN_LEVEL);
assert!(result.is_err());
}
#[test]
fn test_read_from_log_entry_lsn_past_buffer_error() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"hi".to_vec(), Lsn::from_u64(10), 0).unwrap();
let mut buf = Vec::new();
node.write_to_log(&mut buf);
buf.truncate(47);
let result = InNode::read_from_log(&buf, BIN_LEVEL);
assert!(result.is_err());
}
#[test]
fn test_read_from_log_entry_state_past_buffer_error() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"hi".to_vec(), Lsn::from_u64(10), 0).unwrap();
let mut buf = Vec::new();
node.write_to_log(&mut buf);
buf.truncate(52);
let result = InNode::read_from_log(&buf, BIN_LEVEL);
assert!(result.is_err());
}
#[test]
fn test_read_from_log_entry_key_len_past_buffer_error() {
let mut node = InNode::new(1, BIN_LEVEL, 4);
node.insert_entry(b"x".to_vec(), Lsn::from_u64(1), 0).unwrap();
let mut buf = Vec::new();
node.write_to_log(&mut buf);
buf.truncate(40);
let result = InNode::read_from_log(&buf, BIN_LEVEL);
assert!(result.is_err());
}
#[test]
fn test_read_from_log_no_identifier_key_and_no_entry_keys() {
let mut node = InNode::new(5, MAIN_LEVEL | 2, 4);
node.set_node_id(77);
node.entry_keys[0] = None;
node.entry_lsns[0] = Lsn::from_u64(7);
node.entry_states[0] = 0;
node.n_entries = 1;
let mut buf = Vec::new();
node.write_to_log(&mut buf);
let restored = InNode::read_from_log(&buf, MAIN_LEVEL | 2).unwrap();
assert_eq!(restored.node_id(), 77);
assert_eq!(restored.n_entries(), 1);
assert!(restored.identifier_key().is_none());
assert_eq!(restored.get_lsn(0), Lsn::from_u64(7));
}
}