use noxu_db::DatabaseEntry;
use crate::entry_binding::EntryBinding;
use crate::error::Result;
use crate::tuple::tuple_binding::TupleBinding;
use crate::tuple::tuple_input::TupleInput;
use crate::tuple::tuple_output::TupleOutput;
macro_rules! impl_primitive_binding {
(
$(#[$meta:meta])*
$name:ident, $ty:ty,
write: $write_method:ident,
read: $read_method:ident
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, Default)]
pub struct $name;
impl $name {
pub fn new() -> Self {
Self
}
}
impl EntryBinding<$ty> for $name {
fn entry_to_object(&self, entry: &DatabaseEntry) -> Result<$ty> {
let mut input = Self::entry_to_input(entry);
self.tuple_to_object(&mut input)
}
fn object_to_entry(&self, object: &$ty, entry: &mut DatabaseEntry) -> Result<()> {
let mut output = TupleOutput::new();
self.object_to_tuple(object, &mut output)?;
entry.set_data_vec(output.into_vec());
Ok(())
}
}
impl TupleBinding<$ty> for $name {
fn tuple_to_object(&self, input: &mut TupleInput) -> Result<$ty> {
input.$read_method()
}
fn object_to_tuple(&self, object: &$ty, output: &mut TupleOutput) -> Result<()> {
output.$write_method(*object);
Ok(())
}
}
};
}
impl_primitive_binding!(
BoolBinding, bool,
write: write_bool,
read: read_bool
);
impl_primitive_binding!(
ByteBinding, u8,
write: write_u8,
read: read_u8
);
impl_primitive_binding!(
ShortBinding, i16,
write: write_i16,
read: read_i16
);
impl_primitive_binding!(
IntBinding, i32,
write: write_i32,
read: read_i32
);
impl_primitive_binding!(
LongBinding, i64,
write: write_i64,
read: read_i64
);
impl_primitive_binding!(
FloatBinding, f32,
write: write_float,
read: read_float
);
impl_primitive_binding!(
DoubleBinding, f64,
write: write_double,
read: read_double
);
impl_primitive_binding!(
SortedFloatBinding, f32,
write: write_sorted_float,
read: read_sorted_float
);
impl_primitive_binding!(
SortedDoubleBinding, f64,
write: write_sorted_double,
read: read_sorted_double
);
impl_primitive_binding!(
PackedIntBinding, i32,
write: write_packed_int,
read: read_packed_int
);
impl_primitive_binding!(
PackedLongBinding, i64,
write: write_packed_long,
read: read_packed_long
);
impl_primitive_binding!(
SortedPackedIntBinding, i32,
write: write_sorted_packed_int,
read: read_sorted_packed_int
);
impl_primitive_binding!(
SortedPackedLongBinding, i64,
write: write_sorted_packed_long,
read: read_sorted_packed_long
);
impl_primitive_binding!(
CharBinding, u16,
write: write_char,
read: read_char
);
#[derive(Debug, Clone, Copy, Default)]
pub struct StringBinding;
impl StringBinding {
pub fn new() -> Self {
Self
}
}
impl EntryBinding<String> for StringBinding {
fn entry_to_object(&self, entry: &DatabaseEntry) -> Result<String> {
let mut input = Self::entry_to_input(entry);
self.tuple_to_object(&mut input)
}
fn object_to_entry(
&self,
object: &String,
entry: &mut DatabaseEntry,
) -> Result<()> {
let mut output = TupleOutput::new();
self.object_to_tuple(object, &mut output)?;
entry.set_data_vec(output.into_vec());
Ok(())
}
}
impl TupleBinding<String> for StringBinding {
fn tuple_to_object(&self, input: &mut TupleInput) -> Result<String> {
input.read_string()
}
fn object_to_tuple(
&self,
object: &String,
output: &mut TupleOutput,
) -> Result<()> {
output.write_string(object);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bool_binding_round_trip() {
let binding = BoolBinding::new();
for val in [true, false] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_byte_binding_round_trip() {
let binding = ByteBinding::new();
for val in [0u8, 1, 127, 128, 255] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_short_binding_round_trip() {
let binding = ShortBinding::new();
for val in [i16::MIN, -1, 0, 1, i16::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_int_binding_round_trip() {
let binding = IntBinding::new();
for val in [i32::MIN, -1, 0, 1, i32::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_long_binding_round_trip() {
let binding = LongBinding::new();
for val in [i64::MIN, -1, 0, 1, i64::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_float_binding_round_trip() {
let binding = FloatBinding::new();
for val in [-1.5f32, 0.0, 1.5, f32::MAX, f32::MIN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_double_binding_round_trip() {
let binding = DoubleBinding::new();
for val in [-1.5f64, 0.0, 1.5, f64::MAX, f64::MIN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_sorted_float_binding_round_trip() {
let binding = SortedFloatBinding::new();
for val in [-1.5f32, -0.0, 0.0, 1.5, f32::MAX, f32::MIN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val.to_bits(), result.to_bits(), "failed for {}", val);
}
}
#[test]
fn test_sorted_double_binding_round_trip() {
let binding = SortedDoubleBinding::new();
for val in [-1.5f64, -0.0, 0.0, 1.5, f64::MAX, f64::MIN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val.to_bits(), result.to_bits(), "failed for {}", val);
}
}
#[test]
fn test_packed_int_binding_round_trip() {
let binding = PackedIntBinding::new();
for val in [i32::MIN, -120, -119, -1, 0, 1, 119, 120, i32::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "failed for {}", val);
}
}
#[test]
fn test_packed_long_binding_round_trip() {
let binding = PackedLongBinding::new();
for val in [i64::MIN, -120, -119, -1, 0, 1, 119, 120, i64::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "failed for {}", val);
}
}
#[test]
fn test_string_binding_round_trip() {
let binding = StringBinding::new();
for val in ["", "hello", "world", "unicode: \u{1F600}"] {
let s = val.to_string();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&s, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(s, result);
}
}
#[test]
fn test_string_binding_embedded_null() {
let binding = StringBinding::new();
let s = "hello\x00world".to_string();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&s, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(s, result);
}
#[test]
fn test_string_binding_multiple_embedded_nulls() {
let binding = StringBinding::new();
let s = "\x00\x00\x00".to_string();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&s, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(s, result);
}
#[test]
fn test_string_binding_null_at_start_and_end() {
let binding = StringBinding::new();
let s = "\x00hello\x00".to_string();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&s, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(s, result);
}
#[test]
fn test_string_binding_null_escape_encoded_correctly() {
let binding = StringBinding::new();
let s = "a\x00b".to_string();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&s, &mut entry).unwrap();
let raw = entry.data();
assert_eq!(raw, &[b'a', 0x00, 0x01, b'b', 0x00, 0x00]);
}
#[test]
fn test_string_binding_terminator_encoded_correctly() {
let binding = StringBinding::new();
let s = String::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&s, &mut entry).unwrap();
let raw = entry.data();
assert_eq!(raw, &[0x00, 0x00]);
}
#[test]
fn test_string_binding_two_strings_in_tuple() {
use crate::tuple::tuple_input::TupleInput;
use crate::tuple::tuple_output::TupleOutput;
let mut out = TupleOutput::new();
out.write_string("foo\x00bar");
out.write_string("baz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "foo\x00bar");
assert_eq!(inp.read_string().unwrap(), "baz");
}
fn encoded_bytes<T>(binding: &impl EntryBinding<T>, val: &T) -> Vec<u8> {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(val, &mut entry).unwrap();
entry.data().to_vec()
}
#[test]
fn test_short_binding_sort_order() {
let binding = ShortBinding::new();
let values: Vec<i16> = vec![i16::MIN, -100, -1, 0, 1, 100, i16::MAX];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"{} should sort before {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_int_binding_sort_order() {
let binding = IntBinding::new();
let values: Vec<i32> = vec![i32::MIN, -100, -1, 0, 1, 100, i32::MAX];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"{} should sort before {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_long_binding_sort_order() {
let binding = LongBinding::new();
let values: Vec<i64> = vec![i64::MIN, -100, -1, 0, 1, 100, i64::MAX];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"{} should sort before {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_sorted_float_binding_sort_order() {
let binding = SortedFloatBinding::new();
let values: Vec<f32> = vec![
f32::NEG_INFINITY,
f32::MIN,
-100.0,
-1.0,
-f32::MIN_POSITIVE,
0.0,
f32::MIN_POSITIVE,
1.0,
100.0,
f32::MAX,
f32::INFINITY,
];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"{} (encoded {:?}) should sort before {} (encoded {:?})",
values[i],
encoded[i],
values[i + 1],
encoded[i + 1]
);
}
}
#[test]
fn test_sorted_double_binding_sort_order() {
let binding = SortedDoubleBinding::new();
let values: Vec<f64> = vec![
f64::NEG_INFINITY,
f64::MIN,
-100.0,
-1.0,
-f64::MIN_POSITIVE,
0.0,
f64::MIN_POSITIVE,
1.0,
100.0,
f64::MAX,
f64::INFINITY,
];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"{} should sort before {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_string_binding_sort_order() {
let binding = StringBinding::new();
let values: Vec<String> = vec![
"".to_string(),
"a".to_string(),
"ab".to_string(),
"b".to_string(),
"z".to_string(),
];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"{:?} should sort before {:?}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_packed_int_boundary_values() {
let binding = PackedIntBinding::new();
for val in [-120, -119, 119, 120, 256, -257, 65536, -65537] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "boundary test failed for {}", val);
}
}
#[test]
fn test_packed_long_boundary_values() {
let binding = PackedLongBinding::new();
for val in [-120i64, -119, 119, 120, 256, -257, 1 << 32, -(1 << 32)] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "boundary test failed for {}", val);
}
}
#[test]
fn test_sorted_float_nan() {
let binding = SortedFloatBinding::new();
let val = f32::NAN;
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert!(result.is_nan());
}
#[test]
fn test_sorted_double_nan() {
let binding = SortedDoubleBinding::new();
let val = f64::NAN;
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert!(result.is_nan());
}
#[test]
fn test_sorted_float_negative_zero() {
let binding = SortedFloatBinding::new();
let val = -0.0f32;
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val.to_bits(), result.to_bits());
}
#[test]
fn test_sorted_double_negative_zero() {
let binding = SortedDoubleBinding::new();
let val = -0.0f64;
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val.to_bits(), result.to_bits());
}
#[test]
fn test_int_binding_encoded_size() {
let binding = IntBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&42i32, &mut entry).unwrap();
assert_eq!(entry.data().len(), 4);
}
#[test]
fn test_long_binding_encoded_size() {
let binding = LongBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&42i64, &mut entry).unwrap();
assert_eq!(entry.data().len(), 8);
}
#[test]
fn test_packed_int_compact_size() {
let binding = PackedIntBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&0i32, &mut entry).unwrap();
assert_eq!(entry.data().len(), 1);
binding.object_to_entry(&119i32, &mut entry).unwrap();
assert_eq!(entry.data().len(), 1);
binding.object_to_entry(&(-119i32), &mut entry).unwrap();
assert_eq!(entry.data().len(), 1);
binding.object_to_entry(&120i32, &mut entry).unwrap();
assert!(entry.data().len() > 1);
}
#[test]
fn test_sorted_packed_int_binding_round_trip() {
let binding = SortedPackedIntBinding::new();
for val in [i32::MIN, -120, -119, -1, 0, 1, 119, 120, 121, i32::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "round-trip failed for {}", val);
}
}
#[test]
fn test_sorted_packed_int_binding_sort_order() {
let binding = SortedPackedIntBinding::new();
let values: Vec<i32> = vec![
i32::MIN,
-1_000_000,
-120,
-119,
-1,
0,
1,
119,
120,
121,
1_000_000,
i32::MAX,
];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"sort order violated: {} (encoded {:?}) should sort before {} (encoded {:?})",
values[i],
encoded[i],
values[i + 1],
encoded[i + 1]
);
}
}
#[test]
fn test_sorted_packed_int_single_byte_range() {
let binding = SortedPackedIntBinding::new();
for val in (-119..=120i32).step_by(1) {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
1,
"value {} should encode in 1 byte",
val
);
}
}
#[test]
fn test_sorted_packed_int_multi_byte() {
let binding = SortedPackedIntBinding::new();
for val in [121i32, -120, i32::MAX, i32::MIN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert!(
entry.data().len() > 1,
"value {} should encode in more than 1 byte",
val
);
}
}
#[test]
fn test_sorted_packed_long_binding_round_trip() {
let binding = SortedPackedLongBinding::new();
for val in [
i64::MIN,
-1_000_000_000_000i64,
-120,
-119,
-1,
0,
1,
119,
120,
121,
1_000_000_000_000i64,
i64::MAX,
] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "round-trip failed for {}", val);
}
}
#[test]
fn test_sorted_packed_long_binding_sort_order() {
let binding = SortedPackedLongBinding::new();
let values: Vec<i64> = vec![
i64::MIN,
-1_000_000_000_000,
-120,
-119,
-1,
0,
1,
119,
120,
121,
1_000_000_000_000,
i64::MAX,
];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"sort order violated: {} (encoded {:?}) should sort before {} (encoded {:?})",
values[i],
encoded[i],
values[i + 1],
encoded[i + 1]
);
}
}
#[test]
fn test_sorted_packed_long_single_byte_range() {
let binding = SortedPackedLongBinding::new();
for val in (-119..=120i64).step_by(1) {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
1,
"value {} should encode in 1 byte",
val
);
}
}
#[test]
fn test_sorted_packed_long_multi_byte() {
let binding = SortedPackedLongBinding::new();
for val in [121i64, -120, i64::MAX, i64::MIN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert!(
entry.data().len() > 1,
"value {} should encode in more than 1 byte",
val
);
}
}
#[test]
fn test_char_binding_round_trip() {
let binding = CharBinding::new();
for val in
[0u16, 1, 'A' as u16, 'a' as u16, 0x00FF, 0x0100, 0xD800, 0xFFFF]
{
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
let result = binding.entry_to_object(&entry).unwrap();
assert_eq!(val, result, "round-trip failed for U+{:04X}", val);
}
}
#[test]
fn test_char_binding_encoded_size() {
let binding = CharBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&(b'A' as u16), &mut entry).unwrap();
assert_eq!(
entry.data().len(),
2,
"char should always encode to 2 bytes"
);
}
#[test]
fn test_char_binding_big_endian_encoding() {
let binding = CharBinding::new();
let val = b'A' as u16; let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data(),
&[0x00, 0x41],
"char 'A' (U+0041) should encode as [0x00, 0x41]"
);
}
#[test]
fn test_char_binding_high_codepoint_encoding() {
let binding = CharBinding::new();
let val: u16 = 0xFF41;
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data(),
&[0xFF, 0x41],
"U+FF41 should encode as [0xFF, 0x41]"
);
}
#[test]
fn test_char_binding_sort_order() {
let binding = CharBinding::new();
let values: Vec<u16> = vec![
0x0000, 0x0041, 0x007F, 0x0080, 0x00FF, 0x0100, 0xD800, 0xFFFF,
];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"sort order violated: U+{:04X} should sort before U+{:04X}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_sorted_packed_int_sort_order_vs_fixed_int() {
let spb = SortedPackedIntBinding::new();
let ib = IntBinding::new();
let values: Vec<i32> = vec![-1000, -1, 0, 1, 1000];
let sp_encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&spb, v)).collect();
let i_encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&ib, v)).collect();
for i in 0..sp_encoded.len() - 1 {
assert!(
sp_encoded[i] < sp_encoded[i + 1],
"SortedPackedIntBinding sort order violated for {} < {}",
values[i],
values[i + 1]
);
assert!(
i_encoded[i] < i_encoded[i + 1],
"IntBinding sort order violated for {} < {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_i8_sort_order() {
let values: Vec<i8> = vec![-128, -1, 0, 1, 127];
let encoded: Vec<Vec<u8>> = values
.iter()
.map(|&v| {
let mut out = TupleOutput::new();
out.write_i8(v);
out.to_vec()
})
.collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"i8 sort order: {} should be < {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn test_bool_binding_encoded_size() {
let binding = BoolBinding::new();
for val in [true, false] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
1,
"bool {} should encode to 1 byte",
val
);
}
}
#[test]
fn test_byte_binding_encoded_size() {
let binding = ByteBinding::new();
for val in [0u8, 1, 123, 255] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
1,
"u8 {} should encode to 1 byte",
val
);
}
}
#[test]
fn test_short_binding_encoded_size() {
let binding = ShortBinding::new();
for val in [i16::MIN, -1i16, 0, 1, 123, i16::MAX] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
2,
"i16 {} should encode to 2 bytes",
val
);
}
}
#[test]
fn test_float_binding_encoded_size() {
let binding = FloatBinding::new();
for val in [0.0f32, 1.0, -1.0, 123.123, f32::MAX, f32::MIN, f32::NAN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
4,
"f32 {} should encode to 4 bytes",
val
);
}
}
#[test]
fn test_double_binding_encoded_size() {
let binding = DoubleBinding::new();
for val in [0.0f64, 1.0, -1.0, 123.123, f64::MAX, f64::MIN, f64::NAN] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
8,
"f64 {} should encode to 8 bytes",
val
);
}
}
#[test]
fn test_sorted_float_binding_encoded_size() {
let binding = SortedFloatBinding::new();
for val in
[0.0f32, 1.0, -1.0, 123.123, f32::MAX, f32::NEG_INFINITY, f32::NAN]
{
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
4,
"sorted_f32 {} should encode to 4 bytes",
val
);
}
}
#[test]
fn test_sorted_double_binding_encoded_size() {
let binding = SortedDoubleBinding::new();
for val in
[0.0f64, 1.0, -1.0, 123.123, f64::MAX, f64::NEG_INFINITY, f64::NAN]
{
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
8,
"sorted_f64 {} should encode to 8 bytes",
val
);
}
}
#[test]
fn test_char_binding_encoded_size_variants() {
let binding = CharBinding::new();
for val in [0u16, b'a' as u16, 0x7F, 0x00FF, 0x0100, 0xFFFF] {
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&val, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
2,
"char U+{:04X} should encode to 2 bytes",
val
);
}
}
#[test]
fn test_string_binding_abc_size() {
let binding = StringBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&"abc".to_string(), &mut entry).unwrap();
assert_eq!(entry.data().len(), 5);
}
#[test]
fn test_string_binding_empty_size() {
let binding = StringBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&String::new(), &mut entry).unwrap();
assert_eq!(entry.data().len(), 2);
}
#[test]
fn test_nested_binding_coverage_int() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = IntBinding::new();
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&123i32, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let val: i32 = binding.tuple_to_object(&mut inp).unwrap();
assert_eq!(val, 123);
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_nested_binding_coverage_long() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = LongBinding::new();
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&123i64, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let val: i64 = binding.tuple_to_object(&mut inp).unwrap();
assert_eq!(val, 123);
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_nested_binding_coverage_bool() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = BoolBinding::new();
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&true, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let val: bool = binding.tuple_to_object(&mut inp).unwrap();
assert!(val);
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_nested_binding_coverage_sorted_float() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = SortedFloatBinding::new();
let val = 123.123f32;
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&val, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let got: f32 = binding.tuple_to_object(&mut inp).unwrap();
assert_eq!(got.to_bits(), val.to_bits());
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_nested_binding_coverage_sorted_double() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = SortedDoubleBinding::new();
let val = 123.123f64;
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&val, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let got: f64 = binding.tuple_to_object(&mut inp).unwrap();
assert_eq!(got.to_bits(), val.to_bits());
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_nested_binding_coverage_packed_int() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = PackedIntBinding::new();
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&1234i32, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let val: i32 = binding.tuple_to_object(&mut inp).unwrap();
assert_eq!(val, 1234);
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_nested_binding_coverage_packed_long() {
use crate::tuple::tuple_binding::TupleBinding;
let binding = PackedLongBinding::new();
let mut out = TupleOutput::new();
out.write_string("abc");
binding.object_to_tuple(&1234i64, &mut out).unwrap();
out.write_string("xyz");
let mut inp = TupleInput::new(&out.to_vec());
assert_eq!(inp.read_string().unwrap(), "abc");
let val: i64 = binding.tuple_to_object(&mut inp).unwrap();
assert_eq!(val, 1234);
assert_eq!(inp.read_string().unwrap(), "xyz");
assert_eq!(inp.available(), 0);
}
#[test]
fn test_packed_int_binding_1234_size() {
let binding = PackedIntBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&1234i32, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
3,
"1234 should encode to 3 bytes (header + 2 value bytes)"
);
let got = binding.entry_to_object(&entry).unwrap();
assert_eq!(got, 1234i32);
}
#[test]
fn test_packed_long_binding_1234_size() {
let binding = PackedLongBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&1234i64, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
3,
"1234L should encode to 3 bytes (header + 2 value bytes)"
);
let got = binding.entry_to_object(&entry).unwrap();
assert_eq!(got, 1234i64);
}
#[test]
fn test_sorted_packed_int_binding_1234_size() {
let binding = SortedPackedIntBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&1234i32, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
3,
"1234 should encode to 3 bytes in SortedPackedIntBinding"
);
let got = binding.entry_to_object(&entry).unwrap();
assert_eq!(got, 1234i32);
}
#[test]
fn test_sorted_packed_long_binding_1234_size() {
let binding = SortedPackedLongBinding::new();
let mut entry = DatabaseEntry::new();
binding.object_to_entry(&1234i64, &mut entry).unwrap();
assert_eq!(
entry.data().len(),
3,
"1234L should encode to 3 bytes in SortedPackedLongBinding"
);
let got = binding.entry_to_object(&entry).unwrap();
assert_eq!(got, 1234i64);
}
#[test]
fn test_all_primitive_bindings_round_trip() {
{
let b = BoolBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&true, &mut e).unwrap();
assert!(b.entry_to_object(&e).unwrap());
}
{
let b = ByteBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&123u8, &mut e).unwrap();
assert_eq!(b.entry_to_object(&e).unwrap(), 123u8);
}
{
let b = ShortBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&123i16, &mut e).unwrap();
assert_eq!(b.entry_to_object(&e).unwrap(), 123i16);
}
{
let b = IntBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&123i32, &mut e).unwrap();
assert_eq!(b.entry_to_object(&e).unwrap(), 123i32);
}
{
let b = LongBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&123i64, &mut e).unwrap();
assert_eq!(b.entry_to_object(&e).unwrap(), 123i64);
}
{
let b = FloatBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&123.123f32, &mut e).unwrap();
let got = b.entry_to_object(&e).unwrap();
assert!((got - 123.123f32).abs() < 1e-3);
}
{
let b = DoubleBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&123.123f64, &mut e).unwrap();
let got = b.entry_to_object(&e).unwrap();
assert!((got - 123.123f64).abs() < 1e-9);
}
{
let b = CharBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&(b'a' as u16), &mut e).unwrap();
assert_eq!(b.entry_to_object(&e).unwrap(), b'a' as u16);
}
{
let b = StringBinding::new();
let mut e = DatabaseEntry::new();
b.object_to_entry(&"abc".to_string(), &mut e).unwrap();
assert_eq!(b.entry_to_object(&e).unwrap(), "abc".to_string());
}
}
#[test]
fn test_string_ordering_sorted_sequence() {
let binding = StringBinding::new();
let data = vec![
"".to_string(),
"\u{0001}".to_string(),
"\u{0002}".to_string(),
"A".to_string(),
"a".to_string(),
"ab".to_string(),
"b".to_string(),
"bb".to_string(),
"bba".to_string(),
"c".to_string(),
];
let encoded: Vec<Vec<u8>> =
data.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"string ordering violated: {:?} should sort before {:?}",
data[i],
data[i + 1]
);
}
}
#[test]
fn test_u8_binding_ordering() {
let binding = ByteBinding::new();
let values: Vec<u8> = vec![0, 1, 0x7F, 0xFF];
let encoded: Vec<Vec<u8>> =
values.iter().map(|v| encoded_bytes(&binding, v)).collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"u8 binding ordering violated: {} should sort before {}",
values[i],
values[i + 1]
);
}
}
}