lnmp_codec/binary/
entry.rs

1//! Binary entry structure for LNMP v0.4 protocol.
2//!
3//! A BinaryEntry represents a single field in binary format, consisting of:
4//! - FID (Field Identifier): 2 bytes, little-endian
5//! - TAG (Type Tag): 1 byte
6//! - VALUE: Variable length, encoding depends on type
7
8use super::error::BinaryError;
9use super::types::{BinaryValue, TypeTag};
10use super::varint;
11use lnmp_core::{FieldId, LnmpField};
12use lnmp_embedding::{Decoder as EmbeddingDecoder, Encoder as EmbeddingEncoder};
13
14/// A single field encoded in binary format
15#[derive(Debug, Clone, PartialEq)]
16pub struct BinaryEntry {
17    /// Field identifier (16-bit unsigned integer)
18    pub fid: FieldId,
19    /// Type tag indicating value type
20    pub tag: TypeTag,
21    /// The encoded value
22    pub value: BinaryValue,
23}
24
25impl BinaryEntry {
26    /// Creates a new binary entry
27    pub fn new(fid: FieldId, value: BinaryValue) -> Self {
28        Self {
29            fid,
30            tag: value.type_tag(),
31            value,
32        }
33    }
34
35    /// Creates a new binary entry from an LnmpField
36    ///
37    /// # Errors
38    ///
39    /// Returns `BinaryError::InvalidValue` if the field contains nested structures
40    /// (not supported in v0.4 binary format)
41    pub fn from_field(field: &LnmpField) -> Result<Self, BinaryError> {
42        let value = BinaryValue::from_lnmp_value(&field.value).map_err(|e| match e {
43            BinaryError::InvalidValue {
44                type_tag, reason, ..
45            } => BinaryError::InvalidValue {
46                field_id: field.fid,
47                type_tag,
48                reason,
49            },
50            other => other,
51        })?;
52
53        Ok(Self {
54            fid: field.fid,
55            tag: value.type_tag(),
56            value,
57        })
58    }
59
60    /// Converts to an LnmpField
61    pub fn to_field(&self) -> LnmpField {
62        LnmpField {
63            fid: self.fid,
64            value: self.value.to_lnmp_value(),
65        }
66    }
67
68    /// Returns the type tag of this entry
69    pub fn type_tag(&self) -> TypeTag {
70        self.tag
71    }
72
73    /// Encodes the entry to bytes
74    ///
75    /// Binary layout:
76    /// ```text
77    /// ┌──────────┬──────────┬──────────────────┐
78    /// │   FID    │  THTAG   │      VALUE       │
79    /// │ (2 bytes)│ (1 byte) │   (variable)     │
80    /// └──────────┴──────────┴──────────────────┘
81    /// ```
82    pub fn encode(&self) -> Vec<u8> {
83        let mut bytes = Vec::new();
84
85        // Write FID (2 bytes, little-endian)
86        bytes.extend_from_slice(&self.fid.to_le_bytes());
87
88        // Write TAG (1 byte)
89        bytes.push(self.tag.to_u8());
90
91        // Write VALUE (encoding depends on type)
92        match &self.value {
93            BinaryValue::Int(i) => {
94                // VarInt encoding
95                bytes.extend_from_slice(&varint::encode(*i));
96            }
97            BinaryValue::Float(f) => {
98                // 8 bytes IEEE754 little-endian
99                bytes.extend_from_slice(&f.to_le_bytes());
100            }
101            BinaryValue::Bool(b) => {
102                // 1 byte: 0x00 for false, 0x01 for true
103                bytes.push(if *b { 0x01 } else { 0x00 });
104            }
105            BinaryValue::String(s) => {
106                // Length (VarInt) + UTF-8 bytes
107                let utf8_bytes = s.as_bytes();
108                bytes.extend_from_slice(&varint::encode(utf8_bytes.len() as i64));
109                bytes.extend_from_slice(utf8_bytes);
110            }
111            BinaryValue::StringArray(arr) => {
112                // Count (VarInt) + repeated (length + UTF-8 bytes)
113                bytes.extend_from_slice(&varint::encode(arr.len() as i64));
114                for s in arr {
115                    let utf8_bytes = s.as_bytes();
116                    bytes.extend_from_slice(&varint::encode(utf8_bytes.len() as i64));
117                    bytes.extend_from_slice(utf8_bytes);
118                }
119            }
120            BinaryValue::NestedRecord(_) | BinaryValue::NestedArray(_) => {
121                // Nested structure encoding will be implemented in task 2
122                // For now, this is a placeholder that should not be reached
123                // as nested encoding requires special handling
124                panic!("Nested structure encoding not yet implemented - use BinaryNestedEncoder");
125            }
126            BinaryValue::Embedding(vec) => {
127                // Encode using lnmp-embedding crate
128                // We wrap the result in a Result, but encode() signature returns Vec<u8>
129                // For now, we'll panic on error as this is an in-memory operation that shouldn't fail
130                // unless OOM. Ideally signature should return Result.
131                let encoded = EmbeddingEncoder::encode(vec).expect("Failed to encode embedding");
132                // Write length (VarInt) + encoded data
133                bytes.extend_from_slice(&varint::encode(encoded.len() as i64));
134                bytes.extend_from_slice(&encoded);
135            }
136            BinaryValue::QuantizedEmbedding(qv) => {
137                // Encode quantized embedding: scheme + scale + zero_point + min_val + dim + data
138                bytes.push(qv.scheme as u8);
139                bytes.extend_from_slice(&qv.scale.to_le_bytes());
140                bytes.push(qv.zero_point as u8);
141                bytes.extend_from_slice(&qv.min_val.to_le_bytes());
142                bytes.extend_from_slice(&qv.dim.to_le_bytes());
143                bytes.extend_from_slice(&varint::encode(qv.data.len() as i64));
144                bytes.extend_from_slice(&qv.data);
145            }
146        }
147
148        bytes
149    }
150
151    /// Decodes an entry from bytes
152    ///
153    /// Returns a tuple of (BinaryEntry, bytes_consumed)
154    ///
155    /// # Errors
156    ///
157    /// Returns errors for:
158    /// - `UnexpectedEof`: Insufficient data
159    /// - `InvalidTypeTag`: Unknown type tag
160    /// - `InvalidVarInt`: Malformed VarInt
161    /// - `InvalidUtf8`: Invalid UTF-8 in string
162    /// - `InvalidValue`: Other value decoding errors
163    pub fn decode(bytes: &[u8]) -> Result<(Self, usize), BinaryError> {
164        let mut offset = 0;
165
166        // Read FID (2 bytes, little-endian)
167        if bytes.len() < 2 {
168            return Err(BinaryError::UnexpectedEof {
169                expected: 2,
170                found: bytes.len(),
171            });
172        }
173        let fid = u16::from_le_bytes([bytes[0], bytes[1]]);
174        offset += 2;
175
176        // Read TAG (1 byte)
177        if bytes.len() < offset + 1 {
178            return Err(BinaryError::UnexpectedEof {
179                expected: offset + 1,
180                found: bytes.len(),
181            });
182        }
183        let tag = TypeTag::from_u8(bytes[offset])?;
184        offset += 1;
185
186        // Read VALUE (depends on type)
187        let value = match tag {
188            TypeTag::NestedRecord | TypeTag::NestedArray => {
189                // Nested structure decoding will be implemented in task 3
190                return Err(BinaryError::InvalidValue {
191                    field_id: fid,
192                    type_tag: tag.to_u8(),
193                    reason:
194                        "Nested structure decoding not yet implemented - use BinaryNestedDecoder"
195                            .to_string(),
196                });
197            }
198            TypeTag::Reserved09
199            | TypeTag::QuantizedEmbedding
200            | TypeTag::Reserved0B
201            | TypeTag::Reserved0C
202            | TypeTag::Reserved0D
203            | TypeTag::Reserved0E
204            | TypeTag::Reserved0F => {
205                return Err(BinaryError::InvalidValue {
206                    field_id: fid,
207                    type_tag: tag.to_u8(),
208                    reason: format!("Reserved type tag 0x{:02X} cannot be used", tag.to_u8()),
209                });
210            }
211            TypeTag::Int => {
212                let (int_val, consumed) =
213                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
214                        field_id: fid,
215                        type_tag: tag.to_u8(),
216                        reason: "Invalid VarInt encoding".to_string(),
217                    })?;
218                offset += consumed;
219                BinaryValue::Int(int_val)
220            }
221            TypeTag::Float => {
222                if bytes.len() < offset + 8 {
223                    return Err(BinaryError::UnexpectedEof {
224                        expected: offset + 8,
225                        found: bytes.len(),
226                    });
227                }
228                let float_bytes: [u8; 8] = bytes[offset..offset + 8]
229                    .try_into()
230                    .expect("slice length checked");
231                let float_val = f64::from_le_bytes(float_bytes);
232                offset += 8;
233                BinaryValue::Float(float_val)
234            }
235            TypeTag::Bool => {
236                if bytes.len() < offset + 1 {
237                    return Err(BinaryError::UnexpectedEof {
238                        expected: offset + 1,
239                        found: bytes.len(),
240                    });
241                }
242                let bool_val = match bytes[offset] {
243                    0x00 => false,
244                    0x01 => true,
245                    other => {
246                        return Err(BinaryError::InvalidValue {
247                            field_id: fid,
248                            type_tag: tag.to_u8(),
249                            reason: format!(
250                                "Invalid boolean value: 0x{:02X} (expected 0x00 or 0x01)",
251                                other
252                            ),
253                        });
254                    }
255                };
256                offset += 1;
257                BinaryValue::Bool(bool_val)
258            }
259            TypeTag::String => {
260                let (length, consumed) =
261                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
262                        field_id: fid,
263                        type_tag: tag.to_u8(),
264                        reason: "Invalid string length VarInt".to_string(),
265                    })?;
266                offset += consumed;
267
268                if length < 0 {
269                    return Err(BinaryError::InvalidValue {
270                        field_id: fid,
271                        type_tag: tag.to_u8(),
272                        reason: format!("Negative string length: {}", length),
273                    });
274                }
275
276                let length = length as usize;
277                if bytes.len() < offset + length {
278                    return Err(BinaryError::UnexpectedEof {
279                        expected: offset + length,
280                        found: bytes.len(),
281                    });
282                }
283
284                let string_val = std::str::from_utf8(&bytes[offset..offset + length])
285                    .map_err(|_| BinaryError::InvalidUtf8 { field_id: fid })?
286                    .to_string();
287                offset += length;
288                BinaryValue::String(string_val)
289            }
290            TypeTag::StringArray => {
291                let (count, consumed) =
292                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
293                        field_id: fid,
294                        type_tag: tag.to_u8(),
295                        reason: "Invalid array count VarInt".to_string(),
296                    })?;
297                offset += consumed;
298
299                if count < 0 {
300                    return Err(BinaryError::InvalidValue {
301                        field_id: fid,
302                        type_tag: tag.to_u8(),
303                        reason: format!("Negative array count: {}", count),
304                    });
305                }
306
307                let count = count as usize;
308                let mut strings = Vec::with_capacity(count);
309
310                for _ in 0..count {
311                    let (length, consumed) = varint::decode(&bytes[offset..]).map_err(|_| {
312                        BinaryError::InvalidValue {
313                            field_id: fid,
314                            type_tag: tag.to_u8(),
315                            reason: "Invalid string length in array".to_string(),
316                        }
317                    })?;
318                    offset += consumed;
319
320                    if length < 0 {
321                        return Err(BinaryError::InvalidValue {
322                            field_id: fid,
323                            type_tag: tag.to_u8(),
324                            reason: format!("Negative string length in array: {}", length),
325                        });
326                    }
327
328                    let length = length as usize;
329                    if bytes.len() < offset + length {
330                        return Err(BinaryError::UnexpectedEof {
331                            expected: offset + length,
332                            found: bytes.len(),
333                        });
334                    }
335
336                    let string_val = std::str::from_utf8(&bytes[offset..offset + length])
337                        .map_err(|_| BinaryError::InvalidUtf8 { field_id: fid })?
338                        .to_string();
339                    offset += length;
340                    strings.push(string_val);
341                }
342
343                BinaryValue::StringArray(strings)
344            }
345            TypeTag::Embedding => {
346                let (length, consumed) =
347                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
348                        field_id: fid,
349                        type_tag: tag.to_u8(),
350                        reason: "Invalid embedding length VarInt".to_string(),
351                    })?;
352                offset += consumed;
353
354                if length < 0 {
355                    return Err(BinaryError::InvalidValue {
356                        field_id: fid,
357                        type_tag: tag.to_u8(),
358                        reason: format!("Negative embedding length: {}", length),
359                    });
360                }
361
362                let length = length as usize;
363                if bytes.len() < offset + length {
364                    return Err(BinaryError::UnexpectedEof {
365                        expected: offset + length,
366                        found: bytes.len(),
367                    });
368                }
369
370                let embedding_bytes = &bytes[offset..offset + length];
371                let vector = EmbeddingDecoder::decode(embedding_bytes).map_err(|e| {
372                    BinaryError::InvalidValue {
373                        field_id: fid,
374                        type_tag: tag.to_u8(),
375                        reason: format!("Failed to decode embedding: {}", e),
376                    }
377                })?;
378                offset += length;
379
380                BinaryValue::Embedding(vector)
381            }
382        };
383
384        Ok((Self { fid, tag, value }, offset))
385    }
386}
387
388#[cfg(test)]
389mod tests {
390    #![allow(clippy::approx_constant)]
391
392    use super::*;
393    use lnmp_core::LnmpValue;
394
395    #[test]
396    fn test_from_field_int() {
397        let field = LnmpField {
398            fid: 12,
399            value: LnmpValue::Int(14532),
400        };
401
402        let entry = BinaryEntry::from_field(&field).unwrap();
403        assert_eq!(entry.fid, 12);
404        assert_eq!(entry.tag, TypeTag::Int);
405        assert_eq!(entry.value, BinaryValue::Int(14532));
406    }
407
408    #[test]
409    fn test_from_field_float() {
410        let field = LnmpField {
411            fid: 5,
412            value: LnmpValue::Float(3.14),
413        };
414
415        let entry = BinaryEntry::from_field(&field).unwrap();
416        assert_eq!(entry.fid, 5);
417        assert_eq!(entry.tag, TypeTag::Float);
418        assert_eq!(entry.value, BinaryValue::Float(3.14));
419    }
420
421    #[test]
422    fn test_from_field_bool() {
423        let field = LnmpField {
424            fid: 7,
425            value: LnmpValue::Bool(true),
426        };
427
428        let entry = BinaryEntry::from_field(&field).unwrap();
429        assert_eq!(entry.fid, 7);
430        assert_eq!(entry.tag, TypeTag::Bool);
431        assert_eq!(entry.value, BinaryValue::Bool(true));
432    }
433
434    #[test]
435    fn test_from_field_string() {
436        let field = LnmpField {
437            fid: 1,
438            value: LnmpValue::String("hello".to_string()),
439        };
440
441        let entry = BinaryEntry::from_field(&field).unwrap();
442        assert_eq!(entry.fid, 1);
443        assert_eq!(entry.tag, TypeTag::String);
444        assert_eq!(entry.value, BinaryValue::String("hello".to_string()));
445    }
446
447    #[test]
448    fn test_from_field_string_array() {
449        let field = LnmpField {
450            fid: 23,
451            value: LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
452        };
453
454        let entry = BinaryEntry::from_field(&field).unwrap();
455        assert_eq!(entry.fid, 23);
456        assert_eq!(entry.tag, TypeTag::StringArray);
457        assert_eq!(
458            entry.value,
459            BinaryValue::StringArray(vec!["admin".to_string(), "dev".to_string()])
460        );
461    }
462
463    #[test]
464    fn test_to_field_int() {
465        let entry = BinaryEntry {
466            fid: 12,
467            tag: TypeTag::Int,
468            value: BinaryValue::Int(42),
469        };
470
471        let field = entry.to_field();
472        assert_eq!(field.fid, 12);
473        assert_eq!(field.value, LnmpValue::Int(42));
474    }
475
476    #[test]
477    fn test_to_field_float() {
478        let entry = BinaryEntry {
479            fid: 5,
480            tag: TypeTag::Float,
481            value: BinaryValue::Float(2.718),
482        };
483
484        let field = entry.to_field();
485        assert_eq!(field.fid, 5);
486        assert_eq!(field.value, LnmpValue::Float(2.718));
487    }
488
489    #[test]
490    fn test_to_field_bool() {
491        let entry = BinaryEntry {
492            fid: 7,
493            tag: TypeTag::Bool,
494            value: BinaryValue::Bool(false),
495        };
496
497        let field = entry.to_field();
498        assert_eq!(field.fid, 7);
499        assert_eq!(field.value, LnmpValue::Bool(false));
500    }
501
502    #[test]
503    fn test_to_field_string() {
504        let entry = BinaryEntry {
505            fid: 1,
506            tag: TypeTag::String,
507            value: BinaryValue::String("world".to_string()),
508        };
509
510        let field = entry.to_field();
511        assert_eq!(field.fid, 1);
512        assert_eq!(field.value, LnmpValue::String("world".to_string()));
513    }
514
515    #[test]
516    fn test_to_field_string_array() {
517        let entry = BinaryEntry {
518            fid: 23,
519            tag: TypeTag::StringArray,
520            value: BinaryValue::StringArray(vec!["x".to_string(), "y".to_string()]),
521        };
522
523        let field = entry.to_field();
524        assert_eq!(field.fid, 23);
525        assert_eq!(
526            field.value,
527            LnmpValue::StringArray(vec!["x".to_string(), "y".to_string()])
528        );
529    }
530
531    #[test]
532    fn test_encode_int() {
533        let entry = BinaryEntry {
534            fid: 12,
535            tag: TypeTag::Int,
536            value: BinaryValue::Int(14532),
537        };
538
539        let bytes = entry.encode();
540
541        // FID (12 in little-endian)
542        assert_eq!(bytes[0], 0x0C);
543        assert_eq!(bytes[1], 0x00);
544        // TAG (Int = 0x01)
545        assert_eq!(bytes[2], 0x01);
546        // VALUE (14532 as VarInt)
547        let varint_bytes = varint::encode(14532);
548        assert_eq!(&bytes[3..], &varint_bytes[..]);
549    }
550
551    #[test]
552    fn test_encode_float() {
553        let entry = BinaryEntry {
554            fid: 5,
555            tag: TypeTag::Float,
556            value: BinaryValue::Float(3.14),
557        };
558
559        let bytes = entry.encode();
560
561        // FID (5 in little-endian)
562        assert_eq!(bytes[0], 0x05);
563        assert_eq!(bytes[1], 0x00);
564        // TAG (Float = 0x02)
565        assert_eq!(bytes[2], 0x02);
566        // VALUE (3.14 as IEEE754 LE)
567        let float_bytes = 3.14f64.to_le_bytes();
568        assert_eq!(&bytes[3..11], &float_bytes[..]);
569    }
570
571    #[test]
572    fn test_encode_bool_true() {
573        let entry = BinaryEntry {
574            fid: 7,
575            tag: TypeTag::Bool,
576            value: BinaryValue::Bool(true),
577        };
578
579        let bytes = entry.encode();
580
581        // FID (7 in little-endian)
582        assert_eq!(bytes[0], 0x07);
583        assert_eq!(bytes[1], 0x00);
584        // TAG (Bool = 0x03)
585        assert_eq!(bytes[2], 0x03);
586        // VALUE (true = 0x01)
587        assert_eq!(bytes[3], 0x01);
588    }
589
590    #[test]
591    fn test_encode_bool_false() {
592        let entry = BinaryEntry {
593            fid: 7,
594            tag: TypeTag::Bool,
595            value: BinaryValue::Bool(false),
596        };
597
598        let bytes = entry.encode();
599
600        // FID (7 in little-endian)
601        assert_eq!(bytes[0], 0x07);
602        assert_eq!(bytes[1], 0x00);
603        // TAG (Bool = 0x03)
604        assert_eq!(bytes[2], 0x03);
605        // VALUE (false = 0x00)
606        assert_eq!(bytes[3], 0x00);
607    }
608
609    #[test]
610    fn test_encode_string() {
611        let entry = BinaryEntry {
612            fid: 1,
613            tag: TypeTag::String,
614            value: BinaryValue::String("hello".to_string()),
615        };
616
617        let bytes = entry.encode();
618
619        // FID (1 in little-endian)
620        assert_eq!(bytes[0], 0x01);
621        assert_eq!(bytes[1], 0x00);
622        // TAG (String = 0x04)
623        assert_eq!(bytes[2], 0x04);
624        // VALUE (length VarInt + UTF-8)
625        let length_varint = varint::encode(5);
626        assert_eq!(&bytes[3..3 + length_varint.len()], &length_varint[..]);
627        let offset = 3 + length_varint.len();
628        assert_eq!(&bytes[offset..], b"hello");
629    }
630
631    #[test]
632    fn test_encode_string_array() {
633        let entry = BinaryEntry {
634            fid: 23,
635            tag: TypeTag::StringArray,
636            value: BinaryValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
637        };
638
639        let bytes = entry.encode();
640
641        // FID (23 in little-endian)
642        assert_eq!(bytes[0], 0x17);
643        assert_eq!(bytes[1], 0x00);
644        // TAG (StringArray = 0x05)
645        assert_eq!(bytes[2], 0x05);
646
647        let mut offset = 3;
648        // Count VarInt (2)
649        let count_varint = varint::encode(2);
650        assert_eq!(
651            &bytes[offset..offset + count_varint.len()],
652            &count_varint[..]
653        );
654        offset += count_varint.len();
655
656        // First string "admin"
657        let len1_varint = varint::encode(5);
658        assert_eq!(&bytes[offset..offset + len1_varint.len()], &len1_varint[..]);
659        offset += len1_varint.len();
660        assert_eq!(&bytes[offset..offset + 5], b"admin");
661        offset += 5;
662
663        // Second string "dev"
664        let len2_varint = varint::encode(3);
665        assert_eq!(&bytes[offset..offset + len2_varint.len()], &len2_varint[..]);
666        offset += len2_varint.len();
667        assert_eq!(&bytes[offset..offset + 3], b"dev");
668    }
669
670    #[test]
671    fn test_decode_int() {
672        let entry = BinaryEntry {
673            fid: 12,
674            tag: TypeTag::Int,
675            value: BinaryValue::Int(14532),
676        };
677
678        let bytes = entry.encode();
679        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
680
681        assert_eq!(decoded, entry);
682        assert_eq!(consumed, bytes.len());
683    }
684
685    #[test]
686    fn test_decode_float() {
687        let entry = BinaryEntry {
688            fid: 5,
689            tag: TypeTag::Float,
690            value: BinaryValue::Float(3.14),
691        };
692
693        let bytes = entry.encode();
694        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
695
696        assert_eq!(decoded, entry);
697        assert_eq!(consumed, bytes.len());
698    }
699
700    #[test]
701    fn test_decode_bool() {
702        let entry = BinaryEntry {
703            fid: 7,
704            tag: TypeTag::Bool,
705            value: BinaryValue::Bool(true),
706        };
707
708        let bytes = entry.encode();
709        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
710
711        assert_eq!(decoded, entry);
712        assert_eq!(consumed, bytes.len());
713    }
714
715    #[test]
716    fn test_decode_string() {
717        let entry = BinaryEntry {
718            fid: 1,
719            tag: TypeTag::String,
720            value: BinaryValue::String("hello".to_string()),
721        };
722
723        let bytes = entry.encode();
724        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
725
726        assert_eq!(decoded, entry);
727        assert_eq!(consumed, bytes.len());
728    }
729
730    #[test]
731    fn test_decode_string_array() {
732        let entry = BinaryEntry {
733            fid: 23,
734            tag: TypeTag::StringArray,
735            value: BinaryValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
736        };
737
738        let bytes = entry.encode();
739        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
740
741        assert_eq!(decoded, entry);
742        assert_eq!(consumed, bytes.len());
743    }
744
745    #[test]
746    fn test_decode_with_trailing_data() {
747        let entry = BinaryEntry {
748            fid: 1,
749            tag: TypeTag::Int,
750            value: BinaryValue::Int(42),
751        };
752
753        let mut bytes = entry.encode();
754        bytes.extend_from_slice(&[0xFF, 0xFF, 0xFF]); // Extra bytes
755
756        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
757
758        assert_eq!(decoded, entry);
759        assert_eq!(consumed, bytes.len() - 3); // Should not consume trailing bytes
760    }
761
762    #[test]
763    fn test_decode_insufficient_data_fid() {
764        let bytes = vec![0x01]; // Only 1 byte, need 2 for FID
765        let result = BinaryEntry::decode(&bytes);
766        assert!(matches!(result, Err(BinaryError::UnexpectedEof { .. })));
767    }
768
769    #[test]
770    fn test_decode_insufficient_data_tag() {
771        let bytes = vec![0x01, 0x00]; // FID but no TAG
772        let result = BinaryEntry::decode(&bytes);
773        assert!(matches!(result, Err(BinaryError::UnexpectedEof { .. })));
774    }
775
776    #[test]
777    fn test_decode_invalid_type_tag() {
778        let bytes = vec![0x01, 0x00, 0xFF]; // Invalid TAG
779        let result = BinaryEntry::decode(&bytes);
780        assert!(matches!(result, Err(BinaryError::InvalidTypeTag { .. })));
781    }
782
783    #[test]
784    fn test_decode_invalid_bool_value() {
785        let bytes = vec![0x01, 0x00, 0x03, 0x02]; // Bool with value 0x02
786        let result = BinaryEntry::decode(&bytes);
787        assert!(matches!(result, Err(BinaryError::InvalidValue { .. })));
788    }
789
790    #[test]
791    fn test_decode_invalid_utf8() {
792        let mut bytes = vec![0x01, 0x00, 0x04]; // FID=1, TAG=String
793        bytes.extend_from_slice(&varint::encode(3)); // Length=3
794        bytes.extend_from_slice(&[0xFF, 0xFE, 0xFD]); // Invalid UTF-8
795
796        let result = BinaryEntry::decode(&bytes);
797        assert!(matches!(result, Err(BinaryError::InvalidUtf8 { .. })));
798    }
799
800    #[test]
801    fn test_roundtrip_all_types() {
802        let test_cases = vec![
803            BinaryEntry {
804                fid: 1,
805                tag: TypeTag::Int,
806                value: BinaryValue::Int(-42),
807            },
808            BinaryEntry {
809                fid: 2,
810                tag: TypeTag::Float,
811                value: BinaryValue::Float(3.14159),
812            },
813            BinaryEntry {
814                fid: 3,
815                tag: TypeTag::Bool,
816                value: BinaryValue::Bool(true),
817            },
818            BinaryEntry {
819                fid: 4,
820                tag: TypeTag::String,
821                value: BinaryValue::String("test".to_string()),
822            },
823            BinaryEntry {
824                fid: 5,
825                tag: TypeTag::StringArray,
826                value: BinaryValue::StringArray(vec!["a".to_string(), "b".to_string()]),
827            },
828        ];
829
830        for entry in test_cases {
831            let bytes = entry.encode();
832            let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
833            assert_eq!(decoded, entry);
834        }
835    }
836
837    #[test]
838    fn test_fid_boundary_values() {
839        // Test FID = 0
840        let entry0 = BinaryEntry {
841            fid: 0,
842            tag: TypeTag::Int,
843            value: BinaryValue::Int(1),
844        };
845        let bytes0 = entry0.encode();
846        let (decoded0, _) = BinaryEntry::decode(&bytes0).unwrap();
847        assert_eq!(decoded0.fid, 0);
848
849        // Test FID = 65535 (max u16)
850        let entry_max = BinaryEntry {
851            fid: 65535,
852            tag: TypeTag::Int,
853            value: BinaryValue::Int(1),
854        };
855        let bytes_max = entry_max.encode();
856        let (decoded_max, _) = BinaryEntry::decode(&bytes_max).unwrap();
857        assert_eq!(decoded_max.fid, 65535);
858    }
859
860    #[test]
861    fn test_empty_string() {
862        let entry = BinaryEntry {
863            fid: 1,
864            tag: TypeTag::String,
865            value: BinaryValue::String("".to_string()),
866        };
867
868        let bytes = entry.encode();
869        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
870        assert_eq!(decoded, entry);
871    }
872
873    #[test]
874    fn test_empty_string_array() {
875        let entry = BinaryEntry {
876            fid: 1,
877            tag: TypeTag::StringArray,
878            value: BinaryValue::StringArray(vec![]),
879        };
880
881        let bytes = entry.encode();
882        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
883        assert_eq!(decoded, entry);
884    }
885
886    #[test]
887    fn test_string_with_unicode() {
888        let entry = BinaryEntry {
889            fid: 1,
890            tag: TypeTag::String,
891            value: BinaryValue::String("Hello 🎯 World".to_string()),
892        };
893
894        let bytes = entry.encode();
895        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
896        assert_eq!(decoded, entry);
897    }
898
899    #[test]
900    fn test_negative_int() {
901        let entry = BinaryEntry {
902            fid: 1,
903            tag: TypeTag::Int,
904            value: BinaryValue::Int(-9999),
905        };
906
907        let bytes = entry.encode();
908        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
909        assert_eq!(decoded, entry);
910    }
911
912    #[test]
913    fn test_special_floats() {
914        // Test NaN
915        let entry_nan = BinaryEntry {
916            fid: 1,
917            tag: TypeTag::Float,
918            value: BinaryValue::Float(f64::NAN),
919        };
920        let bytes_nan = entry_nan.encode();
921        let (decoded_nan, _) = BinaryEntry::decode(&bytes_nan).unwrap();
922        match decoded_nan.value {
923            BinaryValue::Float(f) => assert!(f.is_nan()),
924            _ => panic!("Expected Float variant"),
925        }
926
927        // Test Infinity
928        let entry_inf = BinaryEntry {
929            fid: 2,
930            tag: TypeTag::Float,
931            value: BinaryValue::Float(f64::INFINITY),
932        };
933        let bytes_inf = entry_inf.encode();
934        let (decoded_inf, _) = BinaryEntry::decode(&bytes_inf).unwrap();
935        assert_eq!(decoded_inf, entry_inf);
936
937        // Test Negative Infinity
938        let entry_neg_inf = BinaryEntry {
939            fid: 3,
940            tag: TypeTag::Float,
941            value: BinaryValue::Float(f64::NEG_INFINITY),
942        };
943        let bytes_neg_inf = entry_neg_inf.encode();
944        let (decoded_neg_inf, _) = BinaryEntry::decode(&bytes_neg_inf).unwrap();
945        assert_eq!(decoded_neg_inf, entry_neg_inf);
946    }
947}