pub mod encoder;
pub mod node;
pub mod parser;
pub mod sized_ints;
pub use encoder::{BatchEncoder, ByteComparableEncoder, EncoderConfig, EncoderStats};
pub use node::{
BtiError, BtiNode, BtiNodeData, BtiNodeType, BtiResult, PayloadRef, SizedPointer, Transition,
};
pub use parser::{
decode_bti_partition_payload, encode_bti_trie_key_from_token, encode_clustering_bound_oss50,
encode_clustering_bound_oss50_with_order, encode_partition_key_for_bti_trie,
iterate_partition_locations_in_bti_file, iterate_partitions_in_bti_file,
iterate_rows_for_partition, iterate_rows_in_bti_file, iterate_rows_in_bti_trie,
lookup_partition_in_bti_file, lookup_raw_key_in_bti_partitions_db, resolve_rows_db_entry,
select_row_index_blocks_for_range, BtiPartitionLocation, BtiRowIndexEntry,
BtiRowIndexEntryWithKey, BtiRowIndexHeader, FLAG_HAS_HASH_BYTE, FLAG_OPEN_MARKER,
};
pub(crate) use parser::lookup_partition_in_bti_slice;
#[cfg(not(feature = "tombstones"))]
pub(crate) use parser::{
encode_partition_key_for_bti_trie_uncounted, partition_successor_in_bti_slice,
resolve_rows_db_entry_uncounted,
};
#[doc(hidden)]
pub use parser::{find_child_offset_for_test, parse_bti_node_for_test};
#[cfg(not(feature = "tombstones"))]
pub(crate) use parser::{rows_floor_block, rows_strict_ceiling_block};
use crate::parser::header::CassandraVersion;
use std::collections::HashMap;
pub const BTI_MAGIC_NUMBER: u32 = 0x6461_0000;
pub const BTI_FORMAT_VERSION: u16 = 0x0001;
pub const MAX_TRIE_DEPTH: usize = 128;
pub const BTI_PAGE_SIZE: usize = 4096;
#[derive(Debug, Clone, PartialEq)]
pub enum FormatType {
Big,
Bti,
}
#[derive(Debug, Clone)]
pub struct BtiMetadata {
pub version: u16,
pub cassandra_version: CassandraVersion,
pub partition_trie_root: u64,
pub row_trie_root: Option<u64>,
pub partition_count: u64,
pub properties: HashMap<String, String>,
}
pub fn detect_format(magic_number: u32) -> FormatType {
match magic_number {
BTI_MAGIC_NUMBER => FormatType::Bti,
_ => FormatType::Big, }
}
pub fn is_bti_format(magic_number: u32) -> bool {
magic_number == BTI_MAGIC_NUMBER
}
#[derive(Debug, Clone)]
pub struct BtiLookupResult {
pub data_offset: u64,
pub data_size: Option<u32>,
pub row_index_offset: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct BtiConfig {
pub page_aware_reading: bool,
pub max_cached_nodes: usize,
pub pointer_compression: bool,
}
impl Default for BtiConfig {
fn default() -> Self {
Self {
page_aware_reading: true,
max_cached_nodes: 1024,
pointer_compression: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bti_magic_number_detection() {
assert_eq!(detect_format(BTI_MAGIC_NUMBER), FormatType::Bti);
assert_eq!(detect_format(0x6F61_0000), FormatType::Big); assert_eq!(detect_format(0x0040_0000), FormatType::Big);
assert!(is_bti_format(BTI_MAGIC_NUMBER));
assert!(!is_bti_format(0x6F61_0000));
}
#[test]
fn test_bti_error_display() {
let err = node::BtiError::InvalidNodeType(0xFF);
assert!(err.to_string().contains("Invalid BTI trie node type: 0xFF"));
let err = node::BtiError::MaxDepthExceeded(150);
assert!(err
.to_string()
.contains("BTI trie depth exceeded maximum: 150"));
let err = node::BtiError::InvalidByteComparableKey("bad_key".to_string());
assert!(err
.to_string()
.contains("Invalid byte-comparable key: bad_key"));
}
#[test]
fn test_bti_config_default() {
let config = BtiConfig::default();
assert!(config.page_aware_reading);
assert_eq!(config.max_cached_nodes, 1024);
assert!(config.pointer_compression);
}
}