Skip to main content

noxu_bind/tuple/
tuple_output.rs

1//! TupleOutput: writes primitive types to a growable buffer using sortable encodings.
2//!
3
4use noxu_db::DatabaseEntry;
5use noxu_util::packed::{write_sorted_i32, write_sorted_i64};
6
7/// A writer for tuple-encoded byte data.
8///
9/// Writes primitive values to a growable byte buffer using the same encoding formats
10/// as `TupleOutput`. Signed integers use big-endian with the sign bit
11/// flipped for sortable ordering. Floats use IEEE 754 with bit manipulation
12/// for sortable ordering.
13///
14///
15#[derive(Debug, Clone)]
16pub struct TupleOutput {
17    buf: Vec<u8>,
18}
19
20impl TupleOutput {
21    /// Creates a new empty `TupleOutput`.
22    pub fn new() -> Self {
23        Self { buf: Vec::with_capacity(32) }
24    }
25
26    /// Creates a new `TupleOutput` with the given initial capacity.
27    pub fn with_capacity(capacity: usize) -> Self {
28        Self { buf: Vec::with_capacity(capacity) }
29    }
30
31    /// Returns the written bytes as a vector.
32    pub fn to_vec(&self) -> Vec<u8> {
33        self.buf.clone()
34    }
35
36    /// Consumes self and returns the written bytes.
37    pub fn into_vec(self) -> Vec<u8> {
38        self.buf
39    }
40
41    /// Returns the current length of written data.
42    pub fn len(&self) -> usize {
43        self.buf.len()
44    }
45
46    /// Returns true if no data has been written.
47    pub fn is_empty(&self) -> bool {
48        self.buf.is_empty()
49    }
50
51    /// Converts the written data to a `DatabaseEntry`.
52    pub fn to_database_entry(&self) -> DatabaseEntry {
53        DatabaseEntry::from_vec(self.buf.clone())
54    }
55
56    /// Returns a reference to the internal buffer.
57    pub fn as_bytes(&self) -> &[u8] {
58        &self.buf
59    }
60
61    /// Writes a boolean (one byte) value. True is stored as 1, false as 0.
62    ///
63    /// Values can be read using `TupleInput::read_bool`.
64    pub fn write_bool(&mut self, val: bool) {
65        self.buf.push(if val { 1 } else { 0 });
66    }
67
68    /// Writes an unsigned byte value directly.
69    ///
70    /// Values can be read using `TupleInput::read_u8`.
71    pub fn write_u8(&mut self, val: u8) {
72        self.buf.push(val);
73    }
74
75    /// Writes a signed byte value with sign bit flipped for sort order.
76    ///
77    /// Values can be read using `TupleInput::read_i8`.
78    pub fn write_i8(&mut self, val: i8) {
79        self.buf.push((val as u8) ^ 0x80);
80    }
81
82    /// Writes an unsigned short (two byte, big-endian) value.
83    ///
84    /// Values can be read using `TupleInput::read_u16`.
85    pub fn write_u16(&mut self, val: u16) {
86        self.buf.push((val >> 8) as u8);
87        self.buf.push(val as u8);
88    }
89
90    /// Writes a signed short (two byte) value with sign bit flipped for sort order.
91    ///
92    /// Values can be read using `TupleInput::read_i16`.
93    pub fn write_i16(&mut self, val: i16) {
94        let encoded = (val as u16) ^ 0x8000;
95        self.buf.push((encoded >> 8) as u8);
96        self.buf.push(encoded as u8);
97    }
98
99    /// Writes an unsigned int (four byte, big-endian) value.
100    ///
101    /// Values can be read using `TupleInput::read_u32`.
102    pub fn write_u32(&mut self, val: u32) {
103        self.buf.push((val >> 24) as u8);
104        self.buf.push((val >> 16) as u8);
105        self.buf.push((val >> 8) as u8);
106        self.buf.push(val as u8);
107    }
108
109    /// Writes a signed int (four byte) value with sign bit flipped for sort order.
110    ///
111    /// Values can be read using `TupleInput::read_i32`.
112    pub fn write_i32(&mut self, val: i32) {
113        let encoded = (val as u32) ^ 0x80000000;
114        self.buf.push((encoded >> 24) as u8);
115        self.buf.push((encoded >> 16) as u8);
116        self.buf.push((encoded >> 8) as u8);
117        self.buf.push(encoded as u8);
118    }
119
120    /// Writes an unsigned long (eight byte, big-endian) value.
121    ///
122    /// Values can be read using `TupleInput::read_u64`.
123    pub fn write_u64(&mut self, val: u64) {
124        self.buf.push((val >> 56) as u8);
125        self.buf.push((val >> 48) as u8);
126        self.buf.push((val >> 40) as u8);
127        self.buf.push((val >> 32) as u8);
128        self.buf.push((val >> 24) as u8);
129        self.buf.push((val >> 16) as u8);
130        self.buf.push((val >> 8) as u8);
131        self.buf.push(val as u8);
132    }
133
134    /// Writes a signed long (eight byte) value with sign bit flipped for sort order.
135    ///
136    /// Values can be read using `TupleInput::read_i64`.
137    pub fn write_i64(&mut self, val: i64) {
138        let encoded = (val as u64) ^ 0x8000000000000000;
139        self.buf.push((encoded >> 56) as u8);
140        self.buf.push((encoded >> 48) as u8);
141        self.buf.push((encoded >> 40) as u8);
142        self.buf.push((encoded >> 32) as u8);
143        self.buf.push((encoded >> 24) as u8);
144        self.buf.push((encoded >> 16) as u8);
145        self.buf.push((encoded >> 8) as u8);
146        self.buf.push(encoded as u8);
147    }
148
149    /// Writes an unsorted float (four byte) value as raw IEEE 754 big-endian bits.
150    ///
151    /// This does NOT produce sortable byte ordering. Use `write_sorted_float`
152    /// for keys that need to sort correctly.
153    ///
154    /// Values can be read using `TupleInput::read_float`.
155    pub fn write_float(&mut self, val: f32) {
156        let bits = val.to_bits();
157        self.write_u32(bits);
158    }
159
160    /// Writes an unsorted double (eight byte) value as raw IEEE 754 big-endian bits.
161    ///
162    /// This does NOT produce sortable byte ordering. Use `write_sorted_double`
163    /// for keys that need to sort correctly.
164    ///
165    /// Values can be read using `TupleInput::read_double`.
166    pub fn write_double(&mut self, val: f64) {
167        let bits = val.to_bits();
168        self.write_u64(bits);
169    }
170
171    /// Writes a sorted float (four byte) value using sign-bit manipulation.
172    ///
173    /// The encoding ensures that the byte representation sorts in the same
174    /// order as the float values:
175    /// - If negative (sign bit set): XOR all bits
176    /// - If positive (sign bit clear): XOR only the sign bit
177    ///
178    /// Values can be read using `TupleInput::read_sorted_float`.
179    pub fn write_sorted_float(&mut self, val: f32) {
180        let bits = val.to_bits() as i32;
181        let encoded = if bits < 0 {
182            (bits as u32) ^ 0xFFFFFFFF
183        } else {
184            (bits as u32) ^ 0x80000000
185        };
186        self.write_u32(encoded);
187    }
188
189    /// Writes a sorted double (eight byte) value using sign-bit manipulation.
190    ///
191    /// The encoding ensures that the byte representation sorts in the same
192    /// order as the double values:
193    /// - If negative (sign bit set): XOR all bits
194    /// - If positive (sign bit clear): XOR only the sign bit
195    ///
196    /// Values can be read using `TupleInput::read_sorted_double`.
197    pub fn write_sorted_double(&mut self, val: f64) {
198        let bits = val.to_bits() as i64;
199        let encoded = if bits < 0 {
200            (bits as u64) ^ 0xFFFFFFFFFFFFFFFF
201        } else {
202            (bits as u64) ^ 0x8000000000000000
203        };
204        self.write_u64(encoded);
205    }
206
207    /// Writes a packed (variable-length) i32 value.
208    ///
209    /// Values in [-119, 119] are stored in a single byte. Larger values use
210    /// 2-5 bytes with the first byte encoding the sign and byte count.
211    ///
212    /// This is an unsorted encoding  -  it is compact but the byte representation
213    /// does NOT sort in the same order as the integer values.
214    ///
215    ///
216    ///
217    /// Values can be read using `TupleInput::read_packed_int`.
218    pub fn write_packed_int(&mut self, value: i32) {
219        if (-119..=119).contains(&value) {
220            self.buf.push(value as u8);
221            return;
222        }
223
224        let negative = value < -119;
225        // Use i64 intermediate to avoid overflow when value == i32::MIN
226        let adjusted: u32 = if negative {
227            (-(value as i64) - 119) as u32
228        } else {
229            (value - 119) as u32
230        };
231
232        // Determine byte length needed
233        let byte_len: u32 = if adjusted & 0xFFFFFF00 == 0 {
234            1
235        } else if adjusted & 0xFFFF0000 == 0 {
236            2
237        } else if adjusted & 0xFF000000 == 0 {
238            3
239        } else {
240            4
241        };
242
243        // Write header byte
244        let header: u8 = if negative {
245            (-(119i16 + byte_len as i16)) as u8
246        } else {
247            (119 + byte_len) as u8
248        };
249        self.buf.push(header);
250
251        // Write value bytes in little-endian order (correct)
252        self.buf.push(adjusted as u8);
253        if byte_len > 1 {
254            self.buf.push((adjusted >> 8) as u8);
255            if byte_len > 2 {
256                self.buf.push((adjusted >> 16) as u8);
257                if byte_len > 3 {
258                    self.buf.push((adjusted >> 24) as u8);
259                }
260            }
261        }
262    }
263
264    /// Writes a packed (variable-length) i64 value.
265    ///
266    /// Values in [-119, 119] are stored in a single byte. Larger values use
267    /// 2-9 bytes with the first byte encoding the sign and byte count.
268    ///
269    /// This is an unsorted encoding  -  it is compact but the byte representation
270    /// does NOT sort in the same order as the integer values.
271    ///
272    ///
273    ///
274    /// Values can be read using `TupleInput::read_packed_long`.
275    pub fn write_packed_long(&mut self, value: i64) {
276        if (-119..=119).contains(&value) {
277            self.buf.push(value as u8);
278            return;
279        }
280
281        let negative = value < -119;
282        // Use i128 intermediate to avoid overflow when value == i64::MIN
283        let adjusted: u64 = if negative {
284            (-(value as i128) - 119) as u64
285        } else {
286            (value - 119) as u64
287        };
288
289        // Determine byte length needed
290        let byte_len = if adjusted & 0xFFFFFFFFFFFFFF00 == 0 {
291            1
292        } else if adjusted & 0xFFFFFFFFFFFF0000 == 0 {
293            2
294        } else if adjusted & 0xFFFFFFFFFF000000 == 0 {
295            3
296        } else if adjusted & 0xFFFFFFFF00000000 == 0 {
297            4
298        } else if adjusted & 0xFFFFFF0000000000 == 0 {
299            5
300        } else if adjusted & 0xFFFF000000000000 == 0 {
301            6
302        } else if adjusted & 0xFF00000000000000 == 0 {
303            7
304        } else {
305            8
306        };
307
308        // Write header byte
309        let header: u8 = if negative {
310            (-(119i16 + byte_len as i16)) as u8
311        } else {
312            (119 + byte_len) as u8
313        };
314        self.buf.push(header);
315
316        // Write value bytes in little-endian order (correct)
317        self.buf.push(adjusted as u8);
318        if byte_len > 1 {
319            self.buf.push((adjusted >> 8) as u8);
320            if byte_len > 2 {
321                self.buf.push((adjusted >> 16) as u8);
322                if byte_len > 3 {
323                    self.buf.push((adjusted >> 24) as u8);
324                    if byte_len > 4 {
325                        self.buf.push((adjusted >> 32) as u8);
326                        if byte_len > 5 {
327                            self.buf.push((adjusted >> 40) as u8);
328                            if byte_len > 6 {
329                                self.buf.push((adjusted >> 48) as u8);
330                                if byte_len > 7 {
331                                    self.buf.push((adjusted >> 56) as u8);
332                                }
333                            }
334                        }
335                    }
336                }
337            }
338        }
339    }
340
341    /// Writes a null-escaped UTF-8 string using null-byte escape format.
342    ///
343    /// Each 0x00 byte in the string is escaped as the two-byte sequence
344    /// [0x00, 0x01], and the string is terminated with [0x00, 0x00].
345    /// This allows strings containing embedded null bytes to round-trip
346    /// correctly and preserves lexicographic sort order.
347    ///
348    /// Values can be read using `TupleInput::read_string`.
349    pub fn write_string(&mut self, val: &str) {
350        for &b in val.as_bytes() {
351            if b == 0x00 {
352                self.buf.push(0x00);
353                self.buf.push(0x01);
354            } else {
355                self.buf.push(b);
356            }
357        }
358        // Null terminator: two-byte sequence [0x00, 0x00]
359        self.buf.push(0x00);
360        self.buf.push(0x00);
361    }
362
363    /// Writes raw bytes to the buffer without any framing.
364    ///
365    /// The caller must know the length when reading back.
366    ///
367    /// Values can be read using `TupleInput::read_bytes`.
368    pub fn write_bytes(&mut self, data: &[u8]) {
369        self.buf.extend_from_slice(data);
370    }
371
372    /// Writes a sorted packed (variable-length, order-preserving) i32 value.
373    ///
374    /// Values in [-119, 120] are stored in a single byte as `(value + 127)`.
375    /// Larger positive values use 2-5 bytes with first byte `0xF7 + N`.
376    /// Smaller negative values use 2-5 bytes with first byte `0x08 - N`.
377    ///
378    /// The byte representation sorts in the same order as the integer values,
379    /// making this suitable for database keys. This is distinct from
380    /// `write_packed_int`, which is compact but NOT sortable.
381    ///
382    /// / `TupleOutput.writeSortedPackedInt()`.
383    ///
384    /// Values can be read using `TupleInput::read_sorted_packed_int`.
385    pub fn write_sorted_packed_int(&mut self, val: i32) {
386        write_sorted_i32(&mut self.buf, val)
387            .expect("write_sorted_i32 to Vec is infallible");
388    }
389
390    /// Writes a sorted packed (variable-length, order-preserving) i64 value.
391    ///
392    /// Values in [-119, 120] are stored in a single byte as `(value + 127)`.
393    /// Larger positive values use 2-9 bytes with first byte `0xF7 + N`.
394    /// Smaller negative values use 2-9 bytes with first byte `0x08 - N`.
395    ///
396    /// The byte representation sorts in the same order as the integer values,
397    /// making this suitable for database keys. This is distinct from
398    /// `write_packed_long`, which is compact but NOT sortable.
399    ///
400    /// / `TupleOutput.writeSortedPackedLong()`.
401    ///
402    /// Values can be read using `TupleInput::read_sorted_packed_long`.
403    pub fn write_sorted_packed_long(&mut self, val: i64) {
404        write_sorted_i64(&mut self.buf, val)
405            .expect("write_sorted_i64 to Vec is infallible");
406    }
407
408    /// Writes a Java `char` (16-bit Unicode code point) as two big-endian bytes.
409    ///
410    /// The encoding is identical to an unsigned big-endian u16: the high byte
411    /// first, then the low byte. This matches Java's `DataOutputStream.writeChar`.
412    ///
413    ///
414    ///
415    /// Values can be read using `TupleInput::read_char`.
416    pub fn write_char(&mut self, val: u16) {
417        self.buf.push((val >> 8) as u8);
418        self.buf.push(val as u8);
419    }
420
421    /// Resets the output, clearing all written data.
422    pub fn reset(&mut self) {
423        self.buf.clear();
424    }
425}
426
427impl Default for TupleOutput {
428    fn default() -> Self {
429        Self::new()
430    }
431}
432
433#[cfg(test)]
434mod tests {
435    use super::*;
436
437    #[test]
438    fn test_new() {
439        let out = TupleOutput::new();
440        assert!(out.is_empty());
441        assert_eq!(out.len(), 0);
442    }
443
444    #[test]
445    fn test_write_bool() {
446        let mut out = TupleOutput::new();
447        out.write_bool(true);
448        out.write_bool(false);
449        assert_eq!(out.to_vec(), vec![1, 0]);
450    }
451
452    #[test]
453    fn test_write_i32_sort_order() {
454        // Verify that encoded i32 values sort correctly as bytes
455        let values = [i32::MIN, -1000, -1, 0, 1, 1000, i32::MAX];
456        let encoded: Vec<Vec<u8>> = values
457            .iter()
458            .map(|&v| {
459                let mut out = TupleOutput::new();
460                out.write_i32(v);
461                out.to_vec()
462            })
463            .collect();
464
465        for i in 0..encoded.len() - 1 {
466            assert!(
467                encoded[i] < encoded[i + 1],
468                "sort order violated: {} (encoded {:?}) should be < {} (encoded {:?})",
469                values[i],
470                encoded[i],
471                values[i + 1],
472                encoded[i + 1]
473            );
474        }
475    }
476
477    #[test]
478    fn test_write_i64_sort_order() {
479        let values = [i64::MIN, -1000, -1, 0, 1, 1000, i64::MAX];
480        let encoded: Vec<Vec<u8>> = values
481            .iter()
482            .map(|&v| {
483                let mut out = TupleOutput::new();
484                out.write_i64(v);
485                out.to_vec()
486            })
487            .collect();
488
489        for i in 0..encoded.len() - 1 {
490            assert!(
491                encoded[i] < encoded[i + 1],
492                "sort order violated: {} should be < {}",
493                values[i],
494                values[i + 1]
495            );
496        }
497    }
498
499    #[test]
500    fn test_write_sorted_float_sort_order() {
501        let values = [f32::MIN, -1.0, -0.5, 0.0, 0.5, 1.0, f32::MAX];
502        let encoded: Vec<Vec<u8>> = values
503            .iter()
504            .map(|&v| {
505                let mut out = TupleOutput::new();
506                out.write_sorted_float(v);
507                out.to_vec()
508            })
509            .collect();
510
511        for i in 0..encoded.len() - 1 {
512            assert!(
513                encoded[i] < encoded[i + 1],
514                "sort order violated: {} (encoded {:?}) should be < {} (encoded {:?})",
515                values[i],
516                encoded[i],
517                values[i + 1],
518                encoded[i + 1]
519            );
520        }
521    }
522
523    #[test]
524    fn test_write_sorted_double_sort_order() {
525        let values = [f64::MIN, -1.0, -0.5, 0.0, 0.5, 1.0, f64::MAX];
526        let encoded: Vec<Vec<u8>> = values
527            .iter()
528            .map(|&v| {
529                let mut out = TupleOutput::new();
530                out.write_sorted_double(v);
531                out.to_vec()
532            })
533            .collect();
534
535        for i in 0..encoded.len() - 1 {
536            assert!(
537                encoded[i] < encoded[i + 1],
538                "sort order violated: {} should be < {}",
539                values[i],
540                values[i + 1]
541            );
542        }
543    }
544
545    #[test]
546    fn test_to_database_entry() {
547        let mut out = TupleOutput::new();
548        out.write_i32(42);
549        let entry = out.to_database_entry();
550        assert_eq!(entry.data().len(), 4);
551    }
552
553    #[test]
554    fn test_reset() {
555        let mut out = TupleOutput::new();
556        out.write_i32(42);
557        assert_eq!(out.len(), 4);
558        out.reset();
559        assert!(out.is_empty());
560    }
561
562    #[test]
563    fn test_packed_int_single_byte() {
564        // Values -119..=119 should be a single byte
565        for v in -119..=119i32 {
566            let mut out = TupleOutput::new();
567            out.write_packed_int(v);
568            assert_eq!(out.len(), 1, "value {} should be 1 byte", v);
569        }
570    }
571
572    #[test]
573    fn test_packed_int_multi_byte() {
574        let mut out = TupleOutput::new();
575        out.write_packed_int(120);
576        assert!(out.len() > 1);
577
578        let mut out = TupleOutput::new();
579        out.write_packed_int(-120);
580        assert!(out.len() > 1);
581    }
582
583    // -----------------------------------------------------------------------
584    // Ported from TupleFormatTest: exact byte sizes per type
585    // -----------------------------------------------------------------------
586
587    /// TupleFormatTest: bool is 1 byte.
588    #[test]
589    fn test_format_bool_size() {
590        let mut out = TupleOutput::new();
591        out.write_bool(true);
592        assert_eq!(out.len(), 1);
593        out.write_bool(false);
594        assert_eq!(out.len(), 2);
595    }
596
597    /// TupleFormatTest: u8 / i8 are 1 byte.
598    #[test]
599    fn test_format_byte_sizes() {
600        let mut out = TupleOutput::new();
601        out.write_u8(123);
602        assert_eq!(out.len(), 1);
603        let mut out = TupleOutput::new();
604        out.write_i8(-1);
605        assert_eq!(out.len(), 1);
606    }
607
608    /// TupleFormatTest: u16 / i16 are 2 bytes.
609    #[test]
610    fn test_format_short_sizes() {
611        let mut out = TupleOutput::new();
612        out.write_u16(0xFFFF);
613        assert_eq!(out.len(), 2);
614        let mut out = TupleOutput::new();
615        out.write_i16(-1);
616        assert_eq!(out.len(), 2);
617    }
618
619    /// TupleFormatTest: u32 / i32 are 4 bytes.
620    #[test]
621    fn test_format_int_sizes() {
622        let mut out = TupleOutput::new();
623        out.write_u32(0xFFFF_FFFF);
624        assert_eq!(out.len(), 4);
625        let mut out = TupleOutput::new();
626        out.write_i32(123);
627        assert_eq!(out.len(), 4);
628    }
629
630    /// TupleFormatTest: u64 / i64 are 8 bytes.
631    #[test]
632    fn test_format_long_sizes() {
633        let mut out = TupleOutput::new();
634        out.write_u64(123);
635        assert_eq!(out.len(), 8);
636        let mut out = TupleOutput::new();
637        out.write_i64(123);
638        assert_eq!(out.len(), 8);
639    }
640
641    /// TupleFormatTest: f32 is 4 bytes (both sorted and unsorted).
642    #[test]
643    fn test_format_float_sizes() {
644        let mut out = TupleOutput::new();
645        out.write_float(123.123);
646        assert_eq!(out.len(), 4);
647        let mut out = TupleOutput::new();
648        out.write_sorted_float(123.123);
649        assert_eq!(out.len(), 4);
650    }
651
652    /// TupleFormatTest: f64 is 8 bytes (both sorted and unsorted).
653    #[test]
654    fn test_format_double_sizes() {
655        let mut out = TupleOutput::new();
656        out.write_double(123.123);
657        assert_eq!(out.len(), 8);
658        let mut out = TupleOutput::new();
659        out.write_sorted_double(123.123);
660        assert_eq!(out.len(), 8);
661    }
662
663    /// TupleFormatTest: char (write_char) is 2 bytes.
664    #[test]
665    fn test_format_char_size() {
666        let mut out = TupleOutput::new();
667        out.write_char(b'a' as u16);
668        assert_eq!(out.len(), 2);
669    }
670
671    /// TupleFormatTest: null-terminated string "abc" is len+1 = 4 bytes.
672    #[test]
673    fn test_format_string_size() {
674        let mut out = TupleOutput::new();
675        out.write_string("abc");
676        // 3 chars + 2-byte null terminator [0x00, 0x00] = 5 bytes
677        // But writes each char as 1 byte then a 1-byte null terminator.
678        // Our format writes UTF-8 bytes + [0x00, 0x00] terminator.
679        // "abc" → 3 bytes + 2 bytes terminator = 5 bytes total.
680        // However TupleFormatTest expects 4 for "abc", because uses
681        // a SINGLE null byte terminator for ASCII-range strings.
682        // Our Rust implementation uses a 2-byte null terminator to allow
683        // embedded nulls, so "abc" → 5 bytes.
684        assert_eq!(out.len(), 5); // "abc" + [0x00, 0x00]
685    }
686
687    /// TupleFormatTest: empty string is just the terminator.
688    #[test]
689    fn test_format_empty_string_size() {
690        let mut out = TupleOutput::new();
691        out.write_string("");
692        assert_eq!(out.len(), 2); // [0x00, 0x00] terminator only
693    }
694
695    /// TupleFormatTest: multiple strings written sequentially accumulate size.
696    #[test]
697    fn test_format_multi_string_size() {
698        let mut out = TupleOutput::new();
699        out.write_string("abc"); // 3 + 2 = 5
700        out.write_string("defg"); // 4 + 2 = 6 → total 11
701        assert_eq!(out.len(), 11);
702    }
703
704    /// TupleFormatTest: three booleans accumulate correctly.
705    #[test]
706    fn test_format_multi_bool_size() {
707        let mut out = TupleOutput::new();
708        out.write_bool(true);
709        out.write_bool(false);
710        out.write_bool(true);
711        assert_eq!(out.len(), 3);
712    }
713
714    /// TupleFormatTest: three i32 values accumulate to 3*4 bytes.
715    #[test]
716    fn test_format_multi_int_size() {
717        let mut out = TupleOutput::new();
718        out.write_i32(0);
719        out.write_i32(1);
720        out.write_i32(-1);
721        assert_eq!(out.len(), 12);
722    }
723
724    /// TupleFormatTest: three i64 values accumulate to 3*8 bytes.
725    #[test]
726    fn test_format_multi_long_size() {
727        let mut out = TupleOutput::new();
728        out.write_i64(0);
729        out.write_i64(1);
730        out.write_i64(-1);
731        assert_eq!(out.len(), 24);
732    }
733
734    /// TupleFormatTest: three f32 values accumulate to 3*4 bytes.
735    #[test]
736    fn test_format_multi_float_size() {
737        let mut out = TupleOutput::new();
738        out.write_float(0.0);
739        out.write_float(1.0);
740        out.write_float(-1.0);
741        assert_eq!(out.len(), 12);
742    }
743
744    /// TupleFormatTest: three f64 values accumulate to 3*8 bytes.
745    #[test]
746    fn test_format_multi_double_size() {
747        let mut out = TupleOutput::new();
748        out.write_double(0.0);
749        out.write_double(1.0);
750        out.write_double(-1.0);
751        assert_eq!(out.len(), 24);
752    }
753
754    // -----------------------------------------------------------------------
755    // Ported from TupleOrderingTest: ordering checks for all types
756    // -----------------------------------------------------------------------
757
758    /// Helper: encode a single value to bytes.
759    fn encode_i8(v: i8) -> Vec<u8> {
760        let mut out = TupleOutput::new();
761        out.write_i8(v);
762        out.to_vec()
763    }
764    fn encode_i16(v: i16) -> Vec<u8> {
765        let mut out = TupleOutput::new();
766        out.write_i16(v);
767        out.to_vec()
768    }
769    fn encode_i32(v: i32) -> Vec<u8> {
770        let mut out = TupleOutput::new();
771        out.write_i32(v);
772        out.to_vec()
773    }
774    fn encode_i64(v: i64) -> Vec<u8> {
775        let mut out = TupleOutput::new();
776        out.write_i64(v);
777        out.to_vec()
778    }
779    fn encode_u8(v: u8) -> Vec<u8> {
780        let mut out = TupleOutput::new();
781        out.write_u8(v);
782        out.to_vec()
783    }
784    fn encode_u16(v: u16) -> Vec<u8> {
785        let mut out = TupleOutput::new();
786        out.write_u16(v);
787        out.to_vec()
788    }
789    fn encode_u32(v: u32) -> Vec<u8> {
790        let mut out = TupleOutput::new();
791        out.write_u32(v);
792        out.to_vec()
793    }
794    fn encode_f32_sorted(v: f32) -> Vec<u8> {
795        let mut out = TupleOutput::new();
796        out.write_sorted_float(v);
797        out.to_vec()
798    }
799    fn encode_f64_sorted(v: f64) -> Vec<u8> {
800        let mut out = TupleOutput::new();
801        out.write_sorted_double(v);
802        out.to_vec()
803    }
804    fn encode_f32(v: f32) -> Vec<u8> {
805        let mut out = TupleOutput::new();
806        out.write_float(v);
807        out.to_vec()
808    }
809    fn encode_f64(v: f64) -> Vec<u8> {
810        let mut out = TupleOutput::new();
811        out.write_double(v);
812        out.to_vec()
813    }
814
815    /// TupleOrderingTest.testByte: signed byte full ordering.
816    #[test]
817    fn test_ordering_i8_full_boundary() {
818        let data: &[i8] =
819            &[i8::MIN, i8::MIN + 1, -1, 0, 1, i8::MAX - 1, i8::MAX];
820        for i in 0..data.len() - 1 {
821            assert!(
822                encode_i8(data[i]) < encode_i8(data[i + 1]),
823                "i8 ordering violated: {} should be < {}",
824                data[i],
825                data[i + 1]
826            );
827        }
828    }
829
830    /// TupleOrderingTest.testShort: signed short full ordering.
831    #[test]
832    fn test_ordering_i16_full_boundary() {
833        let data: &[i16] = &[
834            i16::MIN,
835            i16::MIN + 1,
836            i8::MIN as i16,
837            i8::MIN as i16 + 1,
838            -1,
839            0,
840            1,
841            i8::MAX as i16 - 1,
842            i8::MAX as i16,
843            i16::MAX - 1,
844            i16::MAX,
845        ];
846        for i in 0..data.len() - 1 {
847            assert!(
848                encode_i16(data[i]) < encode_i16(data[i + 1]),
849                "i16 ordering violated: {} should be < {}",
850                data[i],
851                data[i + 1]
852            );
853        }
854    }
855
856    /// TupleOrderingTest.testInt: signed int full ordering.
857    #[test]
858    fn test_ordering_i32_full_boundary() {
859        let data: &[i32] = &[
860            i32::MIN,
861            i32::MIN + 1,
862            i16::MIN as i32,
863            i16::MIN as i32 + 1,
864            i8::MIN as i32,
865            i8::MIN as i32 + 1,
866            -1,
867            0,
868            1,
869            i8::MAX as i32 - 1,
870            i8::MAX as i32,
871            i16::MAX as i32 - 1,
872            i16::MAX as i32,
873            i32::MAX - 1,
874            i32::MAX,
875        ];
876        for i in 0..data.len() - 1 {
877            assert!(
878                encode_i32(data[i]) < encode_i32(data[i + 1]),
879                "i32 ordering violated: {} should be < {}",
880                data[i],
881                data[i + 1]
882            );
883        }
884    }
885
886    /// TupleOrderingTest.testLong: signed long full ordering.
887    #[test]
888    fn test_ordering_i64_full_boundary() {
889        let data: &[i64] = &[
890            i64::MIN,
891            i64::MIN + 1,
892            i32::MIN as i64,
893            i32::MIN as i64 + 1,
894            i16::MIN as i64,
895            i16::MIN as i64 + 1,
896            i8::MIN as i64,
897            i8::MIN as i64 + 1,
898            -1,
899            0,
900            1,
901            i8::MAX as i64 - 1,
902            i8::MAX as i64,
903            i16::MAX as i64 - 1,
904            i16::MAX as i64,
905            i32::MAX as i64 - 1,
906            i32::MAX as i64,
907            i64::MAX - 1,
908            i64::MAX,
909        ];
910        for i in 0..data.len() - 1 {
911            assert!(
912                encode_i64(data[i]) < encode_i64(data[i + 1]),
913                "i64 ordering violated: {} should be < {}",
914                data[i],
915                data[i + 1]
916            );
917        }
918    }
919
920    /// TupleOrderingTest.testUnsignedByte: unsigned byte ordering.
921    #[test]
922    fn test_ordering_u8_full() {
923        let data: &[u8] = &[0, 1, 0x7F, 0xFF];
924        for i in 0..data.len() - 1 {
925            assert!(
926                encode_u8(data[i]) < encode_u8(data[i + 1]),
927                "u8 ordering violated: {} should be < {}",
928                data[i],
929                data[i + 1]
930            );
931        }
932    }
933
934    /// TupleOrderingTest.testUnsignedShort: unsigned short ordering.
935    #[test]
936    fn test_ordering_u16_full() {
937        let data: &[u16] = &[0, 1, 0xFE, 0xFF, 0x800, 0x7FFF, 0xFFFF];
938        for i in 0..data.len() - 1 {
939            assert!(
940                encode_u16(data[i]) < encode_u16(data[i + 1]),
941                "u16 ordering violated: {} should be < {}",
942                data[i],
943                data[i + 1]
944            );
945        }
946    }
947
948    /// TupleOrderingTest.testUnsignedInt: unsigned int ordering.
949    #[test]
950    fn test_ordering_u32_full() {
951        let data: &[u32] = &[
952            0, 1, 0xFE, 0xFF, 0x800, 0x7FFF, 0xFFFF, 0x80000, 0x7FFFFFFF,
953            0x80000000, 0xFFFFFFFF,
954        ];
955        for i in 0..data.len() - 1 {
956            assert!(
957                encode_u32(data[i]) < encode_u32(data[i + 1]),
958                "u32 ordering violated: {} should be < {}",
959                data[i],
960                data[i + 1]
961            );
962        }
963    }
964
965    /// TupleOrderingTest.testBoolean: false < true.
966    #[test]
967    fn test_ordering_bool() {
968        let mut false_out = TupleOutput::new();
969        false_out.write_bool(false);
970        let mut true_out = TupleOutput::new();
971        true_out.write_bool(true);
972        assert!(false_out.to_vec() < true_out.to_vec());
973    }
974
975    /// TupleOrderingTest.testFloat: positive-only float ordering (unsorted write_float).
976    /// notes that ONLY positive floats are ordered deterministically with writeFloat.
977    #[test]
978    fn test_ordering_float_positive_only() {
979        let data: &[f32] = &[
980            0.0,
981            f32::MIN_POSITIVE,
982            2.0 * f32::MIN_POSITIVE,
983            0.01,
984            0.02,
985            0.99,
986            1.0,
987            1.01,
988            1.02,
989            1.99,
990            f32::MAX,
991            f32::INFINITY,
992        ];
993        for i in 0..data.len() - 1 {
994            assert!(
995                encode_f32(data[i]) < encode_f32(data[i + 1]),
996                "positive float ordering violated: {} should be < {}",
997                data[i],
998                data[i + 1]
999            );
1000        }
1001    }
1002
1003    /// TupleOrderingTest.testDouble: positive-only double ordering (unsorted write_double).
1004    #[test]
1005    fn test_ordering_double_positive_only() {
1006        let data: &[f64] = &[
1007            0.0,
1008            f64::MIN_POSITIVE,
1009            2.0 * f64::MIN_POSITIVE,
1010            0.001,
1011            0.002,
1012            0.999,
1013            1.0,
1014            1.001,
1015            1.002,
1016            1.999,
1017            f64::MAX,
1018            f64::INFINITY,
1019        ];
1020        for i in 0..data.len() - 1 {
1021            assert!(
1022                encode_f64(data[i]) < encode_f64(data[i + 1]),
1023                "positive double ordering violated: {} should be < {}",
1024                data[i],
1025                data[i + 1]
1026            );
1027        }
1028    }
1029
1030    /// TupleOrderingTest.testSortedFloat: full sorted float ordering including negatives.
1031    #[test]
1032    fn test_ordering_sorted_float_full() {
1033        let data: &[f32] = &[
1034            f32::NEG_INFINITY,
1035            -f32::MAX,
1036            -1.99,
1037            -1.02,
1038            -1.01,
1039            -1.0,
1040            -0.99,
1041            -0.02,
1042            -0.01,
1043            -2.0 * f32::MIN_POSITIVE,
1044            -f32::MIN_POSITIVE,
1045            0.0,
1046            f32::MIN_POSITIVE,
1047            2.0 * f32::MIN_POSITIVE,
1048            0.01,
1049            0.02,
1050            0.99,
1051            1.0,
1052            1.01,
1053            1.02,
1054            1.99,
1055            f32::MAX,
1056            f32::INFINITY,
1057            f32::NAN,
1058        ];
1059        for i in 0..data.len() - 1 {
1060            assert!(
1061                encode_f32_sorted(data[i]) < encode_f32_sorted(data[i + 1]),
1062                "sorted float ordering violated at index {}: {} should be < {}",
1063                i,
1064                data[i],
1065                data[i + 1]
1066            );
1067        }
1068    }
1069
1070    /// TupleOrderingTest.testSortedDouble: full sorted double ordering including negatives.
1071    #[test]
1072    fn test_ordering_sorted_double_full() {
1073        let data: &[f64] = &[
1074            f64::NEG_INFINITY,
1075            -f64::MAX,
1076            -1.999,
1077            -1.002,
1078            -1.001,
1079            -1.0,
1080            -0.999,
1081            -0.002,
1082            -0.001,
1083            -2.0 * f64::MIN_POSITIVE,
1084            -f64::MIN_POSITIVE,
1085            0.0,
1086            f64::MIN_POSITIVE,
1087            2.0 * f64::MIN_POSITIVE,
1088            0.001,
1089            0.002,
1090            0.999,
1091            1.0,
1092            1.001,
1093            1.002,
1094            1.999,
1095            f64::MAX,
1096            f64::INFINITY,
1097            f64::NAN,
1098        ];
1099        for i in 0..data.len() - 1 {
1100            assert!(
1101                encode_f64_sorted(data[i]) < encode_f64_sorted(data[i + 1]),
1102                "sorted double ordering violated at index {}: {} should be < {}",
1103                i,
1104                data[i],
1105                data[i + 1]
1106            );
1107        }
1108    }
1109
1110    /// TupleOrderingTest.testPackedIntAndLong: packed int 0..=630 are ordered.
1111    #[test]
1112    fn test_ordering_packed_int_0_to_630() {
1113        for i in 0u32..630 {
1114            let mut a = TupleOutput::new();
1115            a.write_packed_int(i as i32);
1116            let mut b = TupleOutput::new();
1117            b.write_packed_int(i as i32 + 1);
1118            assert!(
1119                a.to_vec() < b.to_vec(),
1120                "packed_int ordering violated: {} should be < {}",
1121                i,
1122                i + 1
1123            );
1124        }
1125    }
1126
1127    /// TupleOrderingTest.testPackedIntAndLong: packed long 0..=630 are ordered.
1128    #[test]
1129    fn test_ordering_packed_long_0_to_630() {
1130        for i in 0u64..630 {
1131            let mut a = TupleOutput::new();
1132            a.write_packed_long(i as i64);
1133            let mut b = TupleOutput::new();
1134            b.write_packed_long(i as i64 + 1);
1135            assert!(
1136                a.to_vec() < b.to_vec(),
1137                "packed_long ordering violated: {} should be < {}",
1138                i,
1139                i + 1
1140            );
1141        }
1142    }
1143
1144    /// TupleOrderingTest.testSortedPackedInt: full signed ordering.
1145    #[test]
1146    fn test_ordering_sorted_packed_int_full_boundary() {
1147        let data: &[i32] = &[
1148            i32::MIN,
1149            i32::MIN + 1,
1150            i16::MIN as i32,
1151            i16::MIN as i32 + 1,
1152            i8::MIN as i32,
1153            i8::MIN as i32 + 1,
1154            -1,
1155            0,
1156            1,
1157            i8::MAX as i32 - 1,
1158            i8::MAX as i32,
1159            i16::MAX as i32 - 1,
1160            i16::MAX as i32,
1161            i32::MAX - 1,
1162            i32::MAX,
1163        ];
1164        for i in 0..data.len() - 1 {
1165            let mut a = TupleOutput::new();
1166            a.write_sorted_packed_int(data[i]);
1167            let mut b = TupleOutput::new();
1168            b.write_sorted_packed_int(data[i + 1]);
1169            assert!(
1170                a.to_vec() < b.to_vec(),
1171                "sorted_packed_int ordering violated: {} should be < {}",
1172                data[i],
1173                data[i + 1]
1174            );
1175        }
1176    }
1177
1178    /// TupleOrderingTest.testSortedPackedLong: full signed ordering.
1179    #[test]
1180    fn test_ordering_sorted_packed_long_full_boundary() {
1181        let data: &[i64] = &[
1182            i64::MIN,
1183            i64::MIN + 1,
1184            i32::MIN as i64,
1185            i32::MIN as i64 + 1,
1186            i16::MIN as i64,
1187            i16::MIN as i64 + 1,
1188            i8::MIN as i64,
1189            i8::MIN as i64 + 1,
1190            -1,
1191            0,
1192            1,
1193            i8::MAX as i64 - 1,
1194            i8::MAX as i64,
1195            i16::MAX as i64 - 1,
1196            i16::MAX as i64,
1197            i32::MAX as i64 - 1,
1198            i32::MAX as i64,
1199            i64::MAX - 1,
1200            i64::MAX,
1201        ];
1202        for i in 0..data.len() - 1 {
1203            let mut a = TupleOutput::new();
1204            a.write_sorted_packed_long(data[i]);
1205            let mut b = TupleOutput::new();
1206            b.write_sorted_packed_long(data[i + 1]);
1207            assert!(
1208                a.to_vec() < b.to_vec(),
1209                "sorted_packed_long ordering violated: {} should be < {}",
1210                data[i],
1211                data[i + 1]
1212            );
1213        }
1214    }
1215
1216    /// TupleFormatTest: packed int specific sizes.
1217    #[test]
1218    fn test_format_packed_int_sizes() {
1219        // 119 fits in 1 byte
1220        let mut out = TupleOutput::new();
1221        out.write_packed_int(119);
1222        assert_eq!(out.len(), 1, "119 should be 1 byte");
1223
1224        // 0xFFFF + 119 should be 3 bytes (header + 2 value bytes)
1225        let mut out = TupleOutput::new();
1226        out.write_packed_int(0xFFFF + 119);
1227        assert_eq!(out.len(), 3, "0xFFFF+119 should be 3 bytes");
1228
1229        // i32::MAX should be 5 bytes (header + 4 value bytes)
1230        let mut out = TupleOutput::new();
1231        out.write_packed_int(i32::MAX);
1232        assert_eq!(out.len(), 5, "i32::MAX should be 5 bytes");
1233    }
1234
1235    /// TupleFormatTest: packed long specific sizes.
1236    #[test]
1237    fn test_format_packed_long_sizes() {
1238        // 119 fits in 1 byte
1239        let mut out = TupleOutput::new();
1240        out.write_packed_long(119);
1241        assert_eq!(out.len(), 1, "119 should be 1 byte");
1242
1243        // 0xFFFF_FFFF + 119 should be 5 bytes
1244        let mut out = TupleOutput::new();
1245        out.write_packed_long(0xFFFF_FFFF_i64 + 119);
1246        assert_eq!(out.len(), 5, "0xFFFFFFFF+119 should be 5 bytes");
1247
1248        // i64::MAX should be 9 bytes
1249        let mut out = TupleOutput::new();
1250        out.write_packed_long(i64::MAX);
1251        assert_eq!(out.len(), 9, "i64::MAX should be 9 bytes");
1252    }
1253
1254    /// TupleFormatTest: sorted packed int specific sizes.
1255    #[test]
1256    fn test_format_sorted_packed_int_sizes() {
1257        // -1, 0, 1 fit in 1 byte
1258        for v in [-1i32, 0, 1, -119, 120] {
1259            let mut out = TupleOutput::new();
1260            out.write_sorted_packed_int(v);
1261            assert_eq!(out.len(), 1, "{} should be 1 byte", v);
1262        }
1263        // 121 needs 2 bytes; -120 needs 2 bytes
1264        for v in [121i32, -120] {
1265            let mut out = TupleOutput::new();
1266            out.write_sorted_packed_int(v);
1267            assert_eq!(out.len(), 2, "{} should be 2 bytes", v);
1268        }
1269        // i32::MAX / i32::MIN need 5 bytes
1270        for v in [i32::MAX, i32::MIN] {
1271            let mut out = TupleOutput::new();
1272            out.write_sorted_packed_int(v);
1273            assert_eq!(out.len(), 5, "{} should be 5 bytes", v);
1274        }
1275    }
1276
1277    /// TupleFormatTest: sorted packed long specific sizes.
1278    #[test]
1279    fn test_format_sorted_packed_long_sizes() {
1280        for v in [-1i64, 0, 1, -119, 120] {
1281            let mut out = TupleOutput::new();
1282            out.write_sorted_packed_long(v);
1283            assert_eq!(out.len(), 1, "{} should be 1 byte", v);
1284        }
1285        for v in [121i64, -120] {
1286            let mut out = TupleOutput::new();
1287            out.write_sorted_packed_long(v);
1288            assert_eq!(out.len(), 2, "{} should be 2 bytes", v);
1289        }
1290        for v in [i64::MAX, i64::MIN] {
1291            let mut out = TupleOutput::new();
1292            out.write_sorted_packed_long(v);
1293            assert_eq!(out.len(), 9, "{} should be 9 bytes", v);
1294        }
1295    }
1296
1297    /// TupleFormatTest: string ordering test — multi-segment tuples.
1298    /// Ported from TupleOrderingTest.testString.
1299    #[test]
1300    fn test_ordering_string_multi_segment() {
1301        // Encode "a" then "a"+"" then "a"+""+"a" — each should be strictly greater
1302        fn encode_strings(strs: &[&str]) -> Vec<u8> {
1303            let mut out = TupleOutput::new();
1304            for s in strs {
1305                out.write_string(s);
1306            }
1307            out.to_vec()
1308        }
1309        let a = encode_strings(&["a"]);
1310        let a_empty = encode_strings(&["a", ""]);
1311        let a_empty_a = encode_strings(&["a", "", "a"]);
1312        let a_b = encode_strings(&["a", "b"]);
1313        let aa = encode_strings(&["aa"]);
1314        let b = encode_strings(&["b"]);
1315
1316        assert!(a < a_empty, "\"a\" should sort before \"a\"+\"\"");
1317        assert!(
1318            a_empty < a_empty_a,
1319            "\"a\"+\"\" should sort before \"a\"+\"\"+\"a\""
1320        );
1321        assert!(
1322            a_empty_a < a_b,
1323            "\"a\"+\"\"+\"a\" should sort before \"a\"+\"b\""
1324        );
1325        assert!(a_b < aa, "\"a\"+\"b\" should sort before \"aa\"");
1326        assert!(aa < b, "\"aa\" should sort before \"b\"");
1327    }
1328}