Skip to main content

noxu_bind/tuple/
primitive_bindings.rs

1//! Type-specific tuple bindings for primitive types.
2//!
3//! Each binding implements `EntryBinding<T>` and `TupleBinding<T>` for a
4//! specific primitive type, using the sortable encoding from `TupleOutput`
5//! and `TupleInput`.
6//!
7//! Primitive type bindings for key encoding.
8
9use noxu_db::DatabaseEntry;
10
11use crate::entry_binding::EntryBinding;
12use crate::error::Result;
13use crate::tuple::tuple_binding::TupleBinding;
14use crate::tuple::tuple_input::TupleInput;
15use crate::tuple::tuple_output::TupleOutput;
16
17// ---------------------------------------------------------------------------
18// Macro to reduce boilerplate for simple bindings
19// ---------------------------------------------------------------------------
20
21macro_rules! impl_primitive_binding {
22    (
23        $(#[$meta:meta])*
24        $name:ident, $ty:ty,
25        write: $write_method:ident,
26        read: $read_method:ident
27    ) => {
28        $(#[$meta])*
29        #[derive(Debug, Clone, Copy, Default)]
30        pub struct $name;
31
32        impl $name {
33            /// Creates a new binding instance.
34            pub fn new() -> Self {
35                Self
36            }
37        }
38
39        impl EntryBinding<$ty> for $name {
40            fn entry_to_object(&self, entry: &DatabaseEntry) -> Result<$ty> {
41                let mut input = Self::entry_to_input(entry);
42                self.tuple_to_object(&mut input)
43            }
44
45            fn object_to_entry(&self, object: &$ty, entry: &mut DatabaseEntry) -> Result<()> {
46                let mut output = TupleOutput::new();
47                self.object_to_tuple(object, &mut output)?;
48                entry.set_data_vec(output.into_vec());
49                Ok(())
50            }
51        }
52
53        impl TupleBinding<$ty> for $name {
54            fn tuple_to_object(&self, input: &mut TupleInput) -> Result<$ty> {
55                input.$read_method()
56            }
57
58            fn object_to_tuple(&self, object: &$ty, output: &mut TupleOutput) -> Result<()> {
59                output.$write_method(*object);
60                Ok(())
61            }
62        }
63    };
64}
65
66impl_primitive_binding!(
67    /// Binding for `bool` values.
68    ///
69    /// Encodes as a single byte: 1 for true, 0 for false.
70    ///
71    ///
72    BoolBinding, bool,
73    write: write_bool,
74    read: read_bool
75);
76
77impl_primitive_binding!(
78    /// Binding for unsigned `u8` byte values.
79    ///
80    /// Stored as a single raw byte.
81    ///
82    /// Unsigned byte binding.
83    ByteBinding, u8,
84    write: write_u8,
85    read: read_u8
86);
87
88impl_primitive_binding!(
89    /// Binding for signed `i16` (short) values.
90    ///
91    /// Uses big-endian encoding with sign bit flipped for sortable ordering.
92    ///
93    ///
94    ShortBinding, i16,
95    write: write_i16,
96    read: read_i16
97);
98
99impl_primitive_binding!(
100    /// Binding for signed `i32` (int) values.
101    ///
102    /// Uses big-endian encoding with sign bit flipped for sortable ordering.
103    ///
104    ///
105    IntBinding, i32,
106    write: write_i32,
107    read: read_i32
108);
109
110impl_primitive_binding!(
111    /// Binding for signed `i64` (long) values.
112    ///
113    /// Uses big-endian encoding with sign bit flipped for sortable ordering.
114    ///
115    ///
116    LongBinding, i64,
117    write: write_i64,
118    read: read_i64
119);
120
121impl_primitive_binding!(
122    /// Binding for `f32` (float) values using unsorted encoding.
123    ///
124    /// Stored as raw IEEE 754 big-endian bits. The byte representation does NOT
125    /// sort in the same order as the float values. Use `SortedFloatBinding` for
126    /// sortable keys.
127    ///
128    ///
129    FloatBinding, f32,
130    write: write_float,
131    read: read_float
132);
133
134impl_primitive_binding!(
135    /// Binding for `f64` (double) values using unsorted encoding.
136    ///
137    /// Stored as raw IEEE 754 big-endian bits. The byte representation does NOT
138    /// sort in the same order as the double values. Use `SortedDoubleBinding` for
139    /// sortable keys.
140    ///
141    ///
142    DoubleBinding, f64,
143    write: write_double,
144    read: read_double
145);
146
147impl_primitive_binding!(
148    /// Binding for `f32` (float) values using sortable encoding.
149    ///
150    /// Uses sign-bit manipulation so the byte representation sorts in the
151    /// same order as the float values.
152    ///
153    ///
154    SortedFloatBinding, f32,
155    write: write_sorted_float,
156    read: read_sorted_float
157);
158
159impl_primitive_binding!(
160    /// Binding for `f64` (double) values using sortable encoding.
161    ///
162    /// Uses sign-bit manipulation so the byte representation sorts in the
163    /// same order as the double values.
164    ///
165    ///
166    SortedDoubleBinding, f64,
167    write: write_sorted_double,
168    read: read_sorted_double
169);
170
171impl_primitive_binding!(
172    /// Binding for `i32` values using packed (variable-length) encoding.
173    ///
174    /// Values in [-119, 119] are stored in a single byte. Larger values use
175    /// 2-5 bytes. This encoding is compact but NOT sortable.
176    ///
177    ///
178    PackedIntBinding, i32,
179    write: write_packed_int,
180    read: read_packed_int
181);
182
183impl_primitive_binding!(
184    /// Binding for `i64` values using packed (variable-length) encoding.
185    ///
186    /// Values in [-119, 119] are stored in a single byte. Larger values use
187    /// 2-9 bytes. This encoding is compact but NOT sortable.
188    ///
189    ///
190    PackedLongBinding, i64,
191    write: write_packed_long,
192    read: read_packed_long
193);
194
195impl_primitive_binding!(
196    /// Binding for `i32` values using sorted packed (variable-length,
197    /// order-preserving) encoding.
198    ///
199    /// Values in [-119, 120] are stored in a single byte. Larger values use
200    /// 2-5 bytes. Unlike `PackedIntBinding`, the byte representation DOES
201    /// sort in the same order as the integer values, making this suitable for
202    /// database keys when compactness is also desired.
203    ///
204    ///
205    SortedPackedIntBinding, i32,
206    write: write_sorted_packed_int,
207    read: read_sorted_packed_int
208);
209
210impl_primitive_binding!(
211    /// Binding for `i64` values using sorted packed (variable-length,
212    /// order-preserving) encoding.
213    ///
214    /// Values in [-119, 120] are stored in a single byte. Larger values use
215    /// 2-9 bytes. Unlike `PackedLongBinding`, the byte representation DOES
216    /// sort in the same order as the integer values, making this suitable for
217    /// database keys when compactness is also desired.
218    ///
219    ///
220    SortedPackedLongBinding, i64,
221    write: write_sorted_packed_long,
222    read: read_sorted_packed_long
223);
224
225impl_primitive_binding!(
226    /// Binding for `u16` values representing Java `char` (16-bit Unicode code points).
227    ///
228    /// Encodes as two big-endian bytes. This matches Java's `writeChar` /
229    /// `readChar` in `DataOutputStream` / `DataInputStream`. Sort order is
230    /// unsigned numeric (U+0000 < U+0001 < ... < U+FFFF).
231    ///
232    /// In Rust, this is represented as `u16` rather than Rust's `char` because
233    /// Java `char` covers the full [0, 65535] range including surrogate halves
234    /// which are not valid Unicode scalar values in Rust.
235    ///
236    ///
237    CharBinding, u16,
238    write: write_char,
239    read: read_char
240);
241
242// ---------------------------------------------------------------------------
243// StringBinding  -  requires special handling (reference vs owned)
244// ---------------------------------------------------------------------------
245
246/// Binding for `String` values using null-terminated UTF-8 encoding.
247///
248/// Strings are stored as their UTF-8 bytes followed by a null terminator byte.
249///
250///
251#[derive(Debug, Clone, Copy, Default)]
252pub struct StringBinding;
253
254impl StringBinding {
255    /// Creates a new `StringBinding`.
256    pub fn new() -> Self {
257        Self
258    }
259}
260
261impl EntryBinding<String> for StringBinding {
262    fn entry_to_object(&self, entry: &DatabaseEntry) -> Result<String> {
263        let mut input = Self::entry_to_input(entry);
264        self.tuple_to_object(&mut input)
265    }
266
267    fn object_to_entry(
268        &self,
269        object: &String,
270        entry: &mut DatabaseEntry,
271    ) -> Result<()> {
272        let mut output = TupleOutput::new();
273        self.object_to_tuple(object, &mut output)?;
274        entry.set_data_vec(output.into_vec());
275        Ok(())
276    }
277}
278
279impl TupleBinding<String> for StringBinding {
280    fn tuple_to_object(&self, input: &mut TupleInput) -> Result<String> {
281        input.read_string()
282    }
283
284    fn object_to_tuple(
285        &self,
286        object: &String,
287        output: &mut TupleOutput,
288    ) -> Result<()> {
289        output.write_string(object);
290        Ok(())
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    // -----------------------------------------------------------------------
299    // Round-trip tests
300    // -----------------------------------------------------------------------
301
302    #[test]
303    fn test_bool_binding_round_trip() {
304        let binding = BoolBinding::new();
305        for val in [true, false] {
306            let mut entry = DatabaseEntry::new();
307            binding.object_to_entry(&val, &mut entry).unwrap();
308            let result = binding.entry_to_object(&entry).unwrap();
309            assert_eq!(val, result);
310        }
311    }
312
313    #[test]
314    fn test_byte_binding_round_trip() {
315        let binding = ByteBinding::new();
316        for val in [0u8, 1, 127, 128, 255] {
317            let mut entry = DatabaseEntry::new();
318            binding.object_to_entry(&val, &mut entry).unwrap();
319            let result = binding.entry_to_object(&entry).unwrap();
320            assert_eq!(val, result);
321        }
322    }
323
324    #[test]
325    fn test_short_binding_round_trip() {
326        let binding = ShortBinding::new();
327        for val in [i16::MIN, -1, 0, 1, i16::MAX] {
328            let mut entry = DatabaseEntry::new();
329            binding.object_to_entry(&val, &mut entry).unwrap();
330            let result = binding.entry_to_object(&entry).unwrap();
331            assert_eq!(val, result);
332        }
333    }
334
335    #[test]
336    fn test_int_binding_round_trip() {
337        let binding = IntBinding::new();
338        for val in [i32::MIN, -1, 0, 1, i32::MAX] {
339            let mut entry = DatabaseEntry::new();
340            binding.object_to_entry(&val, &mut entry).unwrap();
341            let result = binding.entry_to_object(&entry).unwrap();
342            assert_eq!(val, result);
343        }
344    }
345
346    #[test]
347    fn test_long_binding_round_trip() {
348        let binding = LongBinding::new();
349        for val in [i64::MIN, -1, 0, 1, i64::MAX] {
350            let mut entry = DatabaseEntry::new();
351            binding.object_to_entry(&val, &mut entry).unwrap();
352            let result = binding.entry_to_object(&entry).unwrap();
353            assert_eq!(val, result);
354        }
355    }
356
357    #[test]
358    fn test_float_binding_round_trip() {
359        let binding = FloatBinding::new();
360        for val in [-1.5f32, 0.0, 1.5, f32::MAX, f32::MIN] {
361            let mut entry = DatabaseEntry::new();
362            binding.object_to_entry(&val, &mut entry).unwrap();
363            let result = binding.entry_to_object(&entry).unwrap();
364            assert_eq!(val, result);
365        }
366    }
367
368    #[test]
369    fn test_double_binding_round_trip() {
370        let binding = DoubleBinding::new();
371        for val in [-1.5f64, 0.0, 1.5, f64::MAX, f64::MIN] {
372            let mut entry = DatabaseEntry::new();
373            binding.object_to_entry(&val, &mut entry).unwrap();
374            let result = binding.entry_to_object(&entry).unwrap();
375            assert_eq!(val, result);
376        }
377    }
378
379    #[test]
380    fn test_sorted_float_binding_round_trip() {
381        let binding = SortedFloatBinding::new();
382        for val in [-1.5f32, -0.0, 0.0, 1.5, f32::MAX, f32::MIN] {
383            let mut entry = DatabaseEntry::new();
384            binding.object_to_entry(&val, &mut entry).unwrap();
385            let result = binding.entry_to_object(&entry).unwrap();
386            // -0.0 and 0.0 have different bits but compare equal
387            assert_eq!(val.to_bits(), result.to_bits(), "failed for {}", val);
388        }
389    }
390
391    #[test]
392    fn test_sorted_double_binding_round_trip() {
393        let binding = SortedDoubleBinding::new();
394        for val in [-1.5f64, -0.0, 0.0, 1.5, f64::MAX, f64::MIN] {
395            let mut entry = DatabaseEntry::new();
396            binding.object_to_entry(&val, &mut entry).unwrap();
397            let result = binding.entry_to_object(&entry).unwrap();
398            assert_eq!(val.to_bits(), result.to_bits(), "failed for {}", val);
399        }
400    }
401
402    #[test]
403    fn test_packed_int_binding_round_trip() {
404        let binding = PackedIntBinding::new();
405        for val in [i32::MIN, -120, -119, -1, 0, 1, 119, 120, i32::MAX] {
406            let mut entry = DatabaseEntry::new();
407            binding.object_to_entry(&val, &mut entry).unwrap();
408            let result = binding.entry_to_object(&entry).unwrap();
409            assert_eq!(val, result, "failed for {}", val);
410        }
411    }
412
413    #[test]
414    fn test_packed_long_binding_round_trip() {
415        let binding = PackedLongBinding::new();
416        for val in [i64::MIN, -120, -119, -1, 0, 1, 119, 120, i64::MAX] {
417            let mut entry = DatabaseEntry::new();
418            binding.object_to_entry(&val, &mut entry).unwrap();
419            let result = binding.entry_to_object(&entry).unwrap();
420            assert_eq!(val, result, "failed for {}", val);
421        }
422    }
423
424    #[test]
425    fn test_string_binding_round_trip() {
426        let binding = StringBinding::new();
427        for val in ["", "hello", "world", "unicode: \u{1F600}"] {
428            let s = val.to_string();
429            let mut entry = DatabaseEntry::new();
430            binding.object_to_entry(&s, &mut entry).unwrap();
431            let result = binding.entry_to_object(&entry).unwrap();
432            assert_eq!(s, result);
433        }
434    }
435
436    #[test]
437    fn test_string_binding_embedded_null() {
438        let binding = StringBinding::new();
439        // String with a single embedded null byte
440        let s = "hello\x00world".to_string();
441        let mut entry = DatabaseEntry::new();
442        binding.object_to_entry(&s, &mut entry).unwrap();
443        let result = binding.entry_to_object(&entry).unwrap();
444        assert_eq!(s, result);
445    }
446
447    #[test]
448    fn test_string_binding_multiple_embedded_nulls() {
449        let binding = StringBinding::new();
450        let s = "\x00\x00\x00".to_string();
451        let mut entry = DatabaseEntry::new();
452        binding.object_to_entry(&s, &mut entry).unwrap();
453        let result = binding.entry_to_object(&entry).unwrap();
454        assert_eq!(s, result);
455    }
456
457    #[test]
458    fn test_string_binding_null_at_start_and_end() {
459        let binding = StringBinding::new();
460        let s = "\x00hello\x00".to_string();
461        let mut entry = DatabaseEntry::new();
462        binding.object_to_entry(&s, &mut entry).unwrap();
463        let result = binding.entry_to_object(&entry).unwrap();
464        assert_eq!(s, result);
465    }
466
467    #[test]
468    fn test_string_binding_null_escape_encoded_correctly() {
469        // Verify that embedded 0x00 is encoded as [0x00, 0x01], not [0x00]
470        let binding = StringBinding::new();
471        let s = "a\x00b".to_string();
472        let mut entry = DatabaseEntry::new();
473        binding.object_to_entry(&s, &mut entry).unwrap();
474        let raw = entry.data();
475        // Expected: 'a', 0x00, 0x01, 'b', 0x00, 0x00
476        assert_eq!(raw, &[b'a', 0x00, 0x01, b'b', 0x00, 0x00]);
477    }
478
479    #[test]
480    fn test_string_binding_terminator_encoded_correctly() {
481        // Empty string should encode as just the two-byte terminator [0x00, 0x00]
482        let binding = StringBinding::new();
483        let s = String::new();
484        let mut entry = DatabaseEntry::new();
485        binding.object_to_entry(&s, &mut entry).unwrap();
486        let raw = entry.data();
487        assert_eq!(raw, &[0x00, 0x00]);
488    }
489
490    #[test]
491    fn test_string_binding_two_strings_in_tuple() {
492        // Two consecutive strings in a TupleOutput, both readable back
493        use crate::tuple::tuple_input::TupleInput;
494        use crate::tuple::tuple_output::TupleOutput;
495        let mut out = TupleOutput::new();
496        out.write_string("foo\x00bar");
497        out.write_string("baz");
498        let mut inp = TupleInput::new(&out.to_vec());
499        assert_eq!(inp.read_string().unwrap(), "foo\x00bar");
500        assert_eq!(inp.read_string().unwrap(), "baz");
501    }
502
503    // -----------------------------------------------------------------------
504    // Sort ordering tests
505    // -----------------------------------------------------------------------
506
507    fn encoded_bytes<T>(binding: &impl EntryBinding<T>, val: &T) -> Vec<u8> {
508        let mut entry = DatabaseEntry::new();
509        binding.object_to_entry(val, &mut entry).unwrap();
510        entry.data().to_vec()
511    }
512
513    #[test]
514    fn test_short_binding_sort_order() {
515        let binding = ShortBinding::new();
516        let values: Vec<i16> = vec![i16::MIN, -100, -1, 0, 1, 100, i16::MAX];
517        let encoded: Vec<Vec<u8>> =
518            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
519        for i in 0..encoded.len() - 1 {
520            assert!(
521                encoded[i] < encoded[i + 1],
522                "{} should sort before {}",
523                values[i],
524                values[i + 1]
525            );
526        }
527    }
528
529    #[test]
530    fn test_int_binding_sort_order() {
531        let binding = IntBinding::new();
532        let values: Vec<i32> = vec![i32::MIN, -100, -1, 0, 1, 100, i32::MAX];
533        let encoded: Vec<Vec<u8>> =
534            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
535        for i in 0..encoded.len() - 1 {
536            assert!(
537                encoded[i] < encoded[i + 1],
538                "{} should sort before {}",
539                values[i],
540                values[i + 1]
541            );
542        }
543    }
544
545    #[test]
546    fn test_long_binding_sort_order() {
547        let binding = LongBinding::new();
548        let values: Vec<i64> = vec![i64::MIN, -100, -1, 0, 1, 100, i64::MAX];
549        let encoded: Vec<Vec<u8>> =
550            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
551        for i in 0..encoded.len() - 1 {
552            assert!(
553                encoded[i] < encoded[i + 1],
554                "{} should sort before {}",
555                values[i],
556                values[i + 1]
557            );
558        }
559    }
560
561    #[test]
562    fn test_sorted_float_binding_sort_order() {
563        let binding = SortedFloatBinding::new();
564        let values: Vec<f32> = vec![
565            f32::NEG_INFINITY,
566            f32::MIN,
567            -100.0,
568            -1.0,
569            -f32::MIN_POSITIVE,
570            0.0,
571            f32::MIN_POSITIVE,
572            1.0,
573            100.0,
574            f32::MAX,
575            f32::INFINITY,
576        ];
577        let encoded: Vec<Vec<u8>> =
578            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
579        for i in 0..encoded.len() - 1 {
580            assert!(
581                encoded[i] < encoded[i + 1],
582                "{} (encoded {:?}) should sort before {} (encoded {:?})",
583                values[i],
584                encoded[i],
585                values[i + 1],
586                encoded[i + 1]
587            );
588        }
589    }
590
591    #[test]
592    fn test_sorted_double_binding_sort_order() {
593        let binding = SortedDoubleBinding::new();
594        let values: Vec<f64> = vec![
595            f64::NEG_INFINITY,
596            f64::MIN,
597            -100.0,
598            -1.0,
599            -f64::MIN_POSITIVE,
600            0.0,
601            f64::MIN_POSITIVE,
602            1.0,
603            100.0,
604            f64::MAX,
605            f64::INFINITY,
606        ];
607        let encoded: Vec<Vec<u8>> =
608            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
609        for i in 0..encoded.len() - 1 {
610            assert!(
611                encoded[i] < encoded[i + 1],
612                "{} should sort before {}",
613                values[i],
614                values[i + 1]
615            );
616        }
617    }
618
619    #[test]
620    fn test_string_binding_sort_order() {
621        let binding = StringBinding::new();
622        let values: Vec<String> = vec![
623            "".to_string(),
624            "a".to_string(),
625            "ab".to_string(),
626            "b".to_string(),
627            "z".to_string(),
628        ];
629        let encoded: Vec<Vec<u8>> =
630            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
631        for i in 0..encoded.len() - 1 {
632            assert!(
633                encoded[i] < encoded[i + 1],
634                "{:?} should sort before {:?}",
635                values[i],
636                values[i + 1]
637            );
638        }
639    }
640
641    // -----------------------------------------------------------------------
642    // Edge case tests
643    // -----------------------------------------------------------------------
644
645    #[test]
646    fn test_packed_int_boundary_values() {
647        let binding = PackedIntBinding::new();
648        // Test boundary values around the single-byte range
649        for val in [-120, -119, 119, 120, 256, -257, 65536, -65537] {
650            let mut entry = DatabaseEntry::new();
651            binding.object_to_entry(&val, &mut entry).unwrap();
652            let result = binding.entry_to_object(&entry).unwrap();
653            assert_eq!(val, result, "boundary test failed for {}", val);
654        }
655    }
656
657    #[test]
658    fn test_packed_long_boundary_values() {
659        let binding = PackedLongBinding::new();
660        for val in [-120i64, -119, 119, 120, 256, -257, 1 << 32, -(1 << 32)] {
661            let mut entry = DatabaseEntry::new();
662            binding.object_to_entry(&val, &mut entry).unwrap();
663            let result = binding.entry_to_object(&entry).unwrap();
664            assert_eq!(val, result, "boundary test failed for {}", val);
665        }
666    }
667
668    #[test]
669    fn test_sorted_float_nan() {
670        // NaN should round-trip (bits preserved)
671        let binding = SortedFloatBinding::new();
672        let val = f32::NAN;
673        let mut entry = DatabaseEntry::new();
674        binding.object_to_entry(&val, &mut entry).unwrap();
675        let result = binding.entry_to_object(&entry).unwrap();
676        assert!(result.is_nan());
677    }
678
679    #[test]
680    fn test_sorted_double_nan() {
681        let binding = SortedDoubleBinding::new();
682        let val = f64::NAN;
683        let mut entry = DatabaseEntry::new();
684        binding.object_to_entry(&val, &mut entry).unwrap();
685        let result = binding.entry_to_object(&entry).unwrap();
686        assert!(result.is_nan());
687    }
688
689    #[test]
690    fn test_sorted_float_negative_zero() {
691        let binding = SortedFloatBinding::new();
692        let val = -0.0f32;
693        let mut entry = DatabaseEntry::new();
694        binding.object_to_entry(&val, &mut entry).unwrap();
695        let result = binding.entry_to_object(&entry).unwrap();
696        // -0.0 should preserve its bit pattern
697        assert_eq!(val.to_bits(), result.to_bits());
698    }
699
700    #[test]
701    fn test_sorted_double_negative_zero() {
702        let binding = SortedDoubleBinding::new();
703        let val = -0.0f64;
704        let mut entry = DatabaseEntry::new();
705        binding.object_to_entry(&val, &mut entry).unwrap();
706        let result = binding.entry_to_object(&entry).unwrap();
707        assert_eq!(val.to_bits(), result.to_bits());
708    }
709
710    #[test]
711    fn test_int_binding_encoded_size() {
712        let binding = IntBinding::new();
713        let mut entry = DatabaseEntry::new();
714        binding.object_to_entry(&42i32, &mut entry).unwrap();
715        assert_eq!(entry.data().len(), 4);
716    }
717
718    #[test]
719    fn test_long_binding_encoded_size() {
720        let binding = LongBinding::new();
721        let mut entry = DatabaseEntry::new();
722        binding.object_to_entry(&42i64, &mut entry).unwrap();
723        assert_eq!(entry.data().len(), 8);
724    }
725
726    #[test]
727    fn test_packed_int_compact_size() {
728        let binding = PackedIntBinding::new();
729        // Values in [-119, 119] should be 1 byte
730        let mut entry = DatabaseEntry::new();
731        binding.object_to_entry(&0i32, &mut entry).unwrap();
732        assert_eq!(entry.data().len(), 1);
733
734        binding.object_to_entry(&119i32, &mut entry).unwrap();
735        assert_eq!(entry.data().len(), 1);
736
737        binding.object_to_entry(&(-119i32), &mut entry).unwrap();
738        assert_eq!(entry.data().len(), 1);
739
740        // 120 should need more than 1 byte
741        binding.object_to_entry(&120i32, &mut entry).unwrap();
742        assert!(entry.data().len() > 1);
743    }
744
745    // -----------------------------------------------------------------------
746    // SortedPackedIntBinding tests
747    // -----------------------------------------------------------------------
748
749    #[test]
750    fn test_sorted_packed_int_binding_round_trip() {
751        let binding = SortedPackedIntBinding::new();
752        for val in [i32::MIN, -120, -119, -1, 0, 1, 119, 120, 121, i32::MAX] {
753            let mut entry = DatabaseEntry::new();
754            binding.object_to_entry(&val, &mut entry).unwrap();
755            let result = binding.entry_to_object(&entry).unwrap();
756            assert_eq!(val, result, "round-trip failed for {}", val);
757        }
758    }
759
760    #[test]
761    fn test_sorted_packed_int_binding_sort_order() {
762        let binding = SortedPackedIntBinding::new();
763        let values: Vec<i32> = vec![
764            i32::MIN,
765            -1_000_000,
766            -120,
767            -119,
768            -1,
769            0,
770            1,
771            119,
772            120,
773            121,
774            1_000_000,
775            i32::MAX,
776        ];
777        let encoded: Vec<Vec<u8>> =
778            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
779        for i in 0..encoded.len() - 1 {
780            assert!(
781                encoded[i] < encoded[i + 1],
782                "sort order violated: {} (encoded {:?}) should sort before {} (encoded {:?})",
783                values[i],
784                encoded[i],
785                values[i + 1],
786                encoded[i + 1]
787            );
788        }
789    }
790
791    #[test]
792    fn test_sorted_packed_int_single_byte_range() {
793        // Values in [-119, 120] must encode to exactly 1 byte
794        let binding = SortedPackedIntBinding::new();
795        for val in (-119..=120i32).step_by(1) {
796            let mut entry = DatabaseEntry::new();
797            binding.object_to_entry(&val, &mut entry).unwrap();
798            assert_eq!(
799                entry.data().len(),
800                1,
801                "value {} should encode in 1 byte",
802                val
803            );
804        }
805    }
806
807    #[test]
808    fn test_sorted_packed_int_multi_byte() {
809        let binding = SortedPackedIntBinding::new();
810        for val in [121i32, -120, i32::MAX, i32::MIN] {
811            let mut entry = DatabaseEntry::new();
812            binding.object_to_entry(&val, &mut entry).unwrap();
813            assert!(
814                entry.data().len() > 1,
815                "value {} should encode in more than 1 byte",
816                val
817            );
818        }
819    }
820
821    // -----------------------------------------------------------------------
822    // SortedPackedLongBinding tests
823    // -----------------------------------------------------------------------
824
825    #[test]
826    fn test_sorted_packed_long_binding_round_trip() {
827        let binding = SortedPackedLongBinding::new();
828        for val in [
829            i64::MIN,
830            -1_000_000_000_000i64,
831            -120,
832            -119,
833            -1,
834            0,
835            1,
836            119,
837            120,
838            121,
839            1_000_000_000_000i64,
840            i64::MAX,
841        ] {
842            let mut entry = DatabaseEntry::new();
843            binding.object_to_entry(&val, &mut entry).unwrap();
844            let result = binding.entry_to_object(&entry).unwrap();
845            assert_eq!(val, result, "round-trip failed for {}", val);
846        }
847    }
848
849    #[test]
850    fn test_sorted_packed_long_binding_sort_order() {
851        let binding = SortedPackedLongBinding::new();
852        let values: Vec<i64> = vec![
853            i64::MIN,
854            -1_000_000_000_000,
855            -120,
856            -119,
857            -1,
858            0,
859            1,
860            119,
861            120,
862            121,
863            1_000_000_000_000,
864            i64::MAX,
865        ];
866        let encoded: Vec<Vec<u8>> =
867            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
868        for i in 0..encoded.len() - 1 {
869            assert!(
870                encoded[i] < encoded[i + 1],
871                "sort order violated: {} (encoded {:?}) should sort before {} (encoded {:?})",
872                values[i],
873                encoded[i],
874                values[i + 1],
875                encoded[i + 1]
876            );
877        }
878    }
879
880    #[test]
881    fn test_sorted_packed_long_single_byte_range() {
882        // Values in [-119, 120] must encode to exactly 1 byte
883        let binding = SortedPackedLongBinding::new();
884        for val in (-119..=120i64).step_by(1) {
885            let mut entry = DatabaseEntry::new();
886            binding.object_to_entry(&val, &mut entry).unwrap();
887            assert_eq!(
888                entry.data().len(),
889                1,
890                "value {} should encode in 1 byte",
891                val
892            );
893        }
894    }
895
896    #[test]
897    fn test_sorted_packed_long_multi_byte() {
898        let binding = SortedPackedLongBinding::new();
899        for val in [121i64, -120, i64::MAX, i64::MIN] {
900            let mut entry = DatabaseEntry::new();
901            binding.object_to_entry(&val, &mut entry).unwrap();
902            assert!(
903                entry.data().len() > 1,
904                "value {} should encode in more than 1 byte",
905                val
906            );
907        }
908    }
909
910    // -----------------------------------------------------------------------
911    // CharBinding tests
912    // -----------------------------------------------------------------------
913
914    #[test]
915    fn test_char_binding_round_trip() {
916        let binding = CharBinding::new();
917        for val in
918            [0u16, 1, 'A' as u16, 'a' as u16, 0x00FF, 0x0100, 0xD800, 0xFFFF]
919        {
920            let mut entry = DatabaseEntry::new();
921            binding.object_to_entry(&val, &mut entry).unwrap();
922            let result = binding.entry_to_object(&entry).unwrap();
923            assert_eq!(val, result, "round-trip failed for U+{:04X}", val);
924        }
925    }
926
927    #[test]
928    fn test_char_binding_encoded_size() {
929        let binding = CharBinding::new();
930        let mut entry = DatabaseEntry::new();
931        binding.object_to_entry(&(b'A' as u16), &mut entry).unwrap();
932        assert_eq!(
933            entry.data().len(),
934            2,
935            "char should always encode to 2 bytes"
936        );
937    }
938
939    #[test]
940    fn test_char_binding_big_endian_encoding() {
941        // 'A' is U+0041. Big-endian: [0x00, 0x41]
942        let binding = CharBinding::new();
943        let val = b'A' as u16; // 0x0041
944        let mut entry = DatabaseEntry::new();
945        binding.object_to_entry(&val, &mut entry).unwrap();
946        assert_eq!(
947            entry.data(),
948            &[0x00, 0x41],
949            "char 'A' (U+0041) should encode as [0x00, 0x41]"
950        );
951    }
952
953    #[test]
954    fn test_char_binding_high_codepoint_encoding() {
955        // U+FF41 (FULLWIDTH LATIN SMALL LETTER A). Big-endian: [0xFF, 0x41]
956        let binding = CharBinding::new();
957        let val: u16 = 0xFF41;
958        let mut entry = DatabaseEntry::new();
959        binding.object_to_entry(&val, &mut entry).unwrap();
960        assert_eq!(
961            entry.data(),
962            &[0xFF, 0x41],
963            "U+FF41 should encode as [0xFF, 0x41]"
964        );
965    }
966
967    #[test]
968    fn test_char_binding_sort_order() {
969        // u16 sort order should match byte-wise comparison (big-endian is unsigned)
970        let binding = CharBinding::new();
971        let values: Vec<u16> = vec![
972            0x0000, 0x0041, 0x007F, 0x0080, 0x00FF, 0x0100, 0xD800, 0xFFFF,
973        ];
974        let encoded: Vec<Vec<u8>> =
975            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
976        for i in 0..encoded.len() - 1 {
977            assert!(
978                encoded[i] < encoded[i + 1],
979                "sort order violated: U+{:04X} should sort before U+{:04X}",
980                values[i],
981                values[i + 1]
982            );
983        }
984    }
985
986    #[test]
987    fn test_sorted_packed_int_sort_order_vs_fixed_int() {
988        // Sorted packed int and fixed-width int should agree on sort order
989        let spb = SortedPackedIntBinding::new();
990        let ib = IntBinding::new();
991        let values: Vec<i32> = vec![-1000, -1, 0, 1, 1000];
992        // Both should produce monotonically increasing byte sequences
993        let sp_encoded: Vec<Vec<u8>> =
994            values.iter().map(|v| encoded_bytes(&spb, v)).collect();
995        let i_encoded: Vec<Vec<u8>> =
996            values.iter().map(|v| encoded_bytes(&ib, v)).collect();
997        for i in 0..sp_encoded.len() - 1 {
998            assert!(
999                sp_encoded[i] < sp_encoded[i + 1],
1000                "SortedPackedIntBinding sort order violated for {} < {}",
1001                values[i],
1002                values[i + 1]
1003            );
1004            assert!(
1005                i_encoded[i] < i_encoded[i + 1],
1006                "IntBinding sort order violated for {} < {}",
1007                values[i],
1008                values[i + 1]
1009            );
1010        }
1011    }
1012
1013    #[test]
1014    fn test_i8_sort_order() {
1015        let values: Vec<i8> = vec![-128, -1, 0, 1, 127];
1016        let encoded: Vec<Vec<u8>> = values
1017            .iter()
1018            .map(|&v| {
1019                let mut out = TupleOutput::new();
1020                out.write_i8(v);
1021                out.to_vec()
1022            })
1023            .collect();
1024        for i in 0..encoded.len() - 1 {
1025            assert!(
1026                encoded[i] < encoded[i + 1],
1027                "i8 sort order: {} should be < {}",
1028                values[i],
1029                values[i + 1]
1030            );
1031        }
1032    }
1033
1034    // -----------------------------------------------------------------------
1035    // Ported from TupleBindingTest: byte-size assertions per binding type
1036    // -----------------------------------------------------------------------
1037
1038    /// TupleBindingTest: BoolBinding encodes to exactly 1 byte.
1039    #[test]
1040    fn test_bool_binding_encoded_size() {
1041        let binding = BoolBinding::new();
1042        for val in [true, false] {
1043            let mut entry = DatabaseEntry::new();
1044            binding.object_to_entry(&val, &mut entry).unwrap();
1045            assert_eq!(
1046                entry.data().len(),
1047                1,
1048                "bool {} should encode to 1 byte",
1049                val
1050            );
1051        }
1052    }
1053
1054    /// TupleBindingTest: ByteBinding encodes to exactly 1 byte.
1055    #[test]
1056    fn test_byte_binding_encoded_size() {
1057        let binding = ByteBinding::new();
1058        for val in [0u8, 1, 123, 255] {
1059            let mut entry = DatabaseEntry::new();
1060            binding.object_to_entry(&val, &mut entry).unwrap();
1061            assert_eq!(
1062                entry.data().len(),
1063                1,
1064                "u8 {} should encode to 1 byte",
1065                val
1066            );
1067        }
1068    }
1069
1070    /// TupleBindingTest: ShortBinding encodes to exactly 2 bytes.
1071    #[test]
1072    fn test_short_binding_encoded_size() {
1073        let binding = ShortBinding::new();
1074        for val in [i16::MIN, -1i16, 0, 1, 123, i16::MAX] {
1075            let mut entry = DatabaseEntry::new();
1076            binding.object_to_entry(&val, &mut entry).unwrap();
1077            assert_eq!(
1078                entry.data().len(),
1079                2,
1080                "i16 {} should encode to 2 bytes",
1081                val
1082            );
1083        }
1084    }
1085
1086    /// TupleBindingTest: FloatBinding encodes to exactly 4 bytes.
1087    #[test]
1088    fn test_float_binding_encoded_size() {
1089        let binding = FloatBinding::new();
1090        for val in [0.0f32, 1.0, -1.0, 123.123, f32::MAX, f32::MIN, f32::NAN] {
1091            let mut entry = DatabaseEntry::new();
1092            binding.object_to_entry(&val, &mut entry).unwrap();
1093            assert_eq!(
1094                entry.data().len(),
1095                4,
1096                "f32 {} should encode to 4 bytes",
1097                val
1098            );
1099        }
1100    }
1101
1102    /// TupleBindingTest: DoubleBinding encodes to exactly 8 bytes.
1103    #[test]
1104    fn test_double_binding_encoded_size() {
1105        let binding = DoubleBinding::new();
1106        for val in [0.0f64, 1.0, -1.0, 123.123, f64::MAX, f64::MIN, f64::NAN] {
1107            let mut entry = DatabaseEntry::new();
1108            binding.object_to_entry(&val, &mut entry).unwrap();
1109            assert_eq!(
1110                entry.data().len(),
1111                8,
1112                "f64 {} should encode to 8 bytes",
1113                val
1114            );
1115        }
1116    }
1117
1118    /// TupleBindingTest: SortedFloatBinding encodes to exactly 4 bytes.
1119    #[test]
1120    fn test_sorted_float_binding_encoded_size() {
1121        let binding = SortedFloatBinding::new();
1122        for val in
1123            [0.0f32, 1.0, -1.0, 123.123, f32::MAX, f32::NEG_INFINITY, f32::NAN]
1124        {
1125            let mut entry = DatabaseEntry::new();
1126            binding.object_to_entry(&val, &mut entry).unwrap();
1127            assert_eq!(
1128                entry.data().len(),
1129                4,
1130                "sorted_f32 {} should encode to 4 bytes",
1131                val
1132            );
1133        }
1134    }
1135
1136    /// TupleBindingTest: SortedDoubleBinding encodes to exactly 8 bytes.
1137    #[test]
1138    fn test_sorted_double_binding_encoded_size() {
1139        let binding = SortedDoubleBinding::new();
1140        for val in
1141            [0.0f64, 1.0, -1.0, 123.123, f64::MAX, f64::NEG_INFINITY, f64::NAN]
1142        {
1143            let mut entry = DatabaseEntry::new();
1144            binding.object_to_entry(&val, &mut entry).unwrap();
1145            assert_eq!(
1146                entry.data().len(),
1147                8,
1148                "sorted_f64 {} should encode to 8 bytes",
1149                val
1150            );
1151        }
1152    }
1153
1154    /// TupleBindingTest: CharBinding encodes to exactly 2 bytes.
1155    #[test]
1156    fn test_char_binding_encoded_size_variants() {
1157        let binding = CharBinding::new();
1158        for val in [0u16, b'a' as u16, 0x7F, 0x00FF, 0x0100, 0xFFFF] {
1159            let mut entry = DatabaseEntry::new();
1160            binding.object_to_entry(&val, &mut entry).unwrap();
1161            assert_eq!(
1162                entry.data().len(),
1163                2,
1164                "char U+{:04X} should encode to 2 bytes",
1165                val
1166            );
1167        }
1168    }
1169
1170    /// TupleBindingTest: StringBinding "abc" encodes to 5 bytes (3 UTF-8 + 2-byte terminator).
1171    #[test]
1172    fn test_string_binding_abc_size() {
1173        let binding = StringBinding::new();
1174        let mut entry = DatabaseEntry::new();
1175        binding.object_to_entry(&"abc".to_string(), &mut entry).unwrap();
1176        // Rust uses 2-byte null terminator, so "abc" = 3 + 2 = 5 bytes
1177        assert_eq!(entry.data().len(), 5);
1178    }
1179
1180    /// TupleBindingTest: StringBinding for null-equivalent (empty) encodes to 2 bytes.
1181    #[test]
1182    fn test_string_binding_empty_size() {
1183        let binding = StringBinding::new();
1184        let mut entry = DatabaseEntry::new();
1185        binding.object_to_entry(&String::new(), &mut entry).unwrap();
1186        // Just the 2-byte null terminator
1187        assert_eq!(entry.data().len(), 2);
1188    }
1189
1190    /// TupleBindingTest: nested binding — write prefix, value, suffix; read all back.
1191    /// Ported from TupleBindingTest.forMoreCoverageTest.
1192    #[test]
1193    fn test_nested_binding_coverage_int() {
1194        use crate::tuple::tuple_binding::TupleBinding;
1195        let binding = IntBinding::new();
1196        let mut out = TupleOutput::new();
1197        out.write_string("abc");
1198        binding.object_to_tuple(&123i32, &mut out).unwrap();
1199        out.write_string("xyz");
1200
1201        let mut inp = TupleInput::new(&out.to_vec());
1202        assert_eq!(inp.read_string().unwrap(), "abc");
1203        let val: i32 = binding.tuple_to_object(&mut inp).unwrap();
1204        assert_eq!(val, 123);
1205        assert_eq!(inp.read_string().unwrap(), "xyz");
1206        assert_eq!(inp.available(), 0);
1207    }
1208
1209    /// TupleBindingTest: nested binding for long.
1210    #[test]
1211    fn test_nested_binding_coverage_long() {
1212        use crate::tuple::tuple_binding::TupleBinding;
1213        let binding = LongBinding::new();
1214        let mut out = TupleOutput::new();
1215        out.write_string("abc");
1216        binding.object_to_tuple(&123i64, &mut out).unwrap();
1217        out.write_string("xyz");
1218
1219        let mut inp = TupleInput::new(&out.to_vec());
1220        assert_eq!(inp.read_string().unwrap(), "abc");
1221        let val: i64 = binding.tuple_to_object(&mut inp).unwrap();
1222        assert_eq!(val, 123);
1223        assert_eq!(inp.read_string().unwrap(), "xyz");
1224        assert_eq!(inp.available(), 0);
1225    }
1226
1227    /// TupleBindingTest: nested binding for bool.
1228    #[test]
1229    fn test_nested_binding_coverage_bool() {
1230        use crate::tuple::tuple_binding::TupleBinding;
1231        let binding = BoolBinding::new();
1232        let mut out = TupleOutput::new();
1233        out.write_string("abc");
1234        binding.object_to_tuple(&true, &mut out).unwrap();
1235        out.write_string("xyz");
1236
1237        let mut inp = TupleInput::new(&out.to_vec());
1238        assert_eq!(inp.read_string().unwrap(), "abc");
1239        let val: bool = binding.tuple_to_object(&mut inp).unwrap();
1240        assert!(val);
1241        assert_eq!(inp.read_string().unwrap(), "xyz");
1242        assert_eq!(inp.available(), 0);
1243    }
1244
1245    /// TupleBindingTest: nested binding for sorted float.
1246    #[test]
1247    fn test_nested_binding_coverage_sorted_float() {
1248        use crate::tuple::tuple_binding::TupleBinding;
1249        let binding = SortedFloatBinding::new();
1250        let val = 123.123f32;
1251        let mut out = TupleOutput::new();
1252        out.write_string("abc");
1253        binding.object_to_tuple(&val, &mut out).unwrap();
1254        out.write_string("xyz");
1255
1256        let mut inp = TupleInput::new(&out.to_vec());
1257        assert_eq!(inp.read_string().unwrap(), "abc");
1258        let got: f32 = binding.tuple_to_object(&mut inp).unwrap();
1259        assert_eq!(got.to_bits(), val.to_bits());
1260        assert_eq!(inp.read_string().unwrap(), "xyz");
1261        assert_eq!(inp.available(), 0);
1262    }
1263
1264    /// TupleBindingTest: nested binding for sorted double.
1265    #[test]
1266    fn test_nested_binding_coverage_sorted_double() {
1267        use crate::tuple::tuple_binding::TupleBinding;
1268        let binding = SortedDoubleBinding::new();
1269        let val = 123.123f64;
1270        let mut out = TupleOutput::new();
1271        out.write_string("abc");
1272        binding.object_to_tuple(&val, &mut out).unwrap();
1273        out.write_string("xyz");
1274
1275        let mut inp = TupleInput::new(&out.to_vec());
1276        assert_eq!(inp.read_string().unwrap(), "abc");
1277        let got: f64 = binding.tuple_to_object(&mut inp).unwrap();
1278        assert_eq!(got.to_bits(), val.to_bits());
1279        assert_eq!(inp.read_string().unwrap(), "xyz");
1280        assert_eq!(inp.available(), 0);
1281    }
1282
1283    /// TupleBindingTest: nested binding for packed int.
1284    #[test]
1285    fn test_nested_binding_coverage_packed_int() {
1286        use crate::tuple::tuple_binding::TupleBinding;
1287        let binding = PackedIntBinding::new();
1288        let mut out = TupleOutput::new();
1289        out.write_string("abc");
1290        binding.object_to_tuple(&1234i32, &mut out).unwrap();
1291        out.write_string("xyz");
1292
1293        let mut inp = TupleInput::new(&out.to_vec());
1294        assert_eq!(inp.read_string().unwrap(), "abc");
1295        let val: i32 = binding.tuple_to_object(&mut inp).unwrap();
1296        assert_eq!(val, 1234);
1297        assert_eq!(inp.read_string().unwrap(), "xyz");
1298        assert_eq!(inp.available(), 0);
1299    }
1300
1301    /// TupleBindingTest: nested binding for packed long.
1302    #[test]
1303    fn test_nested_binding_coverage_packed_long() {
1304        use crate::tuple::tuple_binding::TupleBinding;
1305        let binding = PackedLongBinding::new();
1306        let mut out = TupleOutput::new();
1307        out.write_string("abc");
1308        binding.object_to_tuple(&1234i64, &mut out).unwrap();
1309        out.write_string("xyz");
1310
1311        let mut inp = TupleInput::new(&out.to_vec());
1312        assert_eq!(inp.read_string().unwrap(), "abc");
1313        let val: i64 = binding.tuple_to_object(&mut inp).unwrap();
1314        assert_eq!(val, 1234);
1315        assert_eq!(inp.read_string().unwrap(), "xyz");
1316        assert_eq!(inp.available(), 0);
1317    }
1318
1319    /// TupleBindingTest: PackedIntBinding 1234 encodes correctly.
1320    ///
1321    /// `PackedIntegerBinding.intToEntry(1234)` produces 5 bytes because uses
1322    /// an unsigned packed format that always uses fixed-width headers. Our Rust port
1323    /// uses a signed variable-length format where 1234 = 119 + 1115, which fits in
1324    /// 2 value bytes → 3 bytes total (1 header + 2 value). We test our actual encoding.
1325    #[test]
1326    fn test_packed_int_binding_1234_size() {
1327        let binding = PackedIntBinding::new();
1328        let mut entry = DatabaseEntry::new();
1329        binding.object_to_entry(&1234i32, &mut entry).unwrap();
1330        // Our signed variable-length format: 1234 - 119 = 1115, fits in 2 bytes → 3 bytes
1331        assert_eq!(
1332            entry.data().len(),
1333            3,
1334            "1234 should encode to 3 bytes (header + 2 value bytes)"
1335        );
1336        // Round-trip correctness
1337        let got = binding.entry_to_object(&entry).unwrap();
1338        assert_eq!(got, 1234i32);
1339    }
1340
1341    /// TupleBindingTest: PackedLongBinding 1234 encodes correctly.
1342    ///
1343    /// `PackedLongBinding.longToEntry(1234)` produces 9 bytes (fixed-width unsigned
1344    /// format). Our Rust port uses a signed variable-length format: 1234 - 119 = 1115,
1345    /// fits in 2 bytes → 3 bytes total (1 header + 2 value).
1346    #[test]
1347    fn test_packed_long_binding_1234_size() {
1348        let binding = PackedLongBinding::new();
1349        let mut entry = DatabaseEntry::new();
1350        binding.object_to_entry(&1234i64, &mut entry).unwrap();
1351        // Our signed variable-length format: 1234 - 119 = 1115, fits in 2 bytes → 3 bytes
1352        assert_eq!(
1353            entry.data().len(),
1354            3,
1355            "1234L should encode to 3 bytes (header + 2 value bytes)"
1356        );
1357        let got = binding.entry_to_object(&entry).unwrap();
1358        assert_eq!(got, 1234i64);
1359    }
1360
1361    /// TupleBindingTest: SortedPackedIntBinding 1234 encodes correctly.
1362    ///
1363    /// produces 5 bytes for 1234 in SortedPackedIntegerBinding. Our Rust implementation
1364    /// encodes 1234 in the sorted packed format: it is above the 1-byte threshold (120),
1365    /// so it uses a 2-byte encoding (1 header + 1 value byte for values up to 0xFF+121=376),
1366    /// except 1234 = 0xFF + 122 + remainder, needing 3 bytes (header + 2 value bytes).
1367    #[test]
1368    fn test_sorted_packed_int_binding_1234_size() {
1369        let binding = SortedPackedIntBinding::new();
1370        let mut entry = DatabaseEntry::new();
1371        binding.object_to_entry(&1234i32, &mut entry).unwrap();
1372        // 1234 > 0xFF + 121 = 376, so needs 3 bytes in sorted packed format
1373        assert_eq!(
1374            entry.data().len(),
1375            3,
1376            "1234 should encode to 3 bytes in SortedPackedIntBinding"
1377        );
1378        let got = binding.entry_to_object(&entry).unwrap();
1379        assert_eq!(got, 1234i32);
1380    }
1381
1382    /// TupleBindingTest: SortedPackedLongBinding 1234 encodes correctly.
1383    #[test]
1384    fn test_sorted_packed_long_binding_1234_size() {
1385        let binding = SortedPackedLongBinding::new();
1386        let mut entry = DatabaseEntry::new();
1387        binding.object_to_entry(&1234i64, &mut entry).unwrap();
1388        // 1234 > 0xFF + 121 = 376, so needs 3 bytes in sorted packed format
1389        assert_eq!(
1390            entry.data().len(),
1391            3,
1392            "1234L should encode to 3 bytes in SortedPackedLongBinding"
1393        );
1394        let got = binding.entry_to_object(&entry).unwrap();
1395        assert_eq!(got, 1234i64);
1396    }
1397
1398    // -----------------------------------------------------------------------
1399    // Ported from SerialBindingTest: primitive type round-trips via EntryBinding
1400    // -----------------------------------------------------------------------
1401
1402    /// SerialBindingTest.testPrimitiveBindings: all primitive types via their bindings.
1403    #[test]
1404    fn test_all_primitive_bindings_round_trip() {
1405        // bool
1406        {
1407            let b = BoolBinding::new();
1408            let mut e = DatabaseEntry::new();
1409            b.object_to_entry(&true, &mut e).unwrap();
1410            assert!(b.entry_to_object(&e).unwrap());
1411        }
1412        // u8 (byte)
1413        {
1414            let b = ByteBinding::new();
1415            let mut e = DatabaseEntry::new();
1416            b.object_to_entry(&123u8, &mut e).unwrap();
1417            assert_eq!(b.entry_to_object(&e).unwrap(), 123u8);
1418        }
1419        // i16 (short)
1420        {
1421            let b = ShortBinding::new();
1422            let mut e = DatabaseEntry::new();
1423            b.object_to_entry(&123i16, &mut e).unwrap();
1424            assert_eq!(b.entry_to_object(&e).unwrap(), 123i16);
1425        }
1426        // i32 (int)
1427        {
1428            let b = IntBinding::new();
1429            let mut e = DatabaseEntry::new();
1430            b.object_to_entry(&123i32, &mut e).unwrap();
1431            assert_eq!(b.entry_to_object(&e).unwrap(), 123i32);
1432        }
1433        // i64 (long)
1434        {
1435            let b = LongBinding::new();
1436            let mut e = DatabaseEntry::new();
1437            b.object_to_entry(&123i64, &mut e).unwrap();
1438            assert_eq!(b.entry_to_object(&e).unwrap(), 123i64);
1439        }
1440        // f32 (float)
1441        {
1442            let b = FloatBinding::new();
1443            let mut e = DatabaseEntry::new();
1444            b.object_to_entry(&123.123f32, &mut e).unwrap();
1445            let got = b.entry_to_object(&e).unwrap();
1446            assert!((got - 123.123f32).abs() < 1e-3);
1447        }
1448        // f64 (double)
1449        {
1450            let b = DoubleBinding::new();
1451            let mut e = DatabaseEntry::new();
1452            b.object_to_entry(&123.123f64, &mut e).unwrap();
1453            let got = b.entry_to_object(&e).unwrap();
1454            assert!((got - 123.123f64).abs() < 1e-9);
1455        }
1456        // u16 (char)
1457        {
1458            let b = CharBinding::new();
1459            let mut e = DatabaseEntry::new();
1460            b.object_to_entry(&(b'a' as u16), &mut e).unwrap();
1461            assert_eq!(b.entry_to_object(&e).unwrap(), b'a' as u16);
1462        }
1463        // String
1464        {
1465            let b = StringBinding::new();
1466            let mut e = DatabaseEntry::new();
1467            b.object_to_entry(&"abc".to_string(), &mut e).unwrap();
1468            assert_eq!(b.entry_to_object(&e).unwrap(), "abc".to_string());
1469        }
1470    }
1471
1472    // -----------------------------------------------------------------------
1473    // Ported from TupleOrderingTest: string ordering with sorted sequence
1474    // -----------------------------------------------------------------------
1475
1476    /// TupleOrderingTest.testString: a sorted sequence of strings sorts correctly.
1477    #[test]
1478    fn test_string_ordering_sorted_sequence() {
1479        let binding = StringBinding::new();
1480        // Sequence taken from TupleOrderingTest DATA array (subset — no embedded
1481        // chars beyond ASCII here since our format is UTF-8, not Java modified UTF-8).
1482        let data = vec![
1483            "".to_string(),
1484            "\u{0001}".to_string(),
1485            "\u{0002}".to_string(),
1486            "A".to_string(),
1487            "a".to_string(),
1488            "ab".to_string(),
1489            "b".to_string(),
1490            "bb".to_string(),
1491            "bba".to_string(),
1492            "c".to_string(),
1493        ];
1494        let encoded: Vec<Vec<u8>> =
1495            data.iter().map(|v| encoded_bytes(&binding, v)).collect();
1496        for i in 0..encoded.len() - 1 {
1497            assert!(
1498                encoded[i] < encoded[i + 1],
1499                "string ordering violated: {:?} should sort before {:?}",
1500                data[i],
1501                data[i + 1]
1502            );
1503        }
1504    }
1505
1506    /// TupleOrderingTest.testFixedString / testBytes: raw bytes ordering.
1507    #[test]
1508    fn test_u8_binding_ordering() {
1509        // Raw u8 values sort in ascending numeric order.
1510        let binding = ByteBinding::new();
1511        let values: Vec<u8> = vec![0, 1, 0x7F, 0xFF];
1512        let encoded: Vec<Vec<u8>> =
1513            values.iter().map(|v| encoded_bytes(&binding, v)).collect();
1514        for i in 0..encoded.len() - 1 {
1515            assert!(
1516                encoded[i] < encoded[i + 1],
1517                "u8 binding ordering violated: {} should sort before {}",
1518                values[i],
1519                values[i + 1]
1520            );
1521        }
1522    }
1523}