use crate::{
error::Error,
storage::sstable::bti::{
encoder::ByteComparableEncoder,
node::{
BtiNode, BtiNodeData, BtiNodeType, BtiResult, PayloadRef, SizedPointer, Transition,
TrieNavigator,
},
},
types::Value,
};
use std::collections::HashMap;
use std::io::{Read, Seek, SeekFrom};
fn classify_node_nibble(nibble: u8) -> BtiResult<BtiNodeType> {
match nibble {
0 => Ok(BtiNodeType::PayloadOnly),
1..=4 => Ok(BtiNodeType::Single),
5..=9 => Ok(BtiNodeType::Sparse),
10..=15 => Ok(BtiNodeType::Dense),
other => Err(Error::Parse(format!(
"Invalid BTI node type nibble: {}",
other
))),
}
}
fn pointer_bytes_for_ordinal(ordinal: u8) -> u8 {
match ordinal {
0 => 0, 1 => 0, 2 => 1, 3 => 0, 4 => 2, 5 => 1, 6 => 0, 7 => 2, 8 => 3, 9 => 5, 10 => 0, 11 => 2, 12 => 3, 13 => 4, 14 => 5, 15 => 8, _ => 0,
}
}
fn parse_bti_node(data: &[u8], offset: u64) -> BtiResult<BtiNode> {
if data.is_empty() {
return Err(Error::Parse("Empty BTI node data".to_string()));
}
let header_byte = data[0];
let ordinal = (header_byte >> 4) & 0x0F;
let payload_flags = header_byte & 0x0F;
let has_payload = payload_flags != 0;
let node_type = classify_node_nibble(ordinal)?;
match node_type {
BtiNodeType::PayloadOnly => {
if !has_payload {
return Err(Error::Parse(
"PayloadOnly node has no payload flags set".to_string(),
));
}
let payload = parse_payload_ref(&data[1..])?;
Ok(BtiNode {
node_type,
level: 0,
key_prefix: Vec::new(),
data: BtiNodeData::PayloadOnly { payload },
})
}
BtiNodeType::Single => {
match ordinal {
1 => {
if data.len() < 2 {
return Err(Error::Parse(
"SingleNoPayload4 node data too short".to_string(),
));
}
let delta = (header_byte & 0x0F) as u64;
let transition_byte = data[1];
let child_offset = offset.saturating_sub(delta);
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Single {
transition: Transition::new(
transition_byte,
SizedPointer::new(child_offset),
),
},
})
}
3 => {
if data.len() < 3 {
return Err(Error::Parse(
"SingleNoPayload12 node data too short".to_string(),
));
}
let delta = (((header_byte & 0x0F) as u64) << 8) | (data[1] as u64);
let transition_byte = data[2];
let child_offset = offset.saturating_sub(delta);
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Single {
transition: Transition::new(
transition_byte,
SizedPointer::new(child_offset),
),
},
})
}
_ => {
let ptr_bytes = pointer_bytes_for_ordinal(ordinal) as usize;
let needed = 2 + ptr_bytes;
if data.len() < needed {
return Err(Error::Parse(format!(
"Single node (ordinal {}) data too short: need {} bytes, have {}",
ordinal,
needed,
data.len()
)));
}
let transition_byte = data[1];
let delta = read_be_unsigned(&data[2..2 + ptr_bytes]);
let child_offset = offset.saturating_sub(delta);
let transition =
Transition::new(transition_byte, SizedPointer::new(child_offset));
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Single { transition },
})
}
}
}
BtiNodeType::Sparse => {
if data.len() < 2 {
return Err(Error::Parse("Sparse node data too short".to_string()));
}
let count = data[1] as usize;
if count == 0 {
return Err(Error::Parse(
"Sparse node must have at least one transition".to_string(),
));
}
let bytes_start = 2;
let pointers_start = bytes_start + count;
if ordinal == 6 {
let packed_len = (count * 3).div_ceil(2); let needed = pointers_start + packed_len;
if data.len() < needed {
return Err(Error::Parse(format!(
"Sparse12 node data too short: need {}, have {}",
needed,
data.len()
)));
}
let mut transitions = Vec::with_capacity(count);
for i in 0..count {
let t_byte = data[bytes_start + i];
let delta = read_12bit_packed(&data[pointers_start..], i);
let child_offset = offset.saturating_sub(delta);
transitions.push(Transition::new(t_byte, SizedPointer::new(child_offset)));
}
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Sparse { transitions },
})
} else {
let ptr_bytes = pointer_bytes_for_ordinal(ordinal) as usize;
let needed = pointers_start + count * ptr_bytes;
if data.len() < needed {
return Err(Error::Parse(format!(
"Sparse node (ordinal {}) data too short: need {}, have {}",
ordinal,
needed,
data.len()
)));
}
let mut transitions = Vec::with_capacity(count);
for i in 0..count {
let t_byte = data[bytes_start + i];
let ptr_off = pointers_start + i * ptr_bytes;
let delta = read_be_unsigned(&data[ptr_off..ptr_off + ptr_bytes]);
let child_offset = offset.saturating_sub(delta);
transitions.push(Transition::new(t_byte, SizedPointer::new(child_offset)));
}
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Sparse { transitions },
})
}
}
BtiNodeType::Dense => {
if data.len() < 3 {
return Err(Error::Parse("Dense node data too short".to_string()));
}
let start_byte = data[1];
let range_len = data[2] as usize + 1;
if ordinal == 10 {
let packed_len = (range_len * 3).div_ceil(2);
let needed = 3 + packed_len;
if data.len() < needed {
return Err(Error::Parse(format!(
"Dense12 node data too short: need {}, have {}",
needed,
data.len()
)));
}
let mut children = Vec::with_capacity(range_len);
for i in 0..range_len {
let delta = read_12bit_packed(&data[3..], i);
let child = if delta == 0 {
None
} else {
Some(SizedPointer::new(offset.saturating_sub(delta)))
};
children.push(child);
}
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Dense {
start_byte,
children,
},
})
} else {
let ptr_bytes = pointer_bytes_for_ordinal(ordinal) as usize;
let needed = 3 + range_len * ptr_bytes;
if data.len() < needed {
return Err(Error::Parse(format!(
"Dense node (ordinal {}) data too short: need {}, have {}",
ordinal,
needed,
data.len()
)));
}
let mut children = Vec::with_capacity(range_len);
for i in 0..range_len {
let ptr_off = 3 + i * ptr_bytes;
let delta = read_be_unsigned(&data[ptr_off..ptr_off + ptr_bytes]);
let child = if delta == 0 {
None
} else {
Some(SizedPointer::new(offset.saturating_sub(delta)))
};
children.push(child);
}
Ok(BtiNode {
node_type,
level: 1,
key_prefix: Vec::new(),
data: BtiNodeData::Dense {
start_byte,
children,
},
})
}
}
}
}
fn read_be_unsigned(data: &[u8]) -> u64 {
let mut result = 0u64;
for &byte in data {
result = (result << 8) | (byte as u64);
}
result
}
fn read_12bit_packed(data: &[u8], index: usize) -> u64 {
let byte_offset = (3 * index) / 2;
if byte_offset + 1 >= data.len() {
return 0;
}
let word = ((data[byte_offset] as u16) << 8) | (data[byte_offset + 1] as u16);
let value = if (index & 1) == 0 {
word >> 4
} else {
word & 0x0FFF
};
value as u64
}
fn parse_payload_ref(data: &[u8]) -> BtiResult<PayloadRef> {
if data.len() < 12 {
return Err(Error::Parse(format!(
"PayloadRef data too short: need 12 bytes, have {}",
data.len()
)));
}
let offset = u64::from_be_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]);
let length = u32::from_be_bytes([data[8], data[9], data[10], data[11]]);
Ok(PayloadRef::new(offset, length))
}
pub const FLAG_HAS_HASH_BYTE: u8 = 8;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BtiPartitionLocation {
DataOffset(u64),
RowsOffset(u64),
}
pub fn decode_bti_partition_payload(
trie_data: &[u8],
payload_start: usize,
payload_bits: u8,
) -> BtiResult<BtiPartitionLocation> {
if payload_bits < FLAG_HAS_HASH_BYTE {
return Err(Error::Parse(format!(
"BTI payload_bits {payload_bits} < FLAG_HAS_HASH_BYTE (8); \
hash-byte-less payloads are not supported in Cassandra 5.0 BTI format"
)));
}
if payload_bits > 16 {
return Err(Error::Parse(format!(
"BTI payload_bits {payload_bits} > 16; invalid BTI partition leaf"
)));
}
let position_bytes = (payload_bits - FLAG_HAS_HASH_BYTE + 1) as usize;
let needed = 1 + position_bytes;
if payload_start + needed > trie_data.len() {
return Err(Error::Parse(format!(
"BTI partition payload at {payload_start} is too short: \
need {needed} bytes, have {}",
trie_data.len().saturating_sub(payload_start)
)));
}
let pos_data = &trie_data[payload_start + 1..payload_start + 1 + position_bytes];
let position: i64 = sized_ints_read_from_slice(pos_data)?;
if position < 0 {
let data_offset = !position as u64; Ok(BtiPartitionLocation::DataOffset(data_offset))
} else {
Ok(BtiPartitionLocation::RowsOffset(position as u64))
}
}
fn sized_ints_read_from_slice(data: &[u8]) -> BtiResult<i64> {
match data.len() {
0 => Ok(0),
1 => Ok(data[0] as i8 as i64),
2 => Ok(i16::from_be_bytes([data[0], data[1]]) as i64),
3 => {
let high = data[0] as i8 as i64;
let low = u16::from_be_bytes([data[1], data[2]]) as i64;
Ok((high << 16) | low)
}
4 => Ok(i32::from_be_bytes([data[0], data[1], data[2], data[3]]) as i64),
5 => {
let high = data[0] as i8 as i64;
let low = u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as i64;
Ok((high << 32) | low)
}
6 => {
let high = i16::from_be_bytes([data[0], data[1]]) as i64;
let low = u32::from_be_bytes([data[2], data[3], data[4], data[5]]) as i64;
Ok((high << 32) | low)
}
7 => {
let high1 = data[0] as i8 as i64;
let high2 = u16::from_be_bytes([data[1], data[2]]) as i64;
let low = u32::from_be_bytes([data[3], data[4], data[5], data[6]]) as i64;
Ok((high1 << 48) | (high2 << 32) | low)
}
8 => Ok(i64::from_be_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
])),
n => Err(Error::Parse(format!(
"SizedInts: invalid byte count {n} (expected 1–8)"
))),
}
}
fn parse_bti_node_for_traversal(trie_data: &[u8], node_offset: usize) -> BtiResult<BtiNode> {
if node_offset >= trie_data.len() {
return Err(Error::Parse(format!(
"BTI traversal: node_offset {node_offset} >= trie_data.len {}",
trie_data.len()
)));
}
let data = &trie_data[node_offset..];
let header_byte = data[0];
let ordinal = (header_byte >> 4) & 0x0F;
let payload_flags = header_byte & 0x0F;
let node_type = classify_node_nibble(ordinal)?;
match node_type {
BtiNodeType::PayloadOnly => {
if payload_flags == 0 {
return Err(Error::Parse(
"PayloadOnly node has no payload flags set".to_string(),
));
}
let stub = PayloadRef::new(0, 0);
Ok(BtiNode {
node_type,
level: 0,
key_prefix: Vec::new(),
data: BtiNodeData::PayloadOnly { payload: stub },
})
}
BtiNodeType::Single => {
parse_bti_node(data, node_offset as u64)
}
BtiNodeType::Sparse => parse_bti_node(data, node_offset as u64),
BtiNodeType::Dense => parse_bti_node(data, node_offset as u64),
}
}
fn find_next_child_offset(
trie_data: &[u8],
node_offset: usize,
search_byte: u8,
) -> BtiResult<Option<usize>> {
if node_offset >= trie_data.len() {
return Err(Error::Parse(format!(
"BTI node offset {node_offset} out of bounds (trie_data.len={})",
trie_data.len()
)));
}
let bti_node = parse_bti_node_for_traversal(trie_data, node_offset)?;
match bti_node.find_child(search_byte) {
Some(ptr) => {
let child = ptr.distance as usize; Ok(Some(child))
}
None => Ok(None),
}
}
fn read_node_payload(
trie_data: &[u8],
node_offset: usize,
) -> BtiResult<Option<BtiPartitionLocation>> {
if node_offset >= trie_data.len() {
return Err(Error::Parse(format!(
"BTI payload read: node_offset {node_offset} out of bounds"
)));
}
let header_byte = trie_data[node_offset];
let ordinal = (header_byte >> 4) & 0x0F;
let payload_flags = header_byte & 0x0F;
if ordinal == 1 || ordinal == 3 {
return Ok(None);
}
if ordinal == 0 {
if payload_flags == 0 {
return Err(Error::Parse(
"PayloadOnly node has zero payload_flags".to_string(),
));
}
let payload_start = node_offset + 1; Ok(Some(decode_bti_partition_payload(
trie_data,
payload_start,
payload_flags,
)?))
} else if payload_flags != 0 {
let node = parse_bti_node(&trie_data[node_offset..], node_offset as u64)?;
let payload_start = payload_start_in_node(&node, trie_data, node_offset)?;
Ok(Some(decode_bti_partition_payload(
trie_data,
payload_start,
payload_flags,
)?))
} else {
Ok(None)
}
}
fn payload_start_in_node(node: &BtiNode, trie_data: &[u8], node_offset: usize) -> BtiResult<usize> {
use BtiNodeData::*;
let header_byte = trie_data[node_offset];
let ordinal = (header_byte >> 4) & 0x0F;
let payload_offset = match &node.data {
PayloadOnly { .. } => {
node_offset + 1
}
Single { .. } => {
let ptr_bytes = pointer_bytes_for_ordinal(ordinal) as usize;
node_offset + 1 + 1 + ptr_bytes
}
Sparse { transitions } => {
let count = transitions.len();
let ptr_bytes = pointer_bytes_for_ordinal(ordinal) as usize;
let ptr_area = if ordinal == 6 {
(count * 3).div_ceil(2)
} else {
count * ptr_bytes
};
node_offset + 1 + 1 + count + ptr_area }
Dense { children, .. } => {
let range_len = children.len();
let ptr_bytes = pointer_bytes_for_ordinal(ordinal) as usize;
let ptr_area = if ordinal == 10 {
(range_len * 3).div_ceil(2)
} else {
range_len * ptr_bytes
};
node_offset + 1 + 1 + 1 + ptr_area }
};
Ok(payload_offset)
}
fn walk_bti_trie(
trie_data: &[u8],
root_offset: usize,
encoded_key: &[u8],
) -> BtiResult<Option<BtiPartitionLocation>> {
let mut current_offset = root_offset;
let mut key_pos = 0;
loop {
if current_offset >= trie_data.len() {
return Err(Error::Parse(format!(
"BTI trie walk: offset {current_offset} out of bounds (trie size {})",
trie_data.len()
)));
}
let header_byte = trie_data[current_offset];
let ordinal = (header_byte >> 4) & 0x0F;
let payload_flags = header_byte & 0x0F;
let is_leaf = ordinal == 0;
if is_leaf {
return read_node_payload(trie_data, current_offset);
}
if key_pos >= encoded_key.len() {
if payload_flags != 0 {
return read_node_payload(trie_data, current_offset);
}
return Ok(None);
}
let next_byte = encoded_key[key_pos];
match find_next_child_offset(trie_data, current_offset, next_byte)? {
Some(child_offset) => {
current_offset = child_offset;
key_pos += 1;
}
None => {
return Ok(None);
}
}
}
}
pub fn lookup_partition_in_bti_file<R: Read + Seek>(
reader: &mut R,
encoded_key: &[u8],
) -> BtiResult<Option<BtiPartitionLocation>> {
let file_size = reader.seek(SeekFrom::End(0))?;
if file_size < 8 {
return Err(Error::Parse(format!(
"BTI Partitions.db is too small ({file_size} bytes; need at least 8 for footer)"
)));
}
reader.seek(SeekFrom::End(-8))?;
let mut footer_buf = [0u8; 8];
reader.read_exact(&mut footer_buf)?;
let root_offset = u64::from_be_bytes(footer_buf);
let trie_size = file_size - 8;
if root_offset >= trie_size {
return Err(Error::Parse(format!(
"BTI Partitions.db: root_offset {root_offset} >= trie_size {trie_size}"
)));
}
reader.seek(SeekFrom::Start(0))?;
let mut trie_data = vec![0u8; trie_size as usize];
reader.read_exact(&mut trie_data)?;
walk_bti_trie(&trie_data, root_offset as usize, encoded_key)
}
pub fn encode_partition_key_for_bti_trie(raw_key_bytes: &[u8]) -> [u8; 9] {
use crate::util::cassandra_murmur3::cassandra_murmur3_token;
let token: i64 = cassandra_murmur3_token(raw_key_bytes);
let bc: u64 = (token as u64) ^ 0x8000_0000_0000_0000u64;
let bc_bytes = bc.to_be_bytes();
let mut key = [0u8; 9];
key[0] = 0x40; key[1..9].copy_from_slice(&bc_bytes);
key
}
pub fn lookup_raw_key_in_bti_partitions_db<R: Read + Seek>(
partitions_db_reader: &mut R,
raw_key_bytes: &[u8],
) -> BtiResult<Option<BtiPartitionLocation>> {
let encoded = encode_partition_key_for_bti_trie(raw_key_bytes);
lookup_partition_in_bti_file(partitions_db_reader, &encoded)
}
const DFS_MAX_DEPTH: usize = 128;
fn load_bti_trie_via_footer<R: Read + Seek>(reader: &mut R) -> BtiResult<(Vec<u8>, usize)> {
let file_size = reader.seek(SeekFrom::End(0))?;
if file_size < 8 {
return Err(Error::Parse(format!(
"BTI file too small ({file_size} bytes; need at least 8 for footer)"
)));
}
reader.seek(SeekFrom::End(-8))?;
let mut footer = [0u8; 8];
reader.read_exact(&mut footer)?;
let root_offset = u64::from_be_bytes(footer);
let trie_size = file_size - 8;
if root_offset >= trie_size {
return Err(Error::Parse(format!(
"BTI file: root_offset {root_offset} >= trie_size {trie_size}"
)));
}
reader.seek(SeekFrom::Start(0))?;
let mut trie_data = vec![0u8; trie_size as usize];
reader.read_exact(&mut trie_data)?;
Ok((trie_data, root_offset as usize))
}
fn ordered_children(node: &BtiNode) -> Vec<(u8, usize)> {
match &node.data {
BtiNodeData::PayloadOnly { .. } => Vec::new(),
BtiNodeData::Single { transition } => {
vec![(transition.byte, transition.child.distance as usize)]
}
BtiNodeData::Sparse { transitions } => {
let mut out: Vec<(u8, usize)> = transitions
.iter()
.map(|t| (t.byte, t.child.distance as usize))
.collect();
out.sort_by_key(|&(b, _)| b);
out
}
BtiNodeData::Dense {
start_byte,
children,
} => {
let mut out = Vec::new();
for (i, child) in children.iter().enumerate() {
if let Some(ptr) = child {
let transition_byte = start_byte.wrapping_add(i as u8);
out.push((transition_byte, ptr.distance as usize));
}
}
out
}
}
}
fn dfs_collect_in_order<T, F>(
trie_data: &[u8],
root_offset: usize,
mut decode_payload: F,
) -> BtiResult<Vec<(Vec<u8>, T)>>
where
F: FnMut(&[u8], usize) -> BtiResult<Option<T>>,
{
let mut results: Vec<(Vec<u8>, T)> = Vec::new();
let mut stack: Vec<(usize, Vec<u8>)> = vec![(root_offset, Vec::new())];
while let Some((node_offset, key_bytes)) = stack.pop() {
if key_bytes.len() > DFS_MAX_DEPTH {
return Err(Error::Parse(format!(
"BTI DFS exceeded max depth {DFS_MAX_DEPTH} (corrupt or cyclic trie)"
)));
}
if node_offset >= trie_data.len() {
return Err(Error::Parse(format!(
"BTI DFS: node_offset {node_offset} out of bounds (trie size {})",
trie_data.len()
)));
}
if let Some(payload) = decode_payload(trie_data, node_offset)? {
results.push((key_bytes.clone(), payload));
}
let node = parse_bti_node_for_traversal(trie_data, node_offset)?;
let children = ordered_children(&node);
for &(transition_byte, child_offset) in children.iter().rev() {
let mut child_key = key_bytes.clone();
child_key.push(transition_byte);
stack.push((child_offset, child_key));
}
}
Ok(results)
}
fn dfs_collect_partition_entries(
trie_data: &[u8],
root_offset: usize,
) -> BtiResult<Vec<(Vec<u8>, BtiPartitionLocation)>> {
dfs_collect_in_order(trie_data, root_offset, |data, off| {
read_node_payload(data, off)
})
}
pub fn iterate_partitions_in_bti_file<R: Read + Seek>(
reader: &mut R,
) -> BtiResult<Vec<(Vec<u8>, BtiPartitionLocation)>> {
let file_size = reader.seek(SeekFrom::End(0))?;
if file_size < 8 {
return Ok(Vec::new());
}
let (trie_data, root_offset) = load_bti_trie_via_footer(reader)?;
dfs_collect_partition_entries(&trie_data, root_offset)
}
pub const FLAG_OPEN_MARKER: u8 = 0x8;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BtiRowIndexEntry {
pub data_offset: u64,
pub open_marker: Option<(i32, i64)>,
}
fn read_unsigned_vint_from_slice(data: &[u8]) -> BtiResult<(u64, usize)> {
if data.is_empty() {
return Err(Error::Parse(
"Rows.db payload: unexpected end of data reading unsigned vint".to_string(),
));
}
let first = data[0];
let extra_bytes = first.leading_ones() as usize;
if extra_bytes > 8 {
return Err(Error::Parse(format!(
"Rows.db payload: invalid unsigned vint first byte 0x{first:02x}"
)));
}
let total = extra_bytes + 1;
if data.len() < total {
return Err(Error::Parse(format!(
"Rows.db payload: unsigned vint needs {total} bytes, have {}",
data.len()
)));
}
let mut value: u64 = if extra_bytes >= 8 {
0
} else {
let data_bits = 8 - extra_bytes - 1;
let mask = if data_bits == 0 {
0
} else {
(1u16 << data_bits) - 1
};
(first as u16 & mask) as u64
};
for &b in &data[1..total] {
value = (value << 8) | (b as u64);
}
Ok((value, total))
}
const DA_DELETION_TIME_LIVE_SENTINEL: u8 = 0x80;
const DA_DELETION_TIME_BODY_LEN: usize = 12;
fn decode_da_deletion_time(data: &[u8], start: usize) -> BtiResult<(Option<(i32, i64)>, usize)> {
if start >= data.len() {
return Err(Error::Parse(format!(
"DA DeletionTime: start {start} beyond buffer size {}",
data.len()
)));
}
if data[start] == DA_DELETION_TIME_LIVE_SENTINEL {
return Ok((None, 1));
}
if start + DA_DELETION_TIME_BODY_LEN > data.len() {
return Err(Error::Parse(format!(
"DA DeletionTime: non-live value needs {DA_DELETION_TIME_BODY_LEN} bytes, have {}",
data.len().saturating_sub(start)
)));
}
let b = &data[start..start + DA_DELETION_TIME_BODY_LEN];
let marked_for_delete_at = i64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
let local_deletion_time = u32::from_be_bytes([b[8], b[9], b[10], b[11]]) as i32;
Ok((
Some((local_deletion_time, marked_for_delete_at)),
DA_DELETION_TIME_BODY_LEN,
))
}
fn decode_bti_row_payload(
trie_data: &[u8],
payload_start: usize,
payload_bits: u8,
) -> BtiResult<BtiRowIndexEntry> {
if payload_start > trie_data.len() {
return Err(Error::Parse(format!(
"Rows.db payload start {payload_start} beyond trie size {}",
trie_data.len()
)));
}
let offset_bytes = (payload_bits & !FLAG_OPEN_MARKER) as usize;
if offset_bytes == 0 || offset_bytes > 7 {
return Err(Error::Parse(format!(
"Rows.db payload: invalid SizedInts byte count {offset_bytes} \
(payload_bits=0x{payload_bits:02x}); expected 1..=7"
)));
}
if payload_start + offset_bytes > trie_data.len() {
return Err(Error::Parse(format!(
"Rows.db payload: SizedInts offset needs {offset_bytes} bytes, have {}",
trie_data.len().saturating_sub(payload_start)
)));
}
let raw = sized_ints_read_from_slice(&trie_data[payload_start..payload_start + offset_bytes])?;
let data_offset = raw as u64;
let open_marker = if payload_bits & FLAG_OPEN_MARKER != 0 {
let dt_start = payload_start + offset_bytes;
let (deletion, _consumed) = decode_da_deletion_time(trie_data, dt_start)?;
deletion
} else {
None
};
Ok(BtiRowIndexEntry {
data_offset,
open_marker,
})
}
fn read_row_node_payload(
trie_data: &[u8],
node_offset: usize,
) -> BtiResult<Option<BtiRowIndexEntry>> {
if node_offset >= trie_data.len() {
return Err(Error::Parse(format!(
"Rows.db payload read: node_offset {node_offset} out of bounds"
)));
}
let header_byte = trie_data[node_offset];
let ordinal = (header_byte >> 4) & 0x0F;
let payload_flags = header_byte & 0x0F;
if ordinal == 1 || ordinal == 3 {
return Ok(None);
}
if ordinal == 0 {
if payload_flags == 0 {
return Err(Error::Parse(
"Rows.db PayloadOnly node has zero payload_flags".to_string(),
));
}
let payload_start = node_offset + 1;
Ok(Some(decode_bti_row_payload(
trie_data,
payload_start,
payload_flags,
)?))
} else if payload_flags != 0 {
let node = parse_bti_node(&trie_data[node_offset..], node_offset as u64)?;
let payload_start = payload_start_in_node(&node, trie_data, node_offset)?;
Ok(Some(decode_bti_row_payload(
trie_data,
payload_start,
payload_flags,
)?))
} else {
Ok(None)
}
}
fn dfs_collect_row_entries(
trie_data: &[u8],
root_offset: usize,
) -> BtiResult<Vec<(Vec<u8>, BtiRowIndexEntry)>> {
dfs_collect_in_order(trie_data, root_offset, |data, off| {
read_row_node_payload(data, off)
})
}
pub fn iterate_rows_in_bti_trie(
trie_data: &[u8],
root_offset: usize,
) -> BtiResult<Vec<(Vec<u8>, BtiRowIndexEntry)>> {
dfs_collect_row_entries(trie_data, root_offset)
}
pub type BtiRowIndexEntryWithKey = (Vec<u8>, BtiRowIndexEntry);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BtiRowIndexHeader {
pub data_position: u64,
pub trie_root: usize,
pub block_count: u32,
pub partition_deletion: Option<(i32, i64)>,
}
fn read_signed_vint_from_slice(data: &[u8]) -> BtiResult<(i64, usize)> {
let (u, n) = read_unsigned_vint_from_slice(data)?;
let value = ((u >> 1) as i64) ^ -((u & 1) as i64);
Ok((value, n))
}
#[doc(hidden)]
pub fn read_unsigned_vint_from_slice_for_test(data: &[u8]) -> BtiResult<(u64, usize)> {
read_unsigned_vint_from_slice(data)
}
#[doc(hidden)]
pub fn read_signed_vint_from_slice_for_test(data: &[u8]) -> BtiResult<(i64, usize)> {
read_signed_vint_from_slice(data)
}
pub fn resolve_rows_db_entry(rows_db: &[u8], rows_offset: usize) -> BtiResult<BtiRowIndexHeader> {
if rows_offset + 2 > rows_db.len() {
return Err(Error::Parse(format!(
"Rows.db entry: rows_offset {rows_offset} + 2 (key length) exceeds file size {}",
rows_db.len()
)));
}
let key_length = u16::from_be_bytes([rows_db[rows_offset], rows_db[rows_offset + 1]]) as usize;
let entry_start = rows_offset + 2 + key_length;
if entry_start > rows_db.len() {
return Err(Error::Parse(format!(
"Rows.db entry: key length {key_length} at offset {rows_offset} overruns file size {}",
rows_db.len()
)));
}
let base = rows_offset + key_length;
let mut cur = entry_start;
let (data_position, n) = read_unsigned_vint_from_slice(&rows_db[cur..])?;
cur += n;
let (root_delta, n) = read_signed_vint_from_slice(&rows_db[cur..])?;
cur += n;
let trie_root_signed = root_delta + base as i64;
if trie_root_signed < 0 || (trie_root_signed as usize) >= rows_db.len() {
return Err(Error::Parse(format!(
"Rows.db entry: recovered trie root {trie_root_signed} out of bounds \
(base={base}, delta={root_delta}, file size={})",
rows_db.len()
)));
}
let trie_root = trie_root_signed as usize;
let (block_count_u64, n) = read_unsigned_vint_from_slice(&rows_db[cur..])?;
cur += n;
let block_count = u32::try_from(block_count_u64).map_err(|_| {
Error::Parse(format!(
"Rows.db entry: implausible block count {block_count_u64}"
))
})?;
let partition_deletion = match decode_da_deletion_time(rows_db, cur) {
Ok((deletion, _consumed)) => deletion,
Err(_) => None,
};
Ok(BtiRowIndexHeader {
data_position,
trie_root,
block_count,
partition_deletion,
})
}
const OSS50_NEXT_COMPONENT: u8 = 0x40;
fn encode_varlen_oss50(bytes: &[u8], out: &mut Vec<u8>) {
for &b in bytes {
out.push(b);
if b == 0x00 {
out.push(0xFE);
}
}
out.push(0x00);
out.push(0xFF);
}
fn encode_clustering_component_oss50(value: &Value, out: &mut Vec<u8>) -> BtiResult<()> {
match value {
Value::Integer(v) => {
out.extend_from_slice(&((*v as u32) ^ 0x8000_0000).to_be_bytes());
Ok(())
}
Value::BigInt(v) | Value::Counter(v) => {
out.extend_from_slice(&((*v as u64) ^ 0x8000_0000_0000_0000).to_be_bytes());
Ok(())
}
Value::SmallInt(v) => {
out.extend_from_slice(&((*v as u16) ^ 0x8000).to_be_bytes());
Ok(())
}
Value::TinyInt(v) => {
out.push((*v as u8) ^ 0x80);
Ok(())
}
Value::Boolean(b) => {
out.push(if *b { 0x01 } else { 0x00 });
Ok(())
}
Value::Timestamp(v) => {
out.extend_from_slice(&((*v as u64) ^ 0x8000_0000_0000_0000).to_be_bytes());
Ok(())
}
Value::Uuid(bytes) => {
out.extend_from_slice(bytes);
Ok(())
}
Value::Text(s) => {
encode_varlen_oss50(s.as_bytes(), out);
Ok(())
}
Value::Blob(b) | Value::Inet(b) => {
encode_varlen_oss50(b, out);
Ok(())
}
other => Err(Error::Parse(format!(
"BTI range_query: byte-comparable encoding not implemented for {:?}",
other.data_type()
))),
}
}
pub fn encode_clustering_bound_oss50(values: &[Value]) -> BtiResult<Vec<u8>> {
let mut out = Vec::new();
for (i, v) in values.iter().enumerate() {
if i > 0 {
out.push(OSS50_NEXT_COMPONENT);
}
encode_clustering_component_oss50(v, &mut out)?;
}
Ok(out)
}
pub fn encode_clustering_bound_oss50_with_order(
values: &[Value],
is_reversed: &[bool],
) -> BtiResult<Vec<u8>> {
let mut out = Vec::new();
for (i, v) in values.iter().enumerate() {
if i > 0 {
out.push(OSS50_NEXT_COMPONENT);
}
if is_reversed.get(i).copied().unwrap_or(false) {
let mut scratch = Vec::new();
encode_clustering_component_oss50(v, &mut scratch)?;
for b in &scratch {
out.push(0xFF ^ *b);
}
} else {
encode_clustering_component_oss50(v, &mut out)?;
}
}
Ok(out)
}
pub fn select_row_index_blocks_for_range(
entries: &[(Vec<u8>, BtiRowIndexEntry)],
start: &[u8],
end: &[u8],
) -> Vec<BtiRowIndexEntry> {
if start > end || entries.is_empty() {
return Vec::new();
}
let mut out = Vec::new();
for (i, (sep_i, block)) in entries.iter().enumerate() {
let next_is_greater_than_start = match entries.get(i + 1) {
Some((sep_next, _)) => sep_next.as_slice() > start,
None => true, };
let overlaps = sep_i.as_slice() <= end && next_is_greater_than_start;
if overlaps {
out.push(block.clone());
}
}
out
}
pub fn iterate_rows_for_partition(
rows_db: &[u8],
rows_offset: usize,
) -> BtiResult<(BtiRowIndexHeader, Vec<BtiRowIndexEntryWithKey>)> {
let header = resolve_rows_db_entry(rows_db, rows_offset)?;
let entries = iterate_rows_in_bti_trie(rows_db, header.trie_root)?;
Ok((header, entries))
}
pub fn iterate_rows_in_bti_file<R: Read + Seek>(
reader: &mut R,
) -> BtiResult<Vec<(Vec<u8>, BtiRowIndexEntry)>> {
let file_size = reader.seek(SeekFrom::End(0))?;
if file_size < 8 {
return Ok(Vec::new());
}
let (trie_data, root_offset) = load_bti_trie_via_footer(reader)?;
iterate_rows_in_bti_trie(&trie_data, root_offset)
}
#[derive(Debug, Clone)]
pub struct BtiHeader {
pub magic: u32,
pub version: u16,
pub flags: u16,
pub root_offset: u64,
pub entry_count: u64,
pub metadata_size: u32,
}
impl BtiHeader {
pub const MAGIC: u32 = 0x6461_0000;
pub const VERSION: u16 = 0x0001;
pub fn placeholder() -> Self {
BtiHeader {
magic: Self::MAGIC,
version: Self::VERSION,
flags: 0,
root_offset: 0,
entry_count: 0,
metadata_size: 0,
}
}
pub fn parse(data: &[u8]) -> BtiResult<(Self, usize)> {
if data.len() < 24 {
return Err(Error::Parse("BTI header too short".to_string()));
}
let magic = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
if magic != Self::MAGIC {
return Err(Error::Parse(format!(
"Invalid BTI magic: 0x{:08x}, expected 0x{:08x}",
magic,
Self::MAGIC
)));
}
let version = u16::from_be_bytes([data[4], data[5]]);
if version != Self::VERSION {
return Err(Error::Parse(format!(
"Unsupported BTI version: 0x{:04x}, expected 0x{:04x}",
version,
Self::VERSION
)));
}
let flags = u16::from_be_bytes([data[6], data[7]]);
let root_offset = u64::from_be_bytes([
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
]);
let entry_count = u64::from_be_bytes([
data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
]);
let metadata_size = if data.len() >= 28 {
u32::from_be_bytes([data[24], data[25], data[26], data[27]])
} else {
0
};
let header = BtiHeader {
magic,
version,
flags,
root_offset,
entry_count,
metadata_size,
};
let header_size = if metadata_size > 0 { 28 } else { 24 };
Ok((header, header_size))
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(28);
bytes.extend_from_slice(&self.magic.to_be_bytes());
bytes.extend_from_slice(&self.version.to_be_bytes());
bytes.extend_from_slice(&self.flags.to_be_bytes());
bytes.extend_from_slice(&self.root_offset.to_be_bytes());
bytes.extend_from_slice(&self.entry_count.to_be_bytes());
if self.metadata_size > 0 {
bytes.extend_from_slice(&self.metadata_size.to_be_bytes());
}
bytes
}
}
pub struct PartitionsParser<R: Read + Seek> {
reader: R,
header: BtiHeader,
encoder: ByteComparableEncoder,
node_cache: HashMap<u64, BtiNode>,
}
impl<R: Read + Seek> PartitionsParser<R> {
pub fn new(mut reader: R) -> BtiResult<Self> {
reader.seek(SeekFrom::Start(0))?;
let mut header_data = vec![0u8; 28];
reader.read_exact(&mut header_data)?;
let (header, _) = BtiHeader::parse(&header_data)?;
Ok(Self {
reader,
header,
encoder: ByteComparableEncoder::new(),
node_cache: HashMap::new(),
})
}
pub fn lookup_partition(&mut self, partition_key: &[Value]) -> BtiResult<Option<PayloadRef>> {
let encoded_key = self.encoder.encode_composite_key(partition_key)?;
let mut navigator = TrieNavigator::new(self.header.root_offset);
self.lookup_in_trie(&mut navigator, &encoded_key)
}
fn lookup_in_trie(
&mut self,
navigator: &mut TrieNavigator,
encoded_key: &[u8],
) -> BtiResult<Option<PayloadRef>> {
let mut key_pos = 0;
loop {
let current_node = self.load_node(navigator.current_offset)?;
if current_node.is_leaf() {
return Ok(current_node.get_payload().cloned());
}
if let Some(payload) = current_node.get_payload() {
if key_pos >= encoded_key.len() {
return Ok(Some(payload.clone()));
}
}
if key_pos >= encoded_key.len() {
return Ok(current_node.get_payload().cloned());
}
let next_byte = encoded_key[key_pos];
if let Some(child_pointer) = current_node.find_child(next_byte) {
navigator.navigate_to_child(next_byte, child_pointer)?;
key_pos += 1;
} else {
return Ok(None);
}
}
}
fn load_node(&mut self, offset: u64) -> BtiResult<BtiNode> {
if let Some(cached_node) = self.node_cache.get(&offset) {
return Ok(cached_node.clone());
}
self.reader.seek(SeekFrom::Start(offset))?;
let mut node_data = vec![0u8; 4096]; let bytes_read = self.reader.read(&mut node_data)?;
node_data.truncate(bytes_read);
let node = self.parse_node_data(&node_data, offset)?;
self.node_cache.insert(offset, node.clone());
Ok(node)
}
fn parse_node_data(&self, data: &[u8], offset: u64) -> BtiResult<BtiNode> {
parse_bti_node(data, offset)
}
pub fn iterate_partitions(&mut self) -> BtiResult<PartitionIterator<'_, R>> {
PartitionIterator::new(self)
}
pub fn header(&self) -> &BtiHeader {
&self.header
}
pub fn get_stats(&self) -> BtiIndexStats {
BtiIndexStats {
entry_count: self.header.entry_count,
root_offset: self.header.root_offset,
cached_nodes: self.node_cache.len(),
}
}
}
pub struct RowsParser<R: Read + Seek> {
reader: R,
header: BtiHeader,
encoder: ByteComparableEncoder,
node_cache: HashMap<u64, BtiNode>,
}
impl<R: Read + Seek> RowsParser<R> {
pub fn new(mut reader: R) -> BtiResult<Self> {
reader.seek(SeekFrom::Start(0))?;
let mut header_data = vec![0u8; 28];
let header = match reader.read_exact(&mut header_data) {
Ok(()) => BtiHeader::parse(&header_data)
.map(|(h, _)| h)
.unwrap_or_else(|_| BtiHeader::placeholder()),
Err(_) => BtiHeader::placeholder(),
};
reader.seek(SeekFrom::Start(0))?;
Ok(Self {
reader,
header,
encoder: ByteComparableEncoder::new(),
node_cache: HashMap::new(),
})
}
pub fn lookup_row(&mut self, clustering_key: &[Value]) -> BtiResult<Option<PayloadRef>> {
let encoded_key = self.encoder.encode_composite_key(clustering_key)?;
let mut navigator = TrieNavigator::new(self.header.root_offset);
self.lookup_in_trie(&mut navigator, &encoded_key)
}
fn lookup_in_trie(
&mut self,
navigator: &mut TrieNavigator,
encoded_key: &[u8],
) -> BtiResult<Option<PayloadRef>> {
let mut key_pos = 0;
loop {
let current_node = self.load_node(navigator.current_offset)?;
if let Some(payload) = current_node.get_payload() {
if key_pos >= encoded_key.len() {
return Ok(Some(payload.clone()));
}
}
if key_pos >= encoded_key.len() {
return Ok(current_node.get_payload().cloned());
}
let next_byte = encoded_key[key_pos];
if let Some(child_pointer) = current_node.find_child(next_byte) {
navigator.navigate_to_child(next_byte, child_pointer)?;
key_pos += 1;
} else {
return Ok(None);
}
}
}
fn load_node(&mut self, offset: u64) -> BtiResult<BtiNode> {
if let Some(cached_node) = self.node_cache.get(&offset) {
return Ok(cached_node.clone());
}
self.reader.seek(SeekFrom::Start(offset))?;
let mut node_data = vec![0u8; 4096]; let bytes_read = self.reader.read(&mut node_data)?;
node_data.truncate(bytes_read);
let node = self.parse_node_data(&node_data, offset)?;
self.node_cache.insert(offset, node.clone());
Ok(node)
}
fn parse_node_data(&self, data: &[u8], offset: u64) -> BtiResult<BtiNode> {
parse_bti_node(data, offset)
}
pub fn range_query_encoded(
&mut self,
rows_offset: usize,
encoded_start: &[u8],
encoded_end: &[u8],
) -> BtiResult<(BtiRowIndexHeader, Vec<BtiRowIndexEntry>)> {
let trie_data = self.read_full_rows_db()?;
let header = resolve_rows_db_entry(&trie_data, rows_offset)?;
let all = iterate_rows_in_bti_trie(&trie_data, header.trie_root)?;
let blocks = select_row_index_blocks_for_range(&all, encoded_start, encoded_end);
Ok((header, blocks))
}
pub fn range_query(
&mut self,
rows_offset: usize,
start_key: &[Value],
end_key: &[Value],
) -> BtiResult<Vec<BtiRowIndexEntry>> {
let encoded_start = encode_clustering_bound_oss50(start_key)?;
let encoded_end = encode_clustering_bound_oss50(end_key)?;
if encoded_start > encoded_end {
return Ok(Vec::new());
}
let (_, blocks) = self.range_query_encoded(rows_offset, &encoded_start, &encoded_end)?;
Ok(blocks)
}
pub fn range_query_with_order(
&mut self,
rows_offset: usize,
start_key: &[Value],
end_key: &[Value],
is_reversed: &[bool],
) -> BtiResult<Vec<BtiRowIndexEntry>> {
let encoded_start = encode_clustering_bound_oss50_with_order(start_key, is_reversed)?;
let encoded_end = encode_clustering_bound_oss50_with_order(end_key, is_reversed)?;
if encoded_start > encoded_end {
return Ok(Vec::new());
}
let (_, blocks) = self.range_query_encoded(rows_offset, &encoded_start, &encoded_end)?;
Ok(blocks)
}
fn read_full_rows_db(&mut self) -> BtiResult<Vec<u8>> {
let file_size = self.reader.seek(SeekFrom::End(0))?;
self.reader.seek(SeekFrom::Start(0))?;
let mut buf = vec![0u8; file_size as usize];
self.reader.read_exact(&mut buf)?;
Ok(buf)
}
pub fn iterate_rows(&mut self, rows_offset: usize) -> BtiResult<RowIterator<'_, R>> {
RowIterator::new(self, rows_offset)
}
pub fn header(&self) -> &BtiHeader {
&self.header
}
}
pub struct PartitionIterator<'a, R: Read + Seek> {
#[allow(dead_code)]
parser: &'a mut PartitionsParser<R>,
entries: std::vec::IntoIter<(Vec<u8>, BtiPartitionLocation)>,
pending_error: Option<Error>,
}
impl<'a, R: Read + Seek> PartitionIterator<'a, R> {
fn new(parser: &'a mut PartitionsParser<R>) -> BtiResult<Self> {
let (entries, pending_error) = match load_bti_trie_via_footer(&mut parser.reader)
.and_then(|(trie, root)| dfs_collect_partition_entries(&trie, root))
{
Ok(v) => (v, None),
Err(e) => (Vec::new(), Some(e)),
};
Ok(Self {
parser,
entries: entries.into_iter(),
pending_error,
})
}
}
impl<'a, R: Read + Seek> Iterator for PartitionIterator<'a, R> {
type Item = BtiResult<(Vec<u8>, BtiPartitionLocation)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(err) = self.pending_error.take() {
return Some(Err(err));
}
self.entries.next().map(Ok)
}
}
pub struct RowIterator<'a, R: Read + Seek> {
#[allow(dead_code)]
parser: &'a mut RowsParser<R>,
entries: std::vec::IntoIter<(Vec<u8>, BtiRowIndexEntry)>,
pending_error: Option<Error>,
}
impl<'a, R: Read + Seek> RowIterator<'a, R> {
fn new(parser: &'a mut RowsParser<R>, rows_offset: usize) -> BtiResult<Self> {
let file_size = parser.reader.seek(SeekFrom::End(0))?;
if file_size == 0 {
return Ok(Self {
parser,
entries: Vec::new().into_iter(),
pending_error: None,
});
}
let (entries, pending_error) = match parser.read_full_rows_db().and_then(|trie| {
let header = resolve_rows_db_entry(&trie, rows_offset)?;
iterate_rows_in_bti_trie(&trie, header.trie_root)
}) {
Ok(v) => (v, None),
Err(e) => (Vec::new(), Some(e)),
};
Ok(Self {
parser,
entries: entries.into_iter(),
pending_error,
})
}
}
impl<'a, R: Read + Seek> Iterator for RowIterator<'a, R> {
type Item = BtiResult<(Vec<u8>, BtiRowIndexEntry)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(err) = self.pending_error.take() {
return Some(Err(err));
}
self.entries.next().map(Ok)
}
}
#[derive(Debug, Clone)]
pub struct BtiIndexStats {
pub entry_count: u64,
pub root_offset: u64,
pub cached_nodes: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
fn make_bti_file(root_node_bytes: Vec<u8>) -> Vec<u8> {
let root_offset: u64 = 64; let mut data = Vec::new();
data.extend_from_slice(&BtiHeader::MAGIC.to_be_bytes());
data.extend_from_slice(&BtiHeader::VERSION.to_be_bytes());
data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&root_offset.to_be_bytes());
data.extend_from_slice(&1u64.to_be_bytes()); data.extend_from_slice(&0u32.to_be_bytes()); while data.len() < root_offset as usize {
data.push(0);
}
data.extend(root_node_bytes);
data
}
fn payload_only_node(data_offset: u64, length: u32) -> Vec<u8> {
let mut v = vec![0x01u8]; v.extend_from_slice(&data_offset.to_be_bytes());
v.extend_from_slice(&length.to_be_bytes());
v
}
fn single8_node(payload_flags: u8, transition: u8, delta: u8) -> Vec<u8> {
vec![0x20 | (payload_flags & 0x0F), transition, delta]
}
fn single_nopayload4_node(delta4: u8, transition: u8) -> Vec<u8> {
vec![0x10 | (delta4 & 0x0F), transition]
}
fn single_nopayload12_node(delta: u16, transition: u8) -> Vec<u8> {
vec![
0x30 | ((delta >> 8) as u8 & 0x0F),
(delta & 0xFF) as u8,
transition,
]
}
fn single16_node(payload_flags: u8, transition: u8, delta: u16) -> Vec<u8> {
let mut v = vec![0x40 | (payload_flags & 0x0F), transition];
v.extend_from_slice(&delta.to_be_bytes());
v
}
fn sparse8_node(payload_flags: u8, pairs: &[(u8, u8)]) -> Vec<u8> {
let mut v = vec![0x50 | (payload_flags & 0x0F), pairs.len() as u8];
for &(t, _) in pairs {
v.push(t);
}
for &(_, d) in pairs {
v.push(d);
}
v
}
fn dense16_node(payload_flags: u8, start: u8, deltas: &[u16]) -> Vec<u8> {
let len = deltas.len() as u8;
let mut v = vec![0xB0 | (payload_flags & 0x0F), start, len - 1];
for &d in deltas {
v.extend_from_slice(&d.to_be_bytes());
}
v
}
fn long_dense_node(payload_flags: u8, start: u8, deltas: &[u64]) -> Vec<u8> {
let len = deltas.len() as u8;
let mut v = vec![0xF0 | (payload_flags & 0x0F), start, len - 1];
for &d in deltas {
v.extend_from_slice(&d.to_be_bytes());
}
v
}
fn sparse12_node(payload_flags: u8, pairs: &[(u8, u16)]) -> Vec<u8> {
let count = pairs.len();
let mut v = vec![0x60 | (payload_flags & 0x0F), count as u8];
for &(t, _) in pairs {
v.push(t);
}
let mut i = 0;
while i + 2 <= count {
let p0 = pairs[i].1 as u32;
let p1 = pairs[i + 1].1 as u32;
v.push((p0 >> 4) as u8);
v.push(((p0 << 4) | (p1 >> 8)) as u8);
v.push((p1 & 0xFF) as u8);
i += 2;
}
if i < count {
let pd = pairs[i].1 as u32;
let s = (pd << 4) as u16;
v.extend_from_slice(&s.to_be_bytes());
}
v
}
fn sparse24_node(payload_flags: u8, pairs: &[(u8, u32)]) -> Vec<u8> {
let count = pairs.len();
let mut v = vec![0x80 | (payload_flags & 0x0F), count as u8];
for &(t, _) in pairs {
v.push(t);
}
for &(_, d) in pairs {
v.push(((d >> 16) & 0xFF) as u8);
v.push(((d >> 8) & 0xFF) as u8);
v.push((d & 0xFF) as u8);
}
v
}
fn sparse40_node(payload_flags: u8, pairs: &[(u8, u64)]) -> Vec<u8> {
let count = pairs.len();
let mut v = vec![0x90 | (payload_flags & 0x0F), count as u8];
for &(t, _) in pairs {
v.push(t);
}
for &(_, d) in pairs {
v.push(((d >> 32) & 0xFF) as u8);
v.push(((d >> 24) & 0xFF) as u8);
v.push(((d >> 16) & 0xFF) as u8);
v.push(((d >> 8) & 0xFF) as u8);
v.push((d & 0xFF) as u8);
}
v
}
fn dense12_node(payload_flags: u8, start: u8, deltas: &[u16]) -> Vec<u8> {
let range_len = deltas.len();
let mut v = vec![0xA0 | (payload_flags & 0x0F), start, (range_len - 1) as u8];
let mut carry: u8 = 0;
for (i, &d) in deltas.iter().enumerate() {
let val = d as u32;
if (i & 1) == 0 {
v.push((val >> 4) as u8);
carry = (val << 4) as u8;
} else {
v.push(carry | (val >> 8) as u8);
v.push((val & 0xFF) as u8);
carry = 0;
}
}
if (range_len & 1) == 1 {
v.push(carry);
}
v
}
#[test]
fn regression_rows_parser_single_node_not_mislabeled_as_payload_only() {
let node_bytes = single8_node(0, b'a', 5);
let offset: u64 = 100;
let node = parse_bti_node(&node_bytes, offset)
.expect("parse_bti_node must succeed for a valid Single8 node");
assert_eq!(
node.node_type,
BtiNodeType::Single,
"Single8 node (nibble 0x2) was mislabeled as {:?} — regression from #647 stub",
node.node_type,
);
match &node.data {
BtiNodeData::Single { transition } => {
assert_eq!(transition.byte, b'a');
assert_eq!(
transition.child.distance, 95,
"child offset should be parent(100) - delta(5) = 95"
);
}
other => panic!("Expected BtiNodeData::Single, got {:?}", other),
}
}
#[test]
fn parse_bti_node_payload_only_correct_type_and_offsets() {
let node = payload_only_node(0xDEAD_BEEF_0000_1234, 42);
let parsed = parse_bti_node(&node, 0).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::PayloadOnly);
match &parsed.data {
BtiNodeData::PayloadOnly { payload } => {
assert_eq!(payload.offset, 0xDEAD_BEEF_0000_1234);
assert_eq!(payload.length, 42);
}
other => panic!("Expected PayloadOnly, got {:?}", other),
}
}
#[test]
fn parse_bti_node_payload_only_no_payload_flags_is_error() {
let node_bytes = vec![0x00u8]; let err = parse_bti_node(&node_bytes, 0);
assert!(err.is_err(), "PayloadOnly with flags=0 should be an error");
}
#[test]
fn parse_bti_node_single_nopayload4_ordinal1() {
let node_bytes = single_nopayload4_node(3, b'x');
let parsed = parse_bti_node(&node_bytes, 50).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Single);
match &parsed.data {
BtiNodeData::Single { transition } => {
assert_eq!(transition.byte, b'x');
assert_eq!(transition.child.distance, 47); }
other => panic!("Expected Single, got {:?}", other),
}
}
#[test]
fn parse_bti_node_single8_ordinal2() {
let node_bytes = single8_node(0, b'z', 10);
let parsed = parse_bti_node(&node_bytes, 200).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Single);
match &parsed.data {
BtiNodeData::Single { transition } => {
assert_eq!(transition.byte, b'z');
assert_eq!(transition.child.distance, 190); }
other => panic!("Expected Single, got {:?}", other),
}
}
#[test]
fn parse_bti_node_single_nopayload12_ordinal3() {
let node_bytes = single_nopayload12_node(0x123, b'k');
let parsed = parse_bti_node(&node_bytes, 1000).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Single);
match &parsed.data {
BtiNodeData::Single { transition } => {
assert_eq!(transition.byte, b'k');
assert_eq!(transition.child.distance, 1000 - 0x123);
}
other => panic!("Expected Single, got {:?}", other),
}
}
#[test]
fn parse_bti_node_single16_ordinal4() {
let node_bytes = single16_node(0, b'm', 0x0400);
let parsed = parse_bti_node(&node_bytes, 2048).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Single);
match &parsed.data {
BtiNodeData::Single { transition } => {
assert_eq!(transition.byte, b'm');
assert_eq!(transition.child.distance, 2048 - 1024);
}
other => panic!("Expected Single, got {:?}", other),
}
}
#[test]
fn parse_bti_node_sparse8_ordinal5_two_transitions() {
let node_bytes = sparse8_node(0, &[(b'a', 10), (b'b', 20)]);
let parsed = parse_bti_node(&node_bytes, 100).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Sparse);
match &parsed.data {
BtiNodeData::Sparse { transitions } => {
assert_eq!(transitions.len(), 2);
assert_eq!(transitions[0].byte, b'a');
assert_eq!(transitions[0].child.distance, 90); assert_eq!(transitions[1].byte, b'b');
assert_eq!(transitions[1].child.distance, 80); }
other => panic!("Expected Sparse, got {:?}", other),
}
}
#[test]
fn parse_bti_node_sparse16_ordinal7_three_transitions() {
let payload_flags = 0u8;
let pairs: &[(u8, u16)] = &[(b'x', 0x0010), (b'y', 0x0020), (b'z', 0x0030)];
let mut node_bytes = vec![0x70 | payload_flags, pairs.len() as u8];
for &(t, _) in pairs {
node_bytes.push(t);
}
for &(_, d) in pairs {
node_bytes.extend_from_slice(&d.to_be_bytes());
}
let parsed = parse_bti_node(&node_bytes, 0x100).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Sparse);
match &parsed.data {
BtiNodeData::Sparse { transitions } => {
assert_eq!(transitions.len(), 3);
assert_eq!(transitions[0].byte, b'x');
assert_eq!(transitions[0].child.distance, 0x100 - 0x0010);
assert_eq!(transitions[2].byte, b'z');
assert_eq!(transitions[2].child.distance, 0x100 - 0x0030);
}
other => panic!("Expected Sparse, got {:?}", other),
}
}
#[test]
fn parse_bti_node_sparse12_ordinal6_count1_exact_minimal_5_bytes() {
let node_bytes = sparse12_node(0, &[(b'a', 0xABC)]);
assert_eq!(
node_bytes.len(),
5,
"Sparse12 count=1 must be exactly 5 bytes (was over-counted as 6 with old formula)"
);
let offset: u64 = 0x1000;
let parsed = parse_bti_node(&node_bytes, offset)
.expect("exact-minimal Sparse12 count=1 must parse successfully");
assert_eq!(parsed.node_type, BtiNodeType::Sparse);
match &parsed.data {
BtiNodeData::Sparse { transitions } => {
assert_eq!(transitions.len(), 1);
assert_eq!(transitions[0].byte, b'a');
assert_eq!(
transitions[0].child.distance,
offset - 0xABC,
"child offset = parent(0x1000) - delta(0xABC) = 0x544"
);
}
other => panic!("Expected BtiNodeData::Sparse, got {:?}", other),
}
}
#[test]
fn parse_bti_node_sparse12_ordinal6_count2_exact_minimal_7_bytes() {
let node_bytes = sparse12_node(0, &[(b'x', 0x100), (b'y', 0x200)]);
assert_eq!(
node_bytes.len(),
7,
"Sparse12 count=2 must be exactly 7 bytes"
);
let offset: u64 = 0x800;
let parsed = parse_bti_node(&node_bytes, offset)
.expect("exact-minimal Sparse12 count=2 must parse successfully");
assert_eq!(parsed.node_type, BtiNodeType::Sparse);
match &parsed.data {
BtiNodeData::Sparse { transitions } => {
assert_eq!(transitions.len(), 2);
assert_eq!(transitions[0].byte, b'x');
assert_eq!(transitions[0].child.distance, offset - 0x100);
assert_eq!(transitions[1].byte, b'y');
assert_eq!(transitions[1].child.distance, offset - 0x200);
}
other => panic!("Expected BtiNodeData::Sparse, got {:?}", other),
}
}
#[test]
fn parse_bti_node_sparse24_ordinal8_count1_exact_minimal_6_bytes() {
let node_bytes = sparse24_node(0, &[(b'p', 0x010203)]);
assert_eq!(
node_bytes.len(),
6,
"Sparse24 count=1 must be exactly 6 bytes"
);
let offset: u64 = 0x20000;
let parsed = parse_bti_node(&node_bytes, offset)
.expect("exact-minimal Sparse24 count=1 must parse successfully");
assert_eq!(parsed.node_type, BtiNodeType::Sparse);
match &parsed.data {
BtiNodeData::Sparse { transitions } => {
assert_eq!(transitions.len(), 1);
assert_eq!(transitions[0].byte, b'p');
assert_eq!(transitions[0].child.distance, offset - 0x010203);
}
other => panic!("Expected BtiNodeData::Sparse, got {:?}", other),
}
}
#[test]
fn parse_bti_node_sparse40_ordinal9_count1_exact_minimal_8_bytes() {
let delta: u64 = 0x0000_0001_0000;
let node_bytes = sparse40_node(0, &[(b'q', delta)]);
assert_eq!(
node_bytes.len(),
8,
"Sparse40 count=1 must be exactly 8 bytes"
);
let offset: u64 = 0x0010_0000;
let parsed = parse_bti_node(&node_bytes, offset)
.expect("exact-minimal Sparse40 count=1 must parse successfully");
assert_eq!(parsed.node_type, BtiNodeType::Sparse);
match &parsed.data {
BtiNodeData::Sparse { transitions } => {
assert_eq!(transitions.len(), 1);
assert_eq!(transitions[0].byte, b'q');
assert_eq!(transitions[0].child.distance, offset - delta);
}
other => panic!("Expected BtiNodeData::Sparse, got {:?}", other),
}
}
#[test]
fn parse_bti_node_dense12_ordinal10_range1_exact_minimal_5_bytes() {
let node_bytes = dense12_node(0, b'A', &[0x123]);
assert_eq!(
node_bytes.len(),
5,
"Dense12 range_len=1 must be exactly 5 bytes"
);
let offset: u64 = 0x500;
let parsed = parse_bti_node(&node_bytes, offset)
.expect("exact-minimal Dense12 range=1 must parse successfully");
assert_eq!(parsed.node_type, BtiNodeType::Dense);
match &parsed.data {
BtiNodeData::Dense {
start_byte,
children,
} => {
assert_eq!(*start_byte, b'A');
assert_eq!(children.len(), 1);
assert_eq!(children[0].as_ref().unwrap().distance, offset - 0x123);
}
other => panic!("Expected BtiNodeData::Dense, got {:?}", other),
}
}
#[test]
fn parse_bti_node_dense12_ordinal10_range2_exact_minimal_6_bytes() {
let node_bytes = dense12_node(0, b'A', &[0x100, 0x200]);
assert_eq!(
node_bytes.len(),
6,
"Dense12 range_len=2 must be exactly 6 bytes"
);
let offset: u64 = 0x800;
let parsed = parse_bti_node(&node_bytes, offset)
.expect("exact-minimal Dense12 range=2 must parse successfully");
assert_eq!(parsed.node_type, BtiNodeType::Dense);
match &parsed.data {
BtiNodeData::Dense {
start_byte,
children,
} => {
assert_eq!(*start_byte, b'A');
assert_eq!(children.len(), 2);
assert_eq!(children[0].as_ref().unwrap().distance, offset - 0x100);
assert_eq!(children[1].as_ref().unwrap().distance, offset - 0x200);
}
other => panic!("Expected BtiNodeData::Dense, got {:?}", other),
}
}
#[test]
fn parse_bti_node_dense16_ordinal11_three_children() {
let node_bytes = dense16_node(0, b'a', &[0x0010, 0x0000, 0x0030]);
let parsed = parse_bti_node(&node_bytes, 0x200).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Dense);
match &parsed.data {
BtiNodeData::Dense {
start_byte,
children,
} => {
assert_eq!(*start_byte, b'a');
assert_eq!(children.len(), 3);
assert_eq!(children[0].as_ref().unwrap().distance, 0x200 - 0x0010);
assert!(children[1].is_none());
assert_eq!(children[2].as_ref().unwrap().distance, 0x200 - 0x0030);
}
other => panic!("Expected Dense, got {:?}", other),
}
}
#[test]
fn parse_bti_node_long_dense_ordinal15_two_children() {
let node_bytes = long_dense_node(0, b'A', &[0x0000_0000_0000_0100, 0x0000_0000_0000_0200]);
let parsed = parse_bti_node(&node_bytes, 0x10000).unwrap();
assert_eq!(parsed.node_type, BtiNodeType::Dense);
match &parsed.data {
BtiNodeData::Dense {
start_byte,
children,
} => {
assert_eq!(*start_byte, b'A');
assert_eq!(children.len(), 2);
assert_eq!(children[0].as_ref().unwrap().distance, 0x10000 - 0x100);
assert_eq!(children[1].as_ref().unwrap().distance, 0x10000 - 0x200);
}
other => panic!("Expected Dense, got {:?}", other),
}
}
#[test]
fn classify_node_nibble_all_ordinals() {
assert_eq!(classify_node_nibble(0).unwrap(), BtiNodeType::PayloadOnly);
for n in 1u8..=4 {
assert_eq!(
classify_node_nibble(n).unwrap(),
BtiNodeType::Single,
"ordinal {} should be Single",
n
);
}
for n in 5u8..=9 {
assert_eq!(
classify_node_nibble(n).unwrap(),
BtiNodeType::Sparse,
"ordinal {} should be Sparse",
n
);
}
for n in 10u8..=15 {
assert_eq!(
classify_node_nibble(n).unwrap(),
BtiNodeType::Dense,
"ordinal {} should be Dense",
n
);
}
}
#[test]
fn rows_parser_sparse_root_node_not_mislabeled() {
let root_node = sparse8_node(0, &[(b'a', 5), (b'b', 10)]);
let data = make_bti_file(root_node);
let cursor = Cursor::new(data);
let mut parser = RowsParser::new(cursor).unwrap();
let root_offset = parser.header.root_offset;
let node = parser.load_node(root_offset).unwrap();
assert_eq!(
node.node_type,
BtiNodeType::Sparse,
"RowsParser returned {:?} for a Sparse8 root node — regression from #647",
node.node_type
);
assert_eq!(node.child_count(), 2);
}
#[test]
fn rows_parser_dense_root_node_not_mislabeled() {
let root_node = dense16_node(0, b'0', &[0x0020, 0x0000, 0x0040]);
let data = make_bti_file(root_node);
let cursor = Cursor::new(data);
let mut parser = RowsParser::new(cursor).unwrap();
let root_offset = parser.header.root_offset;
let node = parser.load_node(root_offset).unwrap();
assert_eq!(
node.node_type,
BtiNodeType::Dense,
"RowsParser returned {:?} for a Dense16 root node",
node.node_type
);
}
#[test]
fn rows_parser_single_nopayload4_root_node_not_mislabeled() {
let root_offset_val: u64 = 64;
let root_node = single_nopayload4_node(3, b'q');
let data = make_bti_file(root_node);
let cursor = Cursor::new(data);
let mut parser = RowsParser::new(cursor).unwrap();
let root_offset = parser.header.root_offset;
assert_eq!(root_offset, root_offset_val);
let node = parser.load_node(root_offset).unwrap();
assert_eq!(
node.node_type,
BtiNodeType::Single,
"RowsParser returned {:?} for a SingleNoPayload4 root node",
node.node_type
);
match &node.data {
BtiNodeData::Single { transition } => {
assert_eq!(transition.byte, b'q');
assert_eq!(transition.child.distance, root_offset_val - 3);
}
other => panic!("Expected Single data, got {:?}", other),
}
}
#[test]
fn test_bti_header_parsing() {
let mut header_data = Vec::new();
header_data.extend_from_slice(&BtiHeader::MAGIC.to_be_bytes());
header_data.extend_from_slice(&BtiHeader::VERSION.to_be_bytes());
header_data.extend_from_slice(&0u16.to_be_bytes()); header_data.extend_from_slice(&1024u64.to_be_bytes()); header_data.extend_from_slice(&100u64.to_be_bytes());
let (header, size) = BtiHeader::parse(&header_data).unwrap();
assert_eq!(header.magic, BtiHeader::MAGIC);
assert_eq!(header.version, BtiHeader::VERSION);
assert_eq!(header.root_offset, 1024);
assert_eq!(header.entry_count, 100);
assert_eq!(size, 24);
}
#[test]
fn test_partitions_parser_creation() {
let data = make_bti_file(payload_only_node(1000, 50));
let cursor = Cursor::new(data);
let _parser = PartitionsParser::new(cursor).unwrap();
}
#[test]
fn test_rows_parser_creation() {
let data = make_bti_file(payload_only_node(1000, 50));
let cursor = Cursor::new(data);
let _parser = RowsParser::new(cursor).unwrap();
}
#[test]
fn test_partition_lookup() {
let data = make_bti_file(payload_only_node(1000, 50));
let cursor = Cursor::new(data);
let mut parser = PartitionsParser::new(cursor).unwrap();
let partition_key = vec![Value::Text("test_partition".to_string())];
let result = parser.lookup_partition(&partition_key).unwrap();
assert!(result.is_some());
}
#[test]
fn test_header_serialization_round_trip() {
let original_header = BtiHeader {
magic: BtiHeader::MAGIC,
version: BtiHeader::VERSION,
flags: 0x1234,
root_offset: 0x123456789ABCDEF0,
entry_count: 0xFEDCBA9876543210,
metadata_size: 0x12345678,
};
let serialized = original_header.to_bytes();
let (parsed_header, _) = BtiHeader::parse(&serialized).unwrap();
assert_eq!(original_header.magic, parsed_header.magic);
assert_eq!(original_header.version, parsed_header.version);
assert_eq!(original_header.flags, parsed_header.flags);
assert_eq!(original_header.root_offset, parsed_header.root_offset);
assert_eq!(original_header.entry_count, parsed_header.entry_count);
assert_eq!(original_header.metadata_size, parsed_header.metadata_size);
}
#[test]
fn read_be_unsigned_edge_cases() {
assert_eq!(read_be_unsigned(&[]), 0);
assert_eq!(read_be_unsigned(&[0xFF]), 255);
assert_eq!(read_be_unsigned(&[0x01, 0x00]), 256);
assert_eq!(read_be_unsigned(&[0xFF, 0xFF, 0xFF, 0xFF]), 0xFFFF_FFFF);
}
#[test]
fn read_12bit_packed_even_and_odd_indices() {
let data: &[u8] = &[0xAB, 0xC1, 0x23];
assert_eq!(read_12bit_packed(data, 0), 0xABC);
assert_eq!(read_12bit_packed(data, 1), 0x123);
}
#[test]
fn decode_bti_partition_payload_data_offset_zero() {
let header_byte: u8 = 0x08;
let hash_byte: u8 = 0x24;
let position_byte: u8 = 0xFF; let trie_data = vec![header_byte, hash_byte, position_byte, 0x00, 0x00];
let payload_start = 1; let payload_bits = header_byte & 0x0F; let result = decode_bti_partition_payload(&trie_data, payload_start, payload_bits)
.expect("should decode successfully");
assert_eq!(
result,
BtiPartitionLocation::DataOffset(0),
"position=-1 (0xFF as i8) must map to data_offset=0 via ~(-1)=0"
);
}
#[test]
fn decode_bti_partition_payload_data_offset_63() {
let trie_data = vec![
0x08u8, 0x22, 0xC0, ];
let payload_bits = 8u8;
let result = decode_bti_partition_payload(&trie_data, 1, payload_bits).unwrap();
assert_eq!(result, BtiPartitionLocation::DataOffset(63));
}
#[test]
fn decode_bti_partition_payload_data_offset_125() {
let trie_data = vec![
0x08u8, 0xF4, 0x82, ];
let result = decode_bti_partition_payload(&trie_data, 1, 8).unwrap();
assert_eq!(result, BtiPartitionLocation::DataOffset(125));
}
#[test]
fn decode_bti_partition_payload_rows_offset() {
let trie_data = vec![
0x09u8, 0xAB, 0x01, 0x00, ];
let result = decode_bti_partition_payload(&trie_data, 1, 9).unwrap();
assert_eq!(result, BtiPartitionLocation::RowsOffset(256));
}
#[test]
fn decode_bti_partition_payload_no_hash_byte_returns_error() {
let trie_data = vec![0x07u8, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let err = decode_bti_partition_payload(&trie_data, 1, 7);
assert!(err.is_err(), "payloadBits < 8 must be an error");
}
#[test]
fn decode_bti_partition_payload_2byte_position() {
let trie_data = vec![0x09u8, 0xAB, 0x00, 0xC0];
let result = decode_bti_partition_payload(&trie_data, 1, 9).unwrap();
assert_eq!(result, BtiPartitionLocation::RowsOffset(192));
}
#[test]
fn sized_ints_slice_1_byte_positive() {
assert_eq!(sized_ints_read_from_slice(&[0x7F]).unwrap(), 127);
}
#[test]
fn sized_ints_slice_1_byte_negative() {
assert_eq!(sized_ints_read_from_slice(&[0xFF]).unwrap(), -1);
assert_eq!(sized_ints_read_from_slice(&[0xC0]).unwrap(), -64);
assert_eq!(sized_ints_read_from_slice(&[0x82]).unwrap(), -126);
}
#[test]
fn sized_ints_slice_2_bytes() {
assert_eq!(sized_ints_read_from_slice(&[0x00, 0xFF]).unwrap(), 255);
assert_eq!(sized_ints_read_from_slice(&[0xFF, 0x00]).unwrap(), -256);
}
#[test]
fn walk_bti_trie_payload_only_root_with_empty_key() {
let trie_data: Vec<u8> = vec![
0x08, 0x00, 0xFF, ];
let result = walk_bti_trie(&trie_data, 0, &[]).unwrap();
assert_eq!(result, Some(BtiPartitionLocation::DataOffset(0)));
}
#[test]
fn walk_bti_trie_two_partitions_via_sparse8_root() {
let mut trie_data = vec![0u8; 12];
trie_data[0] = 0x08; trie_data[1] = 0x11; trie_data[2] = 0xFF;
trie_data[3] = 0x08;
trie_data[4] = 0x22; trie_data[5] = 0xBF;
trie_data[6] = 0x50; trie_data[7] = 0x02; trie_data[8] = 0xAA; trie_data[9] = 0xBB; trie_data[10] = 0x06; trie_data[11] = 0x03;
let result_a = walk_bti_trie(&trie_data, 6, &[0xAA]).unwrap();
assert_eq!(
result_a,
Some(BtiPartitionLocation::DataOffset(0)),
"key 0xAA should resolve to data_offset=0"
);
let result_b = walk_bti_trie(&trie_data, 6, &[0xBB]).unwrap();
assert_eq!(
result_b,
Some(BtiPartitionLocation::DataOffset(64)),
"key 0xBB should resolve to data_offset=64"
);
let result_miss = walk_bti_trie(&trie_data, 6, &[0xCC]).unwrap();
assert_eq!(result_miss, None, "key 0xCC should not be found");
}
#[test]
fn lookup_partition_in_bti_file_synthetic_two_partitions() {
use std::io::Cursor;
let mut trie_file = vec![0u8; 12 + 8];
trie_file[0] = 0x08;
trie_file[1] = 0x11;
trie_file[2] = 0xFF;
trie_file[3] = 0x08;
trie_file[4] = 0x22;
trie_file[5] = 0xBF;
trie_file[6] = 0x50;
trie_file[7] = 0x02;
trie_file[8] = 0xAA;
trie_file[9] = 0xBB;
trie_file[10] = 0x06;
trie_file[11] = 0x03;
trie_file[12..20].copy_from_slice(&6u64.to_be_bytes());
{
let mut cursor = Cursor::new(trie_file.clone());
let result =
lookup_partition_in_bti_file(&mut cursor, &[0xAA]).expect("lookup must not error");
assert_eq!(
result,
Some(BtiPartitionLocation::DataOffset(0)),
"Trie lookup for key 0xAA must return DataOffset(0), NOT a sequential scan"
);
}
{
let mut cursor = Cursor::new(trie_file.clone());
let result =
lookup_partition_in_bti_file(&mut cursor, &[0xBB]).expect("lookup must not error");
assert_eq!(
result,
Some(BtiPartitionLocation::DataOffset(64)),
"Trie lookup for key 0xBB must return DataOffset(64)"
);
}
{
let mut cursor = Cursor::new(trie_file.clone());
let result =
lookup_partition_in_bti_file(&mut cursor, &[0xCC]).expect("lookup must not error");
assert_eq!(result, None, "Key 0xCC must not be found");
}
}
#[test]
fn lookup_partition_in_bti_file_real_simple_table_fixture() {
use std::fs::File;
let datasets_root = match std::env::var("CQLITE_DATASETS_ROOT") {
Ok(v) => std::path::PathBuf::from(v),
Err(_) => {
eprintln!(
"SKIP: CQLITE_DATASETS_ROOT not set; \
test requires real BTI fixture files"
);
return;
}
};
let partitions_db = datasets_root.join(
"sstables/test_da/simple_table-de1be8b064e711f19ad401a8c8227b11/da-2-bti-Partitions.db",
);
if !partitions_db.exists() {
eprintln!("SKIP: BTI fixture not found at {:?}", partitions_db);
return;
}
let mut file = File::open(&partitions_db)
.unwrap_or_else(|e| panic!("Cannot open {:?}: {}", partitions_db, e));
let file_size = {
use std::io::Seek;
file.seek(SeekFrom::End(0)).unwrap()
};
assert_eq!(file_size, 79, "simple_table Partitions.db must be 79 bytes");
{
use std::io::Seek;
file.seek(SeekFrom::End(-8)).unwrap();
}
let mut footer = [0u8; 8];
file.read_exact(&mut footer).unwrap();
let root_offset = u64::from_be_bytes(footer);
assert_eq!(
root_offset, 17,
"simple_table Partitions.db root must be at offset 17"
);
{
use std::io::Seek;
file.seek(SeekFrom::Start(0)).unwrap();
}
let mut trie_data = vec![0u8; 71]; file.read_exact(&mut trie_data).unwrap();
let loc0 =
decode_bti_partition_payload(&trie_data, 1, 8).expect("leaf at offset 0 must decode");
assert_eq!(
loc0,
BtiPartitionLocation::DataOffset(0),
"leaf at trie offset 0 must map to Data.db position 0 (UUID 22222222...)"
);
let loc63 =
decode_bti_partition_payload(&trie_data, 4, 8).expect("leaf at offset 3 must decode");
assert_eq!(
loc63,
BtiPartitionLocation::DataOffset(63),
"leaf at trie offset 3 must map to Data.db position 63 (UUID 11111111...)"
);
let loc125 =
decode_bti_partition_payload(&trie_data, 7, 8).expect("leaf at offset 6 must decode");
assert_eq!(
loc125,
BtiPartitionLocation::DataOffset(125),
"leaf at trie offset 6 must map to Data.db position 125 (UUID 33333333...)"
);
let result_0 = walk_bti_trie(&trie_data, 17, &[0x40, 0x90]).unwrap();
assert_eq!(
result_0,
Some(BtiPartitionLocation::DataOffset(0)),
"[0x40,0x90] must resolve to DataOffset(0)"
);
let result_63 = walk_bti_trie(&trie_data, 17, &[0x40, 0xBC]).unwrap();
assert_eq!(
result_63,
Some(BtiPartitionLocation::DataOffset(63)),
"[0x40,0xBC] must resolve to DataOffset(63)"
);
let result_125 = walk_bti_trie(&trie_data, 17, &[0x40, 0xF9]).unwrap();
assert_eq!(
result_125,
Some(BtiPartitionLocation::DataOffset(125)),
"[0x40,0xF9] must resolve to DataOffset(125)"
);
use std::io::Cursor;
let raw = std::fs::read(&partitions_db).unwrap();
let mut cursor = Cursor::new(raw.clone());
let r = lookup_partition_in_bti_file(&mut cursor, &[0x40, 0x90]).unwrap();
assert_eq!(r, Some(BtiPartitionLocation::DataOffset(0)));
let mut cursor = Cursor::new(raw.clone());
let r = lookup_partition_in_bti_file(&mut cursor, &[0x40, 0xBC]).unwrap();
assert_eq!(r, Some(BtiPartitionLocation::DataOffset(63)));
let mut cursor = Cursor::new(raw.clone());
let r = lookup_partition_in_bti_file(&mut cursor, &[0x40, 0xF9]).unwrap();
assert_eq!(r, Some(BtiPartitionLocation::DataOffset(125)));
let mut cursor = Cursor::new(raw.clone());
let r = lookup_partition_in_bti_file(&mut cursor, &[0x40, 0x00]).unwrap();
assert_eq!(r, None);
println!(
"VERIFIED: lookup_partition_in_bti_file resolved all 3 BTI partition \
offsets (0, 63, 125) via trie walk, NOT sequential scan"
);
}
fn make_partitions_db(trie_bytes: Vec<u8>, root_offset: u64) -> Vec<u8> {
let mut v = trie_bytes;
v.extend_from_slice(&root_offset.to_be_bytes());
v
}
fn partition_leaf(hash: u8, position: i8) -> Vec<u8> {
vec![0x08, hash, position as u8]
}
#[test]
fn dfs_partition_sparse_ascending_order_with_offsets() {
let mut trie = vec![0u8; 12];
trie[0..3].copy_from_slice(&partition_leaf(0x11, -1));
trie[3..6].copy_from_slice(&partition_leaf(0x22, -65));
trie[6] = 0x50; trie[7] = 0x02; trie[8] = 0xAA;
trie[9] = 0xBB;
trie[10] = 0x06; trie[11] = 0x03;
let entries = dfs_collect_partition_entries(&trie, 6).unwrap();
assert_eq!(
entries,
vec![
(vec![0xAA], BtiPartitionLocation::DataOffset(0)),
(vec![0xBB], BtiPartitionLocation::DataOffset(64)),
],
"Sparse DFS must emit ascending transition bytes with correct offsets"
);
}
#[test]
fn dfs_partition_dense_skips_gaps() {
let mut trie = vec![0x00u8]; let l1 = trie.len() as u64; trie.extend_from_slice(&partition_leaf(0x11, -1)); let l2 = trie.len() as u64; trie.extend_from_slice(&partition_leaf(0x22, -65)); let dense_off = trie.len() as u64; trie.push(0xB0); trie.push(0x10); trie.push(0x02); trie.extend_from_slice(&((dense_off - l1) as u16).to_be_bytes()); trie.extend_from_slice(&0u16.to_be_bytes()); trie.extend_from_slice(&((dense_off - l2) as u16).to_be_bytes());
let entries = dfs_collect_partition_entries(&trie, dense_off as usize).unwrap();
assert_eq!(
entries,
vec![
(vec![0x10], BtiPartitionLocation::DataOffset(0)),
(vec![0x12], BtiPartitionLocation::DataOffset(64)),
],
"Dense DFS must skip distance==0 gaps and emit start_byte+i order"
);
}
#[test]
fn dfs_partition_internal_payload_before_children() {
let mut trie = Vec::new();
trie.extend_from_slice(&partition_leaf(0x11, -1)); let node_off = trie.len() as u64; trie.push(0x28); trie.push(0xCC); trie.push(node_off as u8); trie.push(0x99); trie.push((-65i8) as u8);
let entries = dfs_collect_partition_entries(&trie, node_off as usize).unwrap();
assert_eq!(
entries,
vec![
(vec![], BtiPartitionLocation::DataOffset(64)),
(vec![0xCC], BtiPartitionLocation::DataOffset(0)),
],
"An internal node's payload must be emitted before its children"
);
}
fn row_leaf_no_marker(pos: u8) -> Vec<u8> {
assert!(pos <= 127, "use a 1-byte unsigned vint position");
vec![0x01, pos] }
fn make_rows_trie_three(
(k1, p1): (u8, u8),
(k2, p2): (u8, u8),
(k3, p3): (u8, u8),
) -> (Vec<u8>, usize) {
let mut trie = Vec::new();
let o1 = trie.len() as u64; trie.extend_from_slice(&row_leaf_no_marker(p1));
let o2 = trie.len() as u64; trie.extend_from_slice(&row_leaf_no_marker(p2));
let o3 = trie.len() as u64; trie.extend_from_slice(&row_leaf_no_marker(p3));
let root = trie.len() as u64; trie.push(0x50); trie.push(0x03); trie.push(k1);
trie.push(k2);
trie.push(k3);
trie.push((root - o1) as u8);
trie.push((root - o2) as u8);
trie.push((root - o3) as u8);
(trie, root as usize)
}
#[test]
fn dfs_rows_yields_byte_order_with_row_payloads() {
let (trie, root) = make_rows_trie_three((0x10, 5), (0x20, 17), (0x30, 99));
let entries = dfs_collect_row_entries(&trie, root).unwrap();
assert_eq!(
entries,
vec![
(
vec![0x10],
BtiRowIndexEntry {
data_offset: 5,
open_marker: None
}
),
(
vec![0x20],
BtiRowIndexEntry {
data_offset: 17,
open_marker: None
}
),
(
vec![0x30],
BtiRowIndexEntry {
data_offset: 99,
open_marker: None
}
),
],
"Rows.db DFS must yield byte-ordered keys with decoded Data.db positions"
);
}
#[test]
fn dfs_dense_emits_offset_zero_child_and_skips_gap() {
let mut trie = Vec::new();
trie.extend_from_slice(&row_leaf_no_marker(5)); trie.extend_from_slice(&row_leaf_no_marker(9)); let root = trie.len() as u64;
let deltas = [root as u16, 0x0000, (root - 2) as u16];
trie.extend(dense16_node(0, 0x10, &deltas));
let entries = dfs_collect_row_entries(&trie, root as usize).unwrap();
assert_eq!(
entries,
vec![
(
vec![0x10],
BtiRowIndexEntry {
data_offset: 5,
open_marker: None
}
),
(
vec![0x12],
BtiRowIndexEntry {
data_offset: 9,
open_marker: None
}
),
],
"DFS must emit the real child at absolute offset 0 and skip the \
no-transition gap (0x11)"
);
let node = parse_bti_node_for_traversal(&trie, root as usize).unwrap();
let c10 = node.find_child(0x10).expect("0x10 child must be found");
assert_eq!(c10.distance, 0, "0x10 must route to absolute offset 0");
assert!(
node.find_child(0x11).is_none(),
"0x11 is the no-transition gap"
);
assert!(node.find_child(0x12).is_some());
}
#[test]
fn decode_row_payload_open_marker_modern() {
let mut data = vec![0x07u8]; data.extend_from_slice(&567890i64.to_be_bytes());
data.extend_from_slice(&1234u32.to_be_bytes());
let entry = decode_bti_row_payload(&data, 0, 0x9).unwrap();
assert_eq!(
entry,
BtiRowIndexEntry {
data_offset: 7,
open_marker: Some((1234, 567890)),
}
);
}
#[test]
fn decode_row_payload_open_marker_live_sentinel() {
let data = vec![0x07u8, 0x80u8];
let entry = decode_bti_row_payload(&data, 0, 0x9).unwrap();
assert_eq!(
entry,
BtiRowIndexEntry {
data_offset: 7,
open_marker: None,
}
);
}
#[test]
fn da_deletion_time_decoder() {
assert_eq!(decode_da_deletion_time(&[0x80], 0).unwrap(), (None, 1));
assert_eq!(
decode_da_deletion_time(&[0x00, 0x80, 0xFF], 1).unwrap(),
(None, 1)
);
let mut buf = Vec::new();
buf.extend_from_slice(&987_654_321_000i64.to_be_bytes()); buf.extend_from_slice(&1_700_000_000u32.to_be_bytes()); let (del, n) = decode_da_deletion_time(&buf, 0).unwrap();
assert_eq!(n, 12);
assert_eq!(del, Some((1_700_000_000i32, 987_654_321_000i64)));
assert_ne!(buf[0], 0x80);
assert!(decode_da_deletion_time(&[0x00, 0x01, 0x02], 0).is_err());
assert!(decode_da_deletion_time(&[0x00], 5).is_err());
}
#[test]
fn oss50_clustering_encoder() {
assert_eq!(
encode_clustering_bound_oss50(&[Value::Integer(8)]).unwrap(),
vec![0x80, 0x00, 0x00, 0x08]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::Integer(-1)]).unwrap(),
vec![0x7F, 0xFF, 0xFF, 0xFF]
);
let neg = encode_clustering_bound_oss50(&[Value::Integer(-1)]).unwrap();
let zero = encode_clustering_bound_oss50(&[Value::Integer(0)]).unwrap();
let pos = encode_clustering_bound_oss50(&[Value::Integer(100)]).unwrap();
assert!(neg < zero && zero < pos);
assert_eq!(
encode_clustering_bound_oss50(&[Value::BigInt(1)]).unwrap(),
vec![0x80, 0, 0, 0, 0, 0, 0, 0x01]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::Integer(1), Value::Text("ab".to_string())])
.unwrap(),
vec![0x80, 0x00, 0x00, 0x01, 0x40, b'a', b'b', 0x00, 0xFF]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::Text("a".to_string())]).unwrap(),
vec![b'a', 0x00, 0xFF]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::Blob(vec![0x01, 0x00, 0x02])]).unwrap(),
vec![0x01, 0x00, 0xFE, 0x02, 0x00, 0xFF]
);
let a = encode_clustering_bound_oss50(&[Value::Text("a".to_string())]).unwrap();
let ab = encode_clustering_bound_oss50(&[Value::Text("ab".to_string())]).unwrap();
assert!(a < ab);
assert!(encode_clustering_bound_oss50(&[Value::Float(1.0)]).is_err());
}
#[test]
fn oss50_clustering_encoder_reversed_order() {
assert_eq!(
encode_clustering_bound_oss50_with_order(&[Value::Integer(8)], &[true]).unwrap(),
vec![0x7F, 0xFF, 0xFF, 0xF7]
);
assert_eq!(
encode_clustering_bound_oss50_with_order(&[Value::Integer(8)], &[false]).unwrap(),
encode_clustering_bound_oss50(&[Value::Integer(8)]).unwrap()
);
let enc = |v: i32| {
encode_clustering_bound_oss50_with_order(&[Value::Integer(v)], &[true]).unwrap()
};
let write_order = [5, 4, 3, 2, 1, 0];
for w in write_order.windows(2) {
assert!(
enc(w[0]) < enc(w[1]),
"DESC: value {} written before {} must yield smaller separator bytes",
w[0],
w[1]
);
}
let mixed = encode_clustering_bound_oss50_with_order(
&[Value::Integer(1), Value::Integer(8)],
&[false, true],
)
.unwrap();
assert_eq!(
mixed,
vec![0x80, 0x00, 0x00, 0x01, 0x40, 0x7F, 0xFF, 0xFF, 0xF7],
"ASC component bare, 0x40 framing un-inverted, DESC component complemented"
);
let m = |a: i32, b: i32| {
encode_clustering_bound_oss50_with_order(
&[Value::Integer(a), Value::Integer(b)],
&[false, true],
)
.unwrap()
};
assert!(m(1, 9) < m(1, 8) && m(1, 8) < m(1, 7));
assert!(m(1, 0) < m(2, 9));
}
#[test]
fn read_unsigned_vint_multibyte() {
let (v, n) = read_unsigned_vint_from_slice(&[0x81, 0x2C]).unwrap();
assert_eq!((v, n), (300, 2));
let (v, n) = read_unsigned_vint_from_slice(&[0x7F]).unwrap();
assert_eq!((v, n), (127, 1));
}
#[test]
fn range_filter_subset_and_empty_and_reversed() {
let (trie, root) = make_rows_trie_three((0x10, 5), (0x20, 17), (0x30, 99));
let all = dfs_collect_row_entries(&trie, root).unwrap();
let filter = |lo: &[u8], hi: &[u8]| -> Vec<u64> {
if lo > hi {
return Vec::new();
}
all.iter()
.filter(|(k, _)| k.as_slice() >= lo && k.as_slice() <= hi)
.map(|(_, e)| e.data_offset)
.collect()
};
assert_eq!(filter(&[0x10], &[0x20]), vec![5, 17]); assert_eq!(filter(&[0x20], &[0x30]), vec![17, 99]); assert_eq!(filter(&[0x00], &[0x0F]), Vec::<u64>::new()); assert_eq!(filter(&[0x31], &[0xFF]), Vec::<u64>::new()); assert_eq!(filter(&[0x30], &[0x10]), Vec::<u64>::new()); assert_eq!(filter(&[0x10], &[0x30]), vec![5, 17, 99]); }
#[test]
fn select_blocks_separator_semantics() {
let entries = vec![
(
vec![0x10u8],
BtiRowIndexEntry {
data_offset: 5,
open_marker: None,
},
),
(
vec![0x20u8],
BtiRowIndexEntry {
data_offset: 17,
open_marker: None,
},
),
(
vec![0x30u8],
BtiRowIndexEntry {
data_offset: 99,
open_marker: None,
},
),
];
let offs = |start: &[u8], end: &[u8]| -> Vec<u64> {
select_row_index_blocks_for_range(&entries, start, end)
.into_iter()
.map(|b| b.data_offset)
.collect()
};
assert_eq!(
offs(&[0x18], &[0x18]),
vec![5],
"floor block must be selected"
);
assert_eq!(offs(&[0x18], &[0x28]), vec![5, 17]);
assert_eq!(offs(&[0x20], &[0x2F]), vec![17]);
assert_eq!(offs(&[0x40], &[0x50]), vec![99]);
assert_eq!(offs(&[0x00], &[0x0F]), Vec::<u64>::new());
assert_eq!(offs(&[0x00], &[0xFF]), vec![5, 17, 99]);
assert_eq!(offs(&[0x30], &[0x10]), Vec::<u64>::new());
assert!(select_row_index_blocks_for_range(&[], &[0x00], &[0xFF]).is_empty());
}
#[test]
fn resolve_rows_db_entry_recovers_root_and_metadata() {
let mut buf = vec![0xEEu8; 4]; let rows_offset = buf.len();
buf.extend_from_slice(&4u16.to_be_bytes());
buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x07]);
let base = rows_offset + 4;
buf.push(123);
let root_delta: i64 = 2 - base as i64;
let zig = ((root_delta << 1) ^ (root_delta >> 63)) as u64;
assert!(zig < 128, "test setup expects a 1-byte vint");
buf.push(zig as u8);
buf.push(38);
buf.extend_from_slice(&17i64.to_be_bytes());
buf.extend_from_slice(&9u32.to_be_bytes());
let header = resolve_rows_db_entry(&buf, rows_offset).unwrap();
assert_eq!(header.data_position, 123);
assert_eq!(
header.trie_root, 2,
"trie root = rootΔ + (RowsOffset + keylen)"
);
assert_eq!(header.block_count, 38);
assert_eq!(header.partition_deletion, Some((9, 17)));
assert!(resolve_rows_db_entry(&buf, buf.len() + 10).is_err());
}
#[test]
fn resolve_rows_db_entry_live_partition_deletion() {
let mut buf = vec![0xEEu8; 4];
let rows_offset = buf.len();
buf.extend_from_slice(&4u16.to_be_bytes());
buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x07]);
let base = rows_offset + 4;
buf.push(123); let root_delta: i64 = 2 - base as i64;
let zig = ((root_delta << 1) ^ (root_delta >> 63)) as u64;
buf.push(zig as u8); buf.push(38); buf.push(0x80);
let header = resolve_rows_db_entry(&buf, rows_offset).unwrap();
assert_eq!(header.block_count, 38);
assert_eq!(
header.partition_deletion, None,
"0x80 live sentinel must decode to no partition deletion"
);
}
#[test]
fn read_signed_vint_zigzag() {
let (v, n) = read_signed_vint_from_slice(&[0x13]).unwrap();
assert_eq!((v, n), (-10, 1));
assert_eq!(read_signed_vint_from_slice(&[0x00]).unwrap(), (0, 1));
assert_eq!(read_signed_vint_from_slice(&[0x7E]).unwrap(), (63, 1));
}
#[test]
fn decode_row_payload_sizedints_two_bytes() {
let data = vec![0x40u8, 0x80];
let entry = decode_bti_row_payload(&data, 0, 0x2).unwrap();
assert_eq!(
entry,
BtiRowIndexEntry {
data_offset: 16512,
open_marker: None,
}
);
}
#[test]
fn partition_iterator_full_traversal_synthetic() {
let mut trie = vec![0u8; 12];
trie[0..3].copy_from_slice(&partition_leaf(0x11, -1));
trie[3..6].copy_from_slice(&partition_leaf(0x22, -65));
trie[6] = 0x50;
trie[7] = 0x02;
trie[8] = 0xAA;
trie[9] = 0xBB;
trie[10] = 0x06;
trie[11] = 0x03;
let file = make_partitions_db(trie, 6);
let (trie_data, root) = load_bti_trie_via_footer(&mut Cursor::new(file)).unwrap();
assert_eq!(root, 6);
let entries = dfs_collect_partition_entries(&trie_data, root).unwrap();
assert_eq!(
entries,
vec![
(vec![0xAA], BtiPartitionLocation::DataOffset(0)),
(vec![0xBB], BtiPartitionLocation::DataOffset(64)),
]
);
}
#[test]
fn iterate_rows_in_bti_trie_empty_and_oob_root() {
let err = iterate_rows_in_bti_trie(&[], 0);
assert!(err.is_err(), "empty Rows.db trie must error, not panic");
let (trie, _root) = make_rows_trie_three((0x10, 5), (0x20, 17), (0x30, 99));
let err = iterate_rows_in_bti_trie(&trie, trie.len() + 100);
assert!(err.is_err(), "out-of-bounds root must error, not panic");
let (trie, root) = make_rows_trie_three((0x10, 5), (0x20, 17), (0x30, 99));
let entries = iterate_rows_in_bti_trie(&trie, root).unwrap();
assert_eq!(entries.len(), 3);
}
#[test]
fn range_filter_prefix_relationship_is_sound() {
let mut trie = Vec::new();
trie.extend_from_slice(&row_leaf_no_marker(2));
let k_off = trie.len() as u64; trie.push(0x21); trie.push(0x20); trie.push(k_off as u8); trie.push(0x01);
let root = trie.len() as u64; trie.push(0x20); trie.push(0x10); trie.push((root - k_off) as u8);
let all = iterate_rows_in_bti_trie(&trie, root as usize).unwrap();
assert_eq!(
all,
vec![
(
vec![0x10],
BtiRowIndexEntry {
data_offset: 1,
open_marker: None
}
),
(
vec![0x10, 0x20],
BtiRowIndexEntry {
data_offset: 2,
open_marker: None
}
),
],
"K (internal payload) must sort before its descendant K2"
);
let filter = |lo: &[u8], hi: &[u8]| -> Vec<u64> {
if lo > hi {
return Vec::new();
}
all.iter()
.filter(|(k, _)| k.as_slice() >= lo && k.as_slice() <= hi)
.map(|(_, e)| e.data_offset)
.collect()
};
let k = [0x10u8];
let k2 = [0x10u8, 0x20u8];
assert_eq!(
filter(&k, &k),
vec![1],
"[K..=K] must include K and exclude the longer K2"
);
assert_eq!(
filter(&k, &k2),
vec![1, 2],
"[K..=K2] must include both K and K2"
);
assert_eq!(
filter(&[0x10, 0x00], &[0x10, 0x10]),
Vec::<u64>::new(),
"a range strictly between K and K2 must exclude both"
);
}
fn make_rows_db_with_three(
(k1, p1): (u8, u8),
(k2, p2): (u8, u8),
(k3, p3): (u8, u8),
) -> (Vec<u8>, usize) {
let (trie, root) = make_rows_trie_three((k1, p1), (k2, p2), (k3, p3));
let mut buf = trie; let rows_offset = buf.len();
buf.extend_from_slice(&4u16.to_be_bytes());
buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x07]);
let base = rows_offset + 4;
buf.push(0);
let root_delta: i64 = root as i64 - base as i64;
let zig = ((root_delta << 1) ^ (root_delta >> 63)) as u64;
write_uvint(&mut buf, zig);
buf.push(3);
buf.push(0x80);
(buf, rows_offset)
}
fn write_uvint(out: &mut Vec<u8>, v: u64) {
if v < 0x80 {
out.push(v as u8);
return;
}
assert!(v < 0x4000, "test vint fixture expects <= 14-bit values");
out.push(0x80 | (v >> 8) as u8);
out.push((v & 0xFF) as u8);
}
#[test]
fn range_query_with_order_all_asc_matches_range_query() {
let v = |t: i8| (t as u8) ^ 0x80;
let (buf, rows_offset) = make_rows_db_with_three((v(3), 5), (v(5), 17), (v(9), 99));
let start = [Value::TinyInt(3)];
let end = [Value::TinyInt(9)];
let mut p1 = RowsParser::new(Cursor::new(buf.clone())).unwrap();
let plain = p1.range_query(rows_offset, &start, &end).unwrap();
let mut p2 = RowsParser::new(Cursor::new(buf.clone())).unwrap();
let ordered = p2
.range_query_with_order(rows_offset, &start, &end, &[false])
.unwrap();
assert_eq!(
ordered, plain,
"all-ASC range_query_with_order must equal range_query (forward)"
);
let offs: Vec<u64> = ordered.iter().map(|b| b.data_offset).collect();
assert_eq!(
offs,
vec![5, 17, 99],
"forward range returns all three blocks"
);
let mut p3 = RowsParser::new(Cursor::new(buf.clone())).unwrap();
let plain_rev = p3.range_query(rows_offset, &end, &start).unwrap();
let mut p4 = RowsParser::new(Cursor::new(buf)).unwrap();
let ordered_rev = p4
.range_query_with_order(rows_offset, &end, &start, &[false])
.unwrap();
assert!(
plain_rev.is_empty(),
"sanity: range_query is empty for reversed bounds"
);
assert_eq!(
ordered_rev, plain_rev,
"all-ASC reversed range must yield empty, matching range_query (no swap)"
);
}
#[test]
fn range_query_with_order_desc_reversed_yields_empty() {
let dv = |t: i8| 0xFFu8 ^ ((t as u8) ^ 0x80);
assert!(dv(9) < dv(5) && dv(5) < dv(3));
let (buf, rows_offset) = make_rows_db_with_three((dv(9), 5), (dv(5), 17), (dv(3), 99));
let mut pf = RowsParser::new(Cursor::new(buf.clone())).unwrap();
let fwd = pf
.range_query_with_order(
rows_offset,
&[Value::TinyInt(9)],
&[Value::TinyInt(3)],
&[true],
)
.unwrap();
let offs: Vec<u64> = fwd.iter().map(|b| b.data_offset).collect();
assert_eq!(
offs,
vec![5, 17, 99],
"forward DESC range (9..3 in DESC value order) returns all blocks"
);
let mut pr = RowsParser::new(Cursor::new(buf)).unwrap();
let rev = pr
.range_query_with_order(
rows_offset,
&[Value::TinyInt(3)],
&[Value::TinyInt(9)],
&[true],
)
.unwrap();
assert!(
rev.is_empty(),
"reversed DESC range must yield empty (encoded_start > encoded_end), no swap"
);
}
}