1use super::error::BinaryError;
4use lnmp_core::LnmpValue;
5
6#[repr(u8)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum TypeTag {
10 Int = 0x01,
12 Float = 0x02,
14 Bool = 0x03,
16 String = 0x04,
18 StringArray = 0x05,
20 NestedRecord = 0x06,
22 NestedArray = 0x07,
24 Embedding = 0x08,
26 Reserved09 = 0x09,
28 QuantizedEmbedding = 0x0A,
30 Reserved0B = 0x0B,
32 Reserved0C = 0x0C,
34 Reserved0D = 0x0D,
36 Reserved0E = 0x0E,
38 Reserved0F = 0x0F,
40}
41
42impl TypeTag {
43 pub fn from_u8(byte: u8) -> Result<Self, BinaryError> {
45 match byte {
46 0x01 => Ok(TypeTag::Int),
47 0x02 => Ok(TypeTag::Float),
48 0x03 => Ok(TypeTag::Bool),
49 0x04 => Ok(TypeTag::String),
50 0x05 => Ok(TypeTag::StringArray),
51 0x06 => Ok(TypeTag::NestedRecord),
52 0x07 => Ok(TypeTag::NestedArray),
53 0x08 => Ok(TypeTag::Embedding),
54 0x09 => Ok(TypeTag::Reserved09),
55 0x0A => Ok(TypeTag::QuantizedEmbedding),
56 0x0B => Ok(TypeTag::Reserved0B),
57 0x0C => Ok(TypeTag::Reserved0C),
58 0x0D => Ok(TypeTag::Reserved0D),
59 0x0E => Ok(TypeTag::Reserved0E),
60 0x0F => Ok(TypeTag::Reserved0F),
61 _ => Err(BinaryError::InvalidTypeTag { tag: byte }),
62 }
63 }
64
65 pub fn to_u8(self) -> u8 {
67 self as u8
68 }
69
70 pub fn is_v0_5_type(&self) -> bool {
72 matches!(
73 self,
74 TypeTag::NestedRecord
75 | TypeTag::NestedArray
76 | TypeTag::Embedding
77 | TypeTag::QuantizedEmbedding
78 | TypeTag::Reserved09
79 | TypeTag::Reserved0B
80 | TypeTag::Reserved0C
81 | TypeTag::Reserved0D
82 | TypeTag::Reserved0E
83 | TypeTag::Reserved0F
84 )
85 }
86
87 pub fn is_reserved(&self) -> bool {
89 matches!(
90 self,
91 TypeTag::Reserved09
92 | TypeTag::Reserved0B
93 | TypeTag::Reserved0C
94 | TypeTag::Reserved0D
95 | TypeTag::Reserved0E
96 | TypeTag::Reserved0F
97 )
98 }
99}
100
101#[derive(Debug, Clone, PartialEq)]
103pub enum BinaryValue {
104 Int(i64),
106 Float(f64),
108 Bool(bool),
110 String(String),
112 StringArray(Vec<String>),
114 NestedRecord(Box<lnmp_core::LnmpRecord>),
116 NestedArray(Vec<lnmp_core::LnmpRecord>),
118 Embedding(lnmp_embedding::Vector),
120 QuantizedEmbedding(lnmp_quant::QuantizedVector),
122}
123
124impl BinaryValue {
125 pub fn from_lnmp_value(value: &LnmpValue) -> Result<Self, BinaryError> {
129 match value {
130 LnmpValue::Int(i) => Ok(BinaryValue::Int(*i)),
131 LnmpValue::Float(f) => Ok(BinaryValue::Float(*f)),
132 LnmpValue::Bool(b) => Ok(BinaryValue::Bool(*b)),
133 LnmpValue::String(s) => Ok(BinaryValue::String(s.clone())),
134 LnmpValue::StringArray(arr) => Ok(BinaryValue::StringArray(arr.clone())),
135 LnmpValue::NestedRecord(rec) => Ok(BinaryValue::NestedRecord(rec.clone())),
136 LnmpValue::NestedArray(arr) => Ok(BinaryValue::NestedArray(arr.clone())),
137 LnmpValue::Embedding(vec) => Ok(BinaryValue::Embedding(vec.clone())),
138 LnmpValue::EmbeddingDelta(_) => Err(BinaryError::InvalidValue {
139 reason: "EmbeddingDelta cannot be encoded as BinaryValue, use full embedding"
140 .into(),
141 field_id: 0,
142 type_tag: 0x08,
143 }),
144 LnmpValue::QuantizedEmbedding(qv) => Ok(BinaryValue::QuantizedEmbedding(qv.clone())),
145 }
146 }
147
148 pub fn from_lnmp_value_v0_4(value: &LnmpValue) -> Result<Self, BinaryError> {
152 match value {
153 LnmpValue::Int(i) => Ok(BinaryValue::Int(*i)),
154 LnmpValue::Float(f) => Ok(BinaryValue::Float(*f)),
155 LnmpValue::Bool(b) => Ok(BinaryValue::Bool(*b)),
156 LnmpValue::String(s) => Ok(BinaryValue::String(s.clone())),
157 LnmpValue::StringArray(arr) => Ok(BinaryValue::StringArray(arr.clone())),
158 LnmpValue::NestedRecord(_) => Err(BinaryError::InvalidValue {
159 field_id: 0,
160 type_tag: 0x06,
161 reason: "Nested records not supported in v0.4 binary format".to_string(),
162 }),
163 LnmpValue::NestedArray(_) => Err(BinaryError::InvalidValue {
164 field_id: 0,
165 type_tag: 0x07,
166 reason: "Nested arrays not supported in v0.4 binary format".to_string(),
167 }),
168 LnmpValue::Embedding(_) => Err(BinaryError::InvalidValue {
169 field_id: 0,
170 type_tag: 0x08,
171 reason: "Embeddings not supported in v0.4 binary format".to_string(),
172 }),
173 LnmpValue::EmbeddingDelta(_) => Err(BinaryError::InvalidValue {
174 reason: "EmbeddingDelta not supported in v0.4".to_string(),
175 field_id: 0,
176 type_tag: 0x08,
177 }),
178 LnmpValue::QuantizedEmbedding(_) => Err(BinaryError::InvalidValue {
179 reason: "QuantizedEmbedding not supported in v0.4".to_string(),
180 field_id: 0,
181 type_tag: 0x0A,
182 }),
183 }
184 }
185
186 pub fn to_lnmp_value(&self) -> LnmpValue {
188 match self {
189 BinaryValue::Int(i) => LnmpValue::Int(*i),
190 BinaryValue::Float(f) => LnmpValue::Float(*f),
191 BinaryValue::Bool(b) => LnmpValue::Bool(*b),
192 BinaryValue::String(s) => LnmpValue::String(s.clone()),
193 BinaryValue::StringArray(arr) => LnmpValue::StringArray(arr.clone()),
194 BinaryValue::NestedRecord(rec) => LnmpValue::NestedRecord(rec.clone()),
195 BinaryValue::NestedArray(arr) => LnmpValue::NestedArray(arr.clone()),
196 BinaryValue::Embedding(vec) => LnmpValue::Embedding(vec.clone()),
197 BinaryValue::QuantizedEmbedding(qv) => LnmpValue::QuantizedEmbedding(qv.clone()),
198 }
199 }
200
201 pub fn type_tag(&self) -> TypeTag {
203 match self {
204 BinaryValue::Int(_) => TypeTag::Int,
205 BinaryValue::Float(_) => TypeTag::Float,
206 BinaryValue::Bool(_) => TypeTag::Bool,
207 BinaryValue::String(_) => TypeTag::String,
208 BinaryValue::StringArray(_) => TypeTag::StringArray,
209 BinaryValue::NestedRecord(_) => TypeTag::NestedRecord,
210 BinaryValue::NestedArray(_) => TypeTag::NestedArray,
211 BinaryValue::Embedding(_) => TypeTag::Embedding,
212 BinaryValue::QuantizedEmbedding(_) => TypeTag::QuantizedEmbedding,
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 #![allow(clippy::approx_constant)]
220
221 use super::*;
222 use lnmp_core::LnmpRecord;
223
224 #[test]
225 fn test_type_tag_from_u8() {
226 assert_eq!(TypeTag::from_u8(0x01).unwrap(), TypeTag::Int);
227 assert_eq!(TypeTag::from_u8(0x02).unwrap(), TypeTag::Float);
228 assert_eq!(TypeTag::from_u8(0x03).unwrap(), TypeTag::Bool);
229 assert_eq!(TypeTag::from_u8(0x04).unwrap(), TypeTag::String);
230 assert_eq!(TypeTag::from_u8(0x05).unwrap(), TypeTag::StringArray);
231 }
232
233 #[test]
234 fn test_type_tag_from_u8_invalid() {
235 assert!(TypeTag::from_u8(0x00).is_err());
236 assert!(TypeTag::from_u8(0xFF).is_err());
237 }
238
239 #[test]
240 fn test_type_tag_from_u8_v0_5_types() {
241 assert_eq!(TypeTag::from_u8(0x06).unwrap(), TypeTag::NestedRecord);
243 assert_eq!(TypeTag::from_u8(0x07).unwrap(), TypeTag::NestedArray);
244 }
245
246 #[test]
247 fn test_type_tag_from_u8_reserved() {
248 assert_eq!(TypeTag::from_u8(0x08).unwrap(), TypeTag::Embedding);
250 assert_eq!(TypeTag::from_u8(0x09).unwrap(), TypeTag::Reserved09);
251 assert_eq!(TypeTag::from_u8(0x0A).unwrap(), TypeTag::QuantizedEmbedding);
252 assert_eq!(TypeTag::from_u8(0x0B).unwrap(), TypeTag::Reserved0B);
253 assert_eq!(TypeTag::from_u8(0x0C).unwrap(), TypeTag::Reserved0C);
254 assert_eq!(TypeTag::from_u8(0x0D).unwrap(), TypeTag::Reserved0D);
255 assert_eq!(TypeTag::from_u8(0x0E).unwrap(), TypeTag::Reserved0E);
256 assert_eq!(TypeTag::from_u8(0x0F).unwrap(), TypeTag::Reserved0F);
257 }
258
259 #[test]
260 fn test_type_tag_to_u8() {
261 assert_eq!(TypeTag::Int.to_u8(), 0x01);
262 assert_eq!(TypeTag::Float.to_u8(), 0x02);
263 assert_eq!(TypeTag::Bool.to_u8(), 0x03);
264 assert_eq!(TypeTag::String.to_u8(), 0x04);
265 assert_eq!(TypeTag::StringArray.to_u8(), 0x05);
266 }
267
268 #[test]
269 fn test_type_tag_round_trip() {
270 let tags = vec![
271 TypeTag::Int,
272 TypeTag::Float,
273 TypeTag::Bool,
274 TypeTag::String,
275 TypeTag::StringArray,
276 TypeTag::NestedRecord,
277 TypeTag::NestedArray,
278 TypeTag::Embedding,
279 TypeTag::Reserved09,
280 TypeTag::QuantizedEmbedding,
281 TypeTag::Reserved0B,
282 TypeTag::Reserved0C,
283 TypeTag::Reserved0D,
284 TypeTag::Reserved0E,
285 TypeTag::Reserved0F,
286 ];
287
288 for tag in tags {
289 let byte = tag.to_u8();
290 let parsed = TypeTag::from_u8(byte).unwrap();
291 assert_eq!(parsed, tag);
292 }
293 }
294
295 #[test]
296 fn test_type_tag_is_v0_5_type() {
297 assert!(!TypeTag::Int.is_v0_5_type());
299 assert!(!TypeTag::Float.is_v0_5_type());
300 assert!(!TypeTag::Bool.is_v0_5_type());
301 assert!(!TypeTag::String.is_v0_5_type());
302 assert!(!TypeTag::StringArray.is_v0_5_type());
303
304 assert!(TypeTag::NestedRecord.is_v0_5_type());
306 assert!(TypeTag::NestedArray.is_v0_5_type());
307 assert!(TypeTag::Embedding.is_v0_5_type());
308 assert!(TypeTag::Reserved09.is_v0_5_type());
309 assert!(TypeTag::QuantizedEmbedding.is_v0_5_type());
310 assert!(TypeTag::Reserved0B.is_v0_5_type());
311 assert!(TypeTag::Reserved0C.is_v0_5_type());
312 assert!(TypeTag::Reserved0D.is_v0_5_type());
313 assert!(TypeTag::Reserved0E.is_v0_5_type());
314 assert!(TypeTag::Reserved0F.is_v0_5_type());
315 }
316
317 #[test]
318 fn test_type_tag_is_reserved() {
319 assert!(!TypeTag::Int.is_reserved());
321 assert!(!TypeTag::Float.is_reserved());
322 assert!(!TypeTag::Bool.is_reserved());
323 assert!(!TypeTag::String.is_reserved());
324 assert!(!TypeTag::StringArray.is_reserved());
325 assert!(!TypeTag::NestedRecord.is_reserved());
326 assert!(!TypeTag::NestedArray.is_reserved());
327
328 assert!(!TypeTag::Embedding.is_reserved());
330 assert!(TypeTag::Reserved09.is_reserved());
331 assert!(!TypeTag::QuantizedEmbedding.is_reserved());
332 assert!(TypeTag::Reserved0B.is_reserved());
333 assert!(TypeTag::Reserved0C.is_reserved());
334 assert!(TypeTag::Reserved0D.is_reserved());
335 assert!(TypeTag::Reserved0E.is_reserved());
336 assert!(TypeTag::Reserved0F.is_reserved());
337 }
338
339 #[test]
340 fn test_binary_value_from_lnmp_int() {
341 let lnmp_val = LnmpValue::Int(42);
342 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
343 assert_eq!(binary_val, BinaryValue::Int(42));
344 }
345
346 #[test]
347 fn test_binary_value_from_lnmp_float() {
348 let lnmp_val = LnmpValue::Float(3.14);
349 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
350 assert_eq!(binary_val, BinaryValue::Float(3.14));
351 }
352
353 #[test]
354 fn test_binary_value_from_lnmp_bool() {
355 let lnmp_val = LnmpValue::Bool(true);
356 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
357 assert_eq!(binary_val, BinaryValue::Bool(true));
358 }
359
360 #[test]
361 fn test_binary_value_from_lnmp_string() {
362 let lnmp_val = LnmpValue::String("hello".to_string());
363 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
364 assert_eq!(binary_val, BinaryValue::String("hello".to_string()));
365 }
366
367 #[test]
368 fn test_binary_value_from_lnmp_string_array() {
369 let lnmp_val = LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()]);
370 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
371 assert_eq!(
372 binary_val,
373 BinaryValue::StringArray(vec!["a".to_string(), "b".to_string()])
374 );
375 }
376
377 #[test]
378 fn test_binary_value_from_lnmp_nested_record() {
379 let nested = LnmpValue::NestedRecord(Box::new(LnmpRecord::new()));
381 let result = BinaryValue::from_lnmp_value(&nested);
382 assert!(result.is_ok());
383 match result.unwrap() {
384 BinaryValue::NestedRecord(_) => {}
385 _ => panic!("Expected NestedRecord variant"),
386 }
387 }
388
389 #[test]
390 fn test_binary_value_from_lnmp_nested_array() {
391 let nested = LnmpValue::NestedArray(vec![]);
393 let result = BinaryValue::from_lnmp_value(&nested);
394 assert!(result.is_ok());
395 match result.unwrap() {
396 BinaryValue::NestedArray(_) => {}
397 _ => panic!("Expected NestedArray variant"),
398 }
399 }
400
401 #[test]
402 fn test_binary_value_from_lnmp_nested_record_error_v0_4() {
403 let nested = LnmpValue::NestedRecord(Box::new(LnmpRecord::new()));
405 let result = BinaryValue::from_lnmp_value_v0_4(&nested);
406 assert!(result.is_err());
407 match result {
408 Err(BinaryError::InvalidValue { reason, .. }) => {
409 assert!(reason.contains("not supported in v0.4"));
410 }
411 _ => panic!("Expected InvalidValue error"),
412 }
413 }
414
415 #[test]
416 fn test_binary_value_from_lnmp_nested_array_error_v0_4() {
417 let nested = LnmpValue::NestedArray(vec![]);
419 let result = BinaryValue::from_lnmp_value_v0_4(&nested);
420 assert!(result.is_err());
421 match result {
422 Err(BinaryError::InvalidValue { reason, .. }) => {
423 assert!(reason.contains("not supported in v0.4"));
424 }
425 _ => panic!("Expected InvalidValue error"),
426 }
427 }
428
429 #[test]
430 fn test_binary_value_to_lnmp_int() {
431 let binary_val = BinaryValue::Int(-42);
432 let lnmp_val = binary_val.to_lnmp_value();
433 assert_eq!(lnmp_val, LnmpValue::Int(-42));
434 }
435
436 #[test]
437 fn test_binary_value_to_lnmp_float() {
438 let binary_val = BinaryValue::Float(2.718);
439 let lnmp_val = binary_val.to_lnmp_value();
440 assert_eq!(lnmp_val, LnmpValue::Float(2.718));
441 }
442
443 #[test]
444 fn test_binary_value_to_lnmp_bool() {
445 let binary_val = BinaryValue::Bool(false);
446 let lnmp_val = binary_val.to_lnmp_value();
447 assert_eq!(lnmp_val, LnmpValue::Bool(false));
448 }
449
450 #[test]
451 fn test_binary_value_to_lnmp_string() {
452 let binary_val = BinaryValue::String("world".to_string());
453 let lnmp_val = binary_val.to_lnmp_value();
454 assert_eq!(lnmp_val, LnmpValue::String("world".to_string()));
455 }
456
457 #[test]
458 fn test_binary_value_to_lnmp_string_array() {
459 let binary_val = BinaryValue::StringArray(vec!["x".to_string(), "y".to_string()]);
460 let lnmp_val = binary_val.to_lnmp_value();
461 assert_eq!(
462 lnmp_val,
463 LnmpValue::StringArray(vec!["x".to_string(), "y".to_string()])
464 );
465 }
466
467 #[test]
468 fn test_binary_value_round_trip_int() {
469 let original = LnmpValue::Int(12345);
470 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
471 let back = binary.to_lnmp_value();
472 assert_eq!(original, back);
473 }
474
475 #[test]
476 fn test_binary_value_round_trip_float() {
477 let original = LnmpValue::Float(1.414);
478 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
479 let back = binary.to_lnmp_value();
480 assert_eq!(original, back);
481 }
482
483 #[test]
484 fn test_binary_value_round_trip_bool() {
485 let original = LnmpValue::Bool(true);
486 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
487 let back = binary.to_lnmp_value();
488 assert_eq!(original, back);
489 }
490
491 #[test]
492 fn test_binary_value_round_trip_string() {
493 let original = LnmpValue::String("test".to_string());
494 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
495 let back = binary.to_lnmp_value();
496 assert_eq!(original, back);
497 }
498
499 #[test]
500 fn test_binary_value_round_trip_string_array() {
501 let original = LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()]);
502 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
503 let back = binary.to_lnmp_value();
504 assert_eq!(original, back);
505 }
506
507 #[test]
508 fn test_binary_value_type_tag_int() {
509 let val = BinaryValue::Int(100);
510 assert_eq!(val.type_tag(), TypeTag::Int);
511 }
512
513 #[test]
514 fn test_binary_value_type_tag_float() {
515 let val = BinaryValue::Float(3.14);
516 assert_eq!(val.type_tag(), TypeTag::Float);
517 }
518
519 #[test]
520 fn test_binary_value_type_tag_bool() {
521 let val = BinaryValue::Bool(true);
522 assert_eq!(val.type_tag(), TypeTag::Bool);
523 }
524
525 #[test]
526 fn test_binary_value_type_tag_string() {
527 let val = BinaryValue::String("test".to_string());
528 assert_eq!(val.type_tag(), TypeTag::String);
529 }
530
531 #[test]
532 fn test_binary_value_type_tag_string_array() {
533 let val = BinaryValue::StringArray(vec!["a".to_string()]);
534 assert_eq!(val.type_tag(), TypeTag::StringArray);
535 }
536
537 #[test]
538 fn test_binary_value_type_tag_nested_record() {
539 let val = BinaryValue::NestedRecord(Box::new(LnmpRecord::new()));
540 assert_eq!(val.type_tag(), TypeTag::NestedRecord);
541 }
542
543 #[test]
544 fn test_binary_value_type_tag_nested_array() {
545 let val = BinaryValue::NestedArray(vec![]);
546 assert_eq!(val.type_tag(), TypeTag::NestedArray);
547 }
548
549 #[test]
550 fn test_binary_value_round_trip_nested_record() {
551 let mut record = LnmpRecord::new();
552 record.add_field(lnmp_core::LnmpField {
553 fid: 1,
554 value: LnmpValue::Int(42),
555 });
556 let original = LnmpValue::NestedRecord(Box::new(record));
557 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
558 let back = binary.to_lnmp_value();
559 assert_eq!(original, back);
560 }
561
562 #[test]
563 fn test_binary_value_round_trip_nested_array() {
564 let mut record = LnmpRecord::new();
565 record.add_field(lnmp_core::LnmpField {
566 fid: 1,
567 value: LnmpValue::String("test".to_string()),
568 });
569 let original = LnmpValue::NestedArray(vec![record]);
570 let binary = BinaryValue::from_lnmp_value(&original).unwrap();
571 let back = binary.to_lnmp_value();
572 assert_eq!(original, back);
573 }
574
575 #[test]
576 fn test_binary_value_empty_string() {
577 let lnmp_val = LnmpValue::String("".to_string());
578 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
579 assert_eq!(binary_val, BinaryValue::String("".to_string()));
580 let back = binary_val.to_lnmp_value();
581 assert_eq!(back, lnmp_val);
582 }
583
584 #[test]
585 fn test_binary_value_empty_string_array() {
586 let lnmp_val = LnmpValue::StringArray(vec![]);
587 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
588 assert_eq!(binary_val, BinaryValue::StringArray(vec![]));
589 let back = binary_val.to_lnmp_value();
590 assert_eq!(back, lnmp_val);
591 }
592
593 #[test]
594 fn test_binary_value_negative_int() {
595 let lnmp_val = LnmpValue::Int(-9999);
596 let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
597 assert_eq!(binary_val, BinaryValue::Int(-9999));
598 let back = binary_val.to_lnmp_value();
599 assert_eq!(back, lnmp_val);
600 }
601
602 #[test]
603 fn test_binary_value_special_floats() {
604 let nan_val = LnmpValue::Float(f64::NAN);
606 let binary_nan = BinaryValue::from_lnmp_value(&nan_val).unwrap();
607 match binary_nan {
608 BinaryValue::Float(f) => assert!(f.is_nan()),
609 _ => panic!("Expected Float variant"),
610 }
611
612 let inf_val = LnmpValue::Float(f64::INFINITY);
614 let binary_inf = BinaryValue::from_lnmp_value(&inf_val).unwrap();
615 assert_eq!(binary_inf, BinaryValue::Float(f64::INFINITY));
616
617 let neg_inf_val = LnmpValue::Float(f64::NEG_INFINITY);
619 let binary_neg_inf = BinaryValue::from_lnmp_value(&neg_inf_val).unwrap();
620 assert_eq!(binary_neg_inf, BinaryValue::Float(f64::NEG_INFINITY));
621 }
622}