1use std::borrow::Cow;
6
7use crate::codec::primitives::{Reader, Writer};
8use crate::error::{DecodeError, EncodeError};
9use crate::limits::{MAX_BYTES_LEN, MAX_EMBEDDING_BYTES, MAX_EMBEDDING_DIMS, MAX_POSITION_LEN, MAX_STRING_LEN};
10use crate::model::{
11 DataType, DecimalMantissa, DictionaryBuilder, EmbeddingSubType, PropertyValue, Value,
12 WireDictionaries,
13};
14
15pub fn decode_value<'a>(
21 reader: &mut Reader<'a>,
22 data_type: DataType,
23 dicts: &WireDictionaries,
24) -> Result<Value<'a>, DecodeError> {
25 match data_type {
26 DataType::Bool => decode_bool(reader),
27 DataType::Int64 => decode_int64(reader, dicts),
28 DataType::Float64 => decode_float64(reader, dicts),
29 DataType::Decimal => decode_decimal(reader, dicts),
30 DataType::Text => decode_text(reader, dicts),
31 DataType::Bytes => decode_bytes(reader),
32 DataType::Date => decode_date(reader),
33 DataType::Time => decode_time(reader),
34 DataType::Datetime => decode_datetime(reader),
35 DataType::Schedule => decode_schedule(reader),
36 DataType::Point => decode_point(reader),
37 DataType::Embedding => decode_embedding(reader),
38 }
39}
40
41fn decode_bool<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
42 let byte = reader.read_byte("bool")?;
43 match byte {
44 0x00 => Ok(Value::Bool(false)),
45 0x01 => Ok(Value::Bool(true)),
46 _ => Err(DecodeError::InvalidBool { value: byte }),
47 }
48}
49
50fn decode_int64<'a>(reader: &mut Reader<'a>, dicts: &WireDictionaries) -> Result<Value<'a>, DecodeError> {
51 let value = reader.read_signed_varint("int64")?;
52 let unit_index = reader.read_varint("int64.unit")? as usize;
53 let unit = if unit_index == 0 {
54 None
55 } else {
56 let idx = unit_index - 1;
57 if idx >= dicts.units.len() {
58 return Err(DecodeError::IndexOutOfBounds {
59 dict: "units",
60 index: unit_index,
61 size: dicts.units.len() + 1,
62 });
63 }
64 Some(dicts.units[idx])
65 };
66 Ok(Value::Int64 { value, unit })
67}
68
69fn decode_float64<'a>(reader: &mut Reader<'a>, dicts: &WireDictionaries) -> Result<Value<'a>, DecodeError> {
70 let value = reader.read_f64("float64")?;
71 let unit_index = reader.read_varint("float64.unit")? as usize;
72 let unit = if unit_index == 0 {
73 None
74 } else {
75 let idx = unit_index - 1;
76 if idx >= dicts.units.len() {
77 return Err(DecodeError::IndexOutOfBounds {
78 dict: "units",
79 index: unit_index,
80 size: dicts.units.len() + 1,
81 });
82 }
83 Some(dicts.units[idx])
84 };
85 Ok(Value::Float64 { value, unit })
86}
87
88fn decode_decimal<'a>(reader: &mut Reader<'a>, dicts: &WireDictionaries) -> Result<Value<'a>, DecodeError> {
89 let exponent = reader.read_signed_varint("decimal.exponent")? as i32;
90 let mantissa_type = reader.read_byte("decimal.mantissa_type")?;
91
92 let mantissa = match mantissa_type {
93 0x00 => {
94 let v = reader.read_signed_varint("decimal.mantissa")?;
95 DecimalMantissa::I64(v)
96 }
97 0x01 => {
98 let len = reader.read_varint("decimal.mantissa_len")? as usize;
99 let bytes = reader.read_bytes(len, "decimal.mantissa_bytes")?;
100
101 if !bytes.is_empty() {
103 let first = bytes[0];
104 if bytes.len() > 1 {
106 let second = bytes[1];
107 if (first == 0x00 && (second & 0x80) == 0)
108 || (first == 0xFF && (second & 0x80) != 0) {
109 return Err(DecodeError::DecimalMantissaNotMinimal);
110 }
111 }
112 }
113
114 DecimalMantissa::Big(Cow::Borrowed(bytes))
115 }
116 _ => {
117 return Err(DecodeError::MalformedEncoding {
118 context: "invalid decimal mantissa type"
119 });
120 }
121 };
122
123 match &mantissa {
125 DecimalMantissa::I64(v) => {
126 if *v == 0 {
127 if exponent != 0 {
128 return Err(DecodeError::DecimalNotNormalized);
129 }
130 } else if *v % 10 == 0 {
131 return Err(DecodeError::DecimalNotNormalized);
132 }
133 }
134 DecimalMantissa::Big(bytes) => {
135 if is_big_mantissa_zero(bytes) {
136 if exponent != 0 {
137 return Err(DecodeError::DecimalNotNormalized);
138 }
139 } else if is_big_mantissa_divisible_by_10(bytes) {
140 return Err(DecodeError::DecimalNotNormalized);
141 }
142 }
143 }
144
145 let unit_index = reader.read_varint("decimal.unit")? as usize;
146 let unit = if unit_index == 0 {
147 None
148 } else {
149 let idx = unit_index - 1;
150 if idx >= dicts.units.len() {
151 return Err(DecodeError::IndexOutOfBounds {
152 dict: "units",
153 index: unit_index,
154 size: dicts.units.len() + 1,
155 });
156 }
157 Some(dicts.units[idx])
158 };
159
160 Ok(Value::Decimal { exponent, mantissa, unit })
161}
162
163fn is_big_mantissa_zero(bytes: &[u8]) -> bool {
165 bytes.iter().all(|&b| b == 0)
166}
167
168fn is_big_mantissa_divisible_by_10(bytes: &[u8]) -> bool {
176 if bytes.is_empty() {
177 return true; }
179
180 let is_negative = bytes[0] & 0x80 != 0;
182
183 if is_negative {
184 let abs_mod = twos_complement_abs_mod_10(bytes);
187 abs_mod == 0
188 } else {
189 let mut remainder = 0u32;
192 for &byte in bytes {
193 remainder = (remainder * 6 + byte as u32) % 10;
196 }
197 remainder == 0
198 }
199}
200
201fn twos_complement_abs_mod_10(bytes: &[u8]) -> u32 {
203 let mut remainder = 0u32;
213 for &byte in bytes {
214 let inverted = !byte;
215 remainder = (remainder * 6 + inverted as u32) % 10;
216 }
217
218 (remainder + 1) % 10
220}
221
222fn decode_text<'a>(reader: &mut Reader<'a>, dicts: &WireDictionaries) -> Result<Value<'a>, DecodeError> {
223 let value = reader.read_str(MAX_STRING_LEN, "text")?;
224 let lang_index = reader.read_varint("text.language")? as usize;
225
226 let language = if lang_index == 0 {
227 None
228 } else {
229 let idx = lang_index - 1;
230 if idx >= dicts.languages.len() {
231 return Err(DecodeError::IndexOutOfBounds {
232 dict: "languages",
233 index: lang_index,
234 size: dicts.languages.len() + 1, });
236 }
237 Some(dicts.languages[idx])
238 };
239
240 Ok(Value::Text { value: Cow::Borrowed(value), language })
241}
242
243fn decode_bytes<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
244 let len = reader.read_varint("bytes.len")? as usize;
245 if len > MAX_BYTES_LEN {
246 return Err(DecodeError::LengthExceedsLimit {
247 field: "bytes",
248 len,
249 max: MAX_BYTES_LEN,
250 });
251 }
252 let bytes = reader.read_bytes(len, "bytes")?;
253 Ok(Value::Bytes(Cow::Borrowed(bytes)))
254}
255
256fn decode_date<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
257 let bytes = reader.read_bytes(6, "date")?;
259 let days = i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
260 let offset_min = i16::from_le_bytes([bytes[4], bytes[5]]);
261
262 if offset_min < -1440 || offset_min > 1440 {
264 return Err(DecodeError::MalformedEncoding {
265 context: "DATE offset_min outside range [-1440, +1440]",
266 });
267 }
268
269 Ok(Value::Date { days, offset_min })
270}
271
272fn decode_time<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
273 let bytes = reader.read_bytes(8, "time")?;
275
276 let time_us_unsigned = u64::from_le_bytes([
278 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], 0, 0
279 ]);
280 let time_us = if time_us_unsigned & 0x8000_0000_0000 != 0 {
282 (time_us_unsigned | 0xFFFF_0000_0000_0000) as i64
283 } else {
284 time_us_unsigned as i64
285 };
286
287 let offset_min = i16::from_le_bytes([bytes[6], bytes[7]]);
288
289 if time_us < 0 || time_us > 86_399_999_999 {
291 return Err(DecodeError::MalformedEncoding {
292 context: "TIME time_us outside range [0, 86399999999]",
293 });
294 }
295
296 if offset_min < -1440 || offset_min > 1440 {
298 return Err(DecodeError::MalformedEncoding {
299 context: "TIME offset_min outside range [-1440, +1440]",
300 });
301 }
302
303 Ok(Value::Time { time_us, offset_min })
304}
305
306fn decode_datetime<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
307 let bytes = reader.read_bytes(10, "datetime")?;
309 let epoch_us = i64::from_le_bytes([
310 bytes[0], bytes[1], bytes[2], bytes[3],
311 bytes[4], bytes[5], bytes[6], bytes[7]
312 ]);
313 let offset_min = i16::from_le_bytes([bytes[8], bytes[9]]);
314
315 if offset_min < -1440 || offset_min > 1440 {
317 return Err(DecodeError::MalformedEncoding {
318 context: "DATETIME offset_min outside range [-1440, +1440]",
319 });
320 }
321
322 Ok(Value::Datetime { epoch_us, offset_min })
323}
324
325fn decode_schedule<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
326 let value = reader.read_str(MAX_STRING_LEN, "schedule")?;
327 Ok(Value::Schedule(Cow::Borrowed(value)))
330}
331
332fn decode_point<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
333 let ordinate_count = reader.read_byte("point.ordinate_count")?;
334
335 if ordinate_count != 2 && ordinate_count != 3 {
336 return Err(DecodeError::MalformedEncoding {
337 context: "POINT ordinate_count must be 2 or 3",
338 });
339 }
340
341 let lon = reader.read_f64("point.lon")?;
343 let lat = reader.read_f64("point.lat")?;
344 let alt = if ordinate_count == 3 {
345 Some(reader.read_f64("point.alt")?)
346 } else {
347 None
348 };
349
350 if !(-180.0..=180.0).contains(&lon) {
352 return Err(DecodeError::LongitudeOutOfRange { lon });
353 }
354 if !(-90.0..=90.0).contains(&lat) {
355 return Err(DecodeError::LatitudeOutOfRange { lat });
356 }
357 if lon.is_nan() || lat.is_nan() {
358 return Err(DecodeError::FloatIsNan);
359 }
360 if let Some(a) = alt {
361 if a.is_nan() {
362 return Err(DecodeError::FloatIsNan);
363 }
364 }
365
366 Ok(Value::Point { lon, lat, alt })
367}
368
369fn decode_embedding<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
370 let sub_type_byte = reader.read_byte("embedding.sub_type")?;
371 let sub_type = EmbeddingSubType::from_u8(sub_type_byte)
372 .ok_or(DecodeError::InvalidEmbeddingSubType { sub_type: sub_type_byte })?;
373
374 let dims = reader.read_varint("embedding.dims")? as usize;
375 if dims > MAX_EMBEDDING_DIMS {
376 return Err(DecodeError::LengthExceedsLimit {
377 field: "embedding.dims",
378 len: dims,
379 max: MAX_EMBEDDING_DIMS,
380 });
381 }
382
383 let expected_bytes = sub_type.bytes_for_dims(dims);
384 if expected_bytes > MAX_EMBEDDING_BYTES {
385 return Err(DecodeError::LengthExceedsLimit {
386 field: "embedding.data",
387 len: expected_bytes,
388 max: MAX_EMBEDDING_BYTES,
389 });
390 }
391
392 let data = reader.read_bytes(expected_bytes, "embedding.data")?;
393
394 if sub_type == EmbeddingSubType::Float32 {
396 for chunk in data.chunks_exact(4) {
397 let f = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
398 if f.is_nan() {
399 return Err(DecodeError::FloatIsNan);
400 }
401 }
402 }
403
404 if sub_type == EmbeddingSubType::Binary && dims % 8 != 0 {
406 let last_byte = data[data.len() - 1];
407 let unused_bits = 8 - (dims % 8);
408 let mask = !((1u8 << (8 - unused_bits)) - 1);
409 if last_byte & mask != 0 {
410 return Err(DecodeError::MalformedEncoding {
411 context: "binary embedding has non-zero unused bits",
412 });
413 }
414 }
415
416 Ok(Value::Embedding { sub_type, dims, data: Cow::Borrowed(data) })
417}
418
419pub fn decode_property_value<'a>(
421 reader: &mut Reader<'a>,
422 dicts: &WireDictionaries,
423) -> Result<PropertyValue<'a>, DecodeError> {
424 let prop_index = reader.read_varint("property")? as usize;
425 if prop_index >= dicts.properties.len() {
426 return Err(DecodeError::IndexOutOfBounds {
427 dict: "properties",
428 index: prop_index,
429 size: dicts.properties.len(),
430 });
431 }
432
433 let (property, data_type) = dicts.properties[prop_index];
434 let value = decode_value(reader, data_type, dicts)?;
435
436 Ok(PropertyValue { property, value })
437}
438
439pub fn encode_value(
445 writer: &mut Writer,
446 value: &Value<'_>,
447 dict_builder: &mut DictionaryBuilder,
448) -> Result<(), EncodeError> {
449 match value {
450 Value::Bool(v) => {
451 writer.write_byte(if *v { 0x01 } else { 0x00 });
452 }
453 Value::Int64 { value, unit } => {
454 writer.write_signed_varint(*value);
455 let unit_index = dict_builder.add_unit(*unit);
456 writer.write_varint(unit_index as u64);
457 }
458 Value::Float64 { value, unit } => {
459 if value.is_nan() {
460 return Err(EncodeError::FloatIsNan);
461 }
462 writer.write_f64(*value);
463 let unit_index = dict_builder.add_unit(*unit);
464 writer.write_varint(unit_index as u64);
465 }
466 Value::Decimal { exponent, mantissa, unit } => {
467 encode_decimal(writer, *exponent, mantissa)?;
468 let unit_index = dict_builder.add_unit(*unit);
469 writer.write_varint(unit_index as u64);
470 }
471 Value::Text { value, language } => {
472 writer.write_string(value);
473 let lang_index = dict_builder.add_language(*language);
474 writer.write_varint(lang_index as u64);
475 }
476 Value::Bytes(bytes) => {
477 writer.write_bytes_prefixed(bytes);
478 }
479 Value::Date { days, offset_min } => {
480 if *offset_min < -1440 || *offset_min > 1440 {
482 return Err(EncodeError::InvalidInput {
483 context: "DATE offset_min outside range [-1440, +1440]",
484 });
485 }
486 writer.write_bytes(&days.to_le_bytes());
488 writer.write_bytes(&offset_min.to_le_bytes());
489 }
490 Value::Time { time_us, offset_min } => {
491 if *time_us < 0 || *time_us > 86_399_999_999 {
493 return Err(EncodeError::InvalidInput {
494 context: "TIME time_us outside range [0, 86399999999]",
495 });
496 }
497 if *offset_min < -1440 || *offset_min > 1440 {
499 return Err(EncodeError::InvalidInput {
500 context: "TIME offset_min outside range [-1440, +1440]",
501 });
502 }
503 let time_bytes = time_us.to_le_bytes();
506 writer.write_bytes(&time_bytes[0..6]);
507 writer.write_bytes(&offset_min.to_le_bytes());
508 }
509 Value::Datetime { epoch_us, offset_min } => {
510 if *offset_min < -1440 || *offset_min > 1440 {
512 return Err(EncodeError::InvalidInput {
513 context: "DATETIME offset_min outside range [-1440, +1440]",
514 });
515 }
516 writer.write_bytes(&epoch_us.to_le_bytes());
518 writer.write_bytes(&offset_min.to_le_bytes());
519 }
520 Value::Schedule(s) => {
521 writer.write_string(s);
523 }
524 Value::Point { lon, lat, alt } => {
525 if *lon < -180.0 || *lon > 180.0 {
526 return Err(EncodeError::LongitudeOutOfRange { lon: *lon });
527 }
528 if *lat < -90.0 || *lat > 90.0 {
529 return Err(EncodeError::LatitudeOutOfRange { lat: *lat });
530 }
531 if lat.is_nan() || lon.is_nan() {
532 return Err(EncodeError::FloatIsNan);
533 }
534 if let Some(a) = alt {
535 if a.is_nan() {
536 return Err(EncodeError::FloatIsNan);
537 }
538 }
539 let ordinate_count = if alt.is_some() { 3u8 } else { 2u8 };
541 writer.write_byte(ordinate_count);
542 writer.write_f64(*lon);
544 writer.write_f64(*lat);
545 if let Some(a) = alt {
546 writer.write_f64(*a);
547 }
548 }
549 Value::Embedding { sub_type, dims, data } => {
550 let expected = sub_type.bytes_for_dims(*dims);
551 if data.len() != expected {
552 return Err(EncodeError::EmbeddingDimensionMismatch {
553 sub_type: *sub_type as u8,
554 dims: *dims,
555 data_len: data.len(),
556 });
557 }
558 if *sub_type == EmbeddingSubType::Float32 {
560 for chunk in data.chunks_exact(4) {
561 let f = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
562 if f.is_nan() {
563 return Err(EncodeError::FloatIsNan);
564 }
565 }
566 }
567 writer.write_byte(*sub_type as u8);
568 writer.write_varint(*dims as u64);
569 writer.write_bytes(data);
570 }
571 }
572 Ok(())
573}
574
575fn encode_decimal(
576 writer: &mut Writer,
577 exponent: i32,
578 mantissa: &DecimalMantissa<'_>,
579) -> Result<(), EncodeError> {
580 match mantissa {
582 DecimalMantissa::I64(v) => {
583 if *v == 0 {
584 if exponent != 0 {
585 return Err(EncodeError::DecimalNotNormalized);
586 }
587 } else if *v % 10 == 0 {
588 return Err(EncodeError::DecimalNotNormalized);
589 }
590 }
591 DecimalMantissa::Big(bytes) => {
592 if is_big_mantissa_zero(bytes) {
593 if exponent != 0 {
594 return Err(EncodeError::DecimalNotNormalized);
595 }
596 } else if is_big_mantissa_divisible_by_10(bytes) {
597 return Err(EncodeError::DecimalNotNormalized);
598 }
599 }
600 }
601
602 writer.write_signed_varint(exponent as i64);
603
604 match mantissa {
605 DecimalMantissa::I64(v) => {
606 writer.write_byte(0x00);
607 writer.write_signed_varint(*v);
608 }
609 DecimalMantissa::Big(bytes) => {
610 writer.write_byte(0x01);
611 writer.write_varint(bytes.len() as u64);
612 writer.write_bytes(bytes);
613 }
614 }
615
616 Ok(())
617}
618
619pub fn encode_property_value(
621 writer: &mut Writer,
622 pv: &PropertyValue<'_>,
623 dict_builder: &mut DictionaryBuilder,
624 data_type: DataType,
625) -> Result<(), EncodeError> {
626 let prop_index = dict_builder.add_property(pv.property, data_type);
627 writer.write_varint(prop_index as u64);
628 encode_value(writer, &pv.value, dict_builder)?;
629 Ok(())
630}
631
632pub fn validate_position(pos: &str) -> Result<(), EncodeError> {
634 if pos.len() > MAX_POSITION_LEN {
635 return Err(EncodeError::PositionTooLong);
636 }
637 for c in pos.chars() {
638 if !c.is_ascii_alphanumeric() {
639 return Err(EncodeError::InvalidPositionChar);
640 }
641 }
642 Ok(())
643}
644
645pub fn decode_position<'a>(reader: &mut Reader<'a>) -> Result<Cow<'a, str>, DecodeError> {
647 let pos = reader.read_str(MAX_POSITION_LEN, "position")?;
648 for c in pos.chars() {
649 if !c.is_ascii_alphanumeric() {
650 return Err(DecodeError::InvalidPositionChar { char: c });
651 }
652 }
653 Ok(Cow::Borrowed(pos))
654}
655
656#[cfg(test)]
657mod tests {
658 use super::*;
659
660 #[test]
661 fn test_bool_roundtrip() {
662 for v in [true, false] {
663 let value = Value::Bool(v);
664 let dicts = WireDictionaries::default();
665 let mut dict_builder = DictionaryBuilder::new();
666
667 let mut writer = Writer::new();
668 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
669
670 let mut reader = Reader::new(writer.as_bytes());
671 let decoded = decode_value(&mut reader, DataType::Bool, &dicts).unwrap();
672
673 assert_eq!(value, decoded);
674 }
675 }
676
677 #[test]
678 fn test_int64_roundtrip() {
679 for v in [0i64, 1, -1, i64::MAX, i64::MIN, 12345678] {
680 let value = Value::Int64 { value: v, unit: None };
681 let mut dict_builder = DictionaryBuilder::new();
682
683 let mut writer = Writer::new();
684 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
685
686 let dicts = dict_builder.build();
687 let mut reader = Reader::new(writer.as_bytes());
688 let decoded = decode_value(&mut reader, DataType::Int64, &dicts).unwrap();
689
690 assert_eq!(value, decoded);
691 }
692 }
693
694 #[test]
695 fn test_float64_roundtrip() {
696 for v in [0.0, 1.0, -1.0, f64::INFINITY, f64::NEG_INFINITY, 3.14159] {
697 let value = Value::Float64 { value: v, unit: None };
698 let mut dict_builder = DictionaryBuilder::new();
699
700 let mut writer = Writer::new();
701 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
702
703 let dicts = dict_builder.build();
704 let mut reader = Reader::new(writer.as_bytes());
705 let decoded = decode_value(&mut reader, DataType::Float64, &dicts).unwrap();
706
707 assert_eq!(value, decoded);
708 }
709 }
710
711 #[test]
712 fn test_text_roundtrip() {
713 let value = Value::Text {
714 value: Cow::Owned("hello world".to_string()),
715 language: None,
716 };
717 let mut dict_builder = DictionaryBuilder::new();
718
719 let mut writer = Writer::new();
720 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
721
722 let decode_dicts = dict_builder.build();
724
725 let mut reader = Reader::new(writer.as_bytes());
726 let decoded = decode_value(&mut reader, DataType::Text, &decode_dicts).unwrap();
727
728 match (&value, &decoded) {
730 (Value::Text { value: v1, language: l1 }, Value::Text { value: v2, language: l2 }) => {
731 assert_eq!(v1.as_ref(), v2.as_ref());
732 assert_eq!(l1, l2);
733 }
734 _ => panic!("expected Text values"),
735 }
736 }
737
738 #[test]
739 fn test_point_roundtrip() {
740 let value = Value::Point { lon: -122.4194, lat: 37.7749, alt: None };
742 let dicts = WireDictionaries::default();
743 let mut dict_builder = DictionaryBuilder::new();
744
745 let mut writer = Writer::new();
746 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
747
748 let mut reader = Reader::new(writer.as_bytes());
749 let decoded = decode_value(&mut reader, DataType::Point, &dicts).unwrap();
750
751 assert_eq!(value, decoded);
752
753 let value_3d = Value::Point { lon: -122.4194, lat: 37.7749, alt: Some(100.0) };
755 let mut dict_builder = DictionaryBuilder::new();
756
757 let mut writer = Writer::new();
758 encode_value(&mut writer, &value_3d, &mut dict_builder).unwrap();
759
760 let mut reader = Reader::new(writer.as_bytes());
761 let decoded_3d = decode_value(&mut reader, DataType::Point, &dicts).unwrap();
762
763 assert_eq!(value_3d, decoded_3d);
764 }
765
766 #[test]
767 fn test_point_validation() {
768 let value = Value::Point { lon: 0.0, lat: 91.0, alt: None };
770 let mut dict_builder = DictionaryBuilder::new();
771 let mut writer = Writer::new();
772 let result = encode_value(&mut writer, &value, &mut dict_builder);
773 assert!(result.is_err());
774
775 let value = Value::Point { lon: 181.0, lat: 0.0, alt: None };
777 let mut dict_builder = DictionaryBuilder::new();
778 let mut writer = Writer::new();
779 let result = encode_value(&mut writer, &value, &mut dict_builder);
780 assert!(result.is_err());
781
782 let value = Value::Point { lon: 0.0, lat: 0.0, alt: Some(f64::NAN) };
784 let mut dict_builder = DictionaryBuilder::new();
785 let mut writer = Writer::new();
786 let result = encode_value(&mut writer, &value, &mut dict_builder);
787 assert!(result.is_err());
788 }
789
790 #[test]
791 fn test_schedule_roundtrip() {
792 let dicts = WireDictionaries::default();
793 let mut dict_builder = DictionaryBuilder::new();
794
795 let value = Value::Schedule(Cow::Owned("BEGIN:VEVENT\r\nDTSTART:20240315T090000Z\r\nDTEND:20240315T100000Z\r\nEND:VEVENT".to_string()));
797
798 let mut writer = Writer::new();
799 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
800
801 let mut reader = Reader::new(writer.as_bytes());
802 let decoded = decode_value(&mut reader, DataType::Schedule, &dicts).unwrap();
803
804 match (&value, &decoded) {
805 (Value::Schedule(s1), Value::Schedule(s2)) => {
806 assert_eq!(s1.as_ref(), s2.as_ref());
807 }
808 _ => panic!("expected Schedule values"),
809 }
810 }
811
812 #[test]
813 fn test_embedding_roundtrip() {
814 let value = Value::Embedding {
815 sub_type: EmbeddingSubType::Float32,
816 dims: 4,
817 data: Cow::Owned(vec![0u8; 16]), };
819 let dicts = WireDictionaries::default();
820 let mut dict_builder = DictionaryBuilder::new();
821
822 let mut writer = Writer::new();
823 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
824
825 let mut reader = Reader::new(writer.as_bytes());
826 let decoded = decode_value(&mut reader, DataType::Embedding, &dicts).unwrap();
827
828 match (&value, &decoded) {
830 (
831 Value::Embedding { sub_type: s1, dims: d1, data: data1 },
832 Value::Embedding { sub_type: s2, dims: d2, data: data2 },
833 ) => {
834 assert_eq!(s1, s2);
835 assert_eq!(d1, d2);
836 assert_eq!(data1.as_ref(), data2.as_ref());
837 }
838 _ => panic!("expected Embedding values"),
839 }
840 }
841
842 #[test]
843 fn test_decimal_normalized() {
844 let valid = Value::Decimal {
846 exponent: -2,
847 mantissa: DecimalMantissa::I64(1234),
848 unit: None,
849 };
850 let mut dict_builder = DictionaryBuilder::new();
851 let mut writer = Writer::new();
852 assert!(encode_value(&mut writer, &valid, &mut dict_builder).is_ok());
853
854 let invalid = Value::Decimal {
856 exponent: -2,
857 mantissa: DecimalMantissa::I64(1230),
858 unit: None,
859 };
860 let mut dict_builder = DictionaryBuilder::new();
861 let mut writer = Writer::new();
862 assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
863 }
864
865 #[test]
866 fn test_date_roundtrip() {
867 let dicts = WireDictionaries::default();
868 let mut dict_builder = DictionaryBuilder::new();
869
870 let test_cases = [
872 (0, 0), (19797, 0), (19797, 330), (-36524, 0), (i32::MAX, 0), (i32::MIN, 0), ];
879
880 for (days, offset_min) in test_cases {
881 let value = Value::Date { days, offset_min };
882
883 let mut writer = Writer::new();
884 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
885
886 let mut reader = Reader::new(writer.as_bytes());
887 let decoded = decode_value(&mut reader, DataType::Date, &dicts).unwrap();
888
889 assert_eq!(value, decoded);
890 }
891 }
892
893 #[test]
894 fn test_time_roundtrip() {
895 let dicts = WireDictionaries::default();
896 let mut dict_builder = DictionaryBuilder::new();
897
898 let test_cases = [
900 (0, 0), (52_200_000_000, 0), (52_200_500_000, 330), (86_399_999_999, 0), (0, -300), ];
906
907 for (time_us, offset_min) in test_cases {
908 let value = Value::Time { time_us, offset_min };
909
910 let mut writer = Writer::new();
911 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
912
913 let mut reader = Reader::new(writer.as_bytes());
914 let decoded = decode_value(&mut reader, DataType::Time, &dicts).unwrap();
915
916 assert_eq!(value, decoded);
917 }
918 }
919
920 #[test]
921 fn test_datetime_roundtrip() {
922 let dicts = WireDictionaries::default();
923 let mut dict_builder = DictionaryBuilder::new();
924
925 let test_cases = [
927 (0, 0), (1_710_513_000_000_000, 0), (1_710_493_200_000_000, 330), (-1_000_000_000_000, 0), (i64::MAX / 2, 0), ];
933
934 for (epoch_us, offset_min) in test_cases {
935 let value = Value::Datetime { epoch_us, offset_min };
936
937 let mut writer = Writer::new();
938 encode_value(&mut writer, &value, &mut dict_builder).unwrap();
939
940 let mut reader = Reader::new(writer.as_bytes());
941 let decoded = decode_value(&mut reader, DataType::Datetime, &dicts).unwrap();
942
943 assert_eq!(value, decoded);
944 }
945 }
946
947 #[test]
948 fn test_date_validation() {
949 let mut dict_builder = DictionaryBuilder::new();
950
951 let invalid = Value::Date { days: 0, offset_min: 1500 };
953 let mut writer = Writer::new();
954 assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
955
956 let invalid_neg = Value::Date { days: 0, offset_min: -1500 };
957 let mut writer = Writer::new();
958 assert!(encode_value(&mut writer, &invalid_neg, &mut dict_builder).is_err());
959 }
960
961 #[test]
962 fn test_time_validation() {
963 let mut dict_builder = DictionaryBuilder::new();
964
965 let invalid_high = Value::Time { time_us: 86_400_000_000, offset_min: 0 };
967 let mut writer = Writer::new();
968 assert!(encode_value(&mut writer, &invalid_high, &mut dict_builder).is_err());
969
970 let invalid_neg = Value::Time { time_us: -1, offset_min: 0 };
971 let mut writer = Writer::new();
972 assert!(encode_value(&mut writer, &invalid_neg, &mut dict_builder).is_err());
973
974 let invalid_offset = Value::Time { time_us: 0, offset_min: 1500 };
976 let mut writer = Writer::new();
977 assert!(encode_value(&mut writer, &invalid_offset, &mut dict_builder).is_err());
978 }
979
980 #[test]
981 fn test_datetime_validation() {
982 let mut dict_builder = DictionaryBuilder::new();
983
984 let invalid = Value::Datetime { epoch_us: 0, offset_min: 1500 };
986 let mut writer = Writer::new();
987 assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
988
989 let invalid_neg = Value::Datetime { epoch_us: 0, offset_min: -1500 };
990 let mut writer = Writer::new();
991 assert!(encode_value(&mut writer, &invalid_neg, &mut dict_builder).is_err());
992 }
993
994 #[test]
995 fn test_big_decimal_normalization_helpers() {
996 assert!(is_big_mantissa_zero(&[]));
998 assert!(is_big_mantissa_zero(&[0]));
999 assert!(is_big_mantissa_zero(&[0, 0, 0]));
1000 assert!(!is_big_mantissa_zero(&[1]));
1001 assert!(!is_big_mantissa_zero(&[0, 1]));
1002
1003 assert!(is_big_mantissa_divisible_by_10(&[0x0A])); assert!(is_big_mantissa_divisible_by_10(&[0x14])); assert!(is_big_mantissa_divisible_by_10(&[0x64])); assert!(is_big_mantissa_divisible_by_10(&[0x01, 0xF4])); assert!(!is_big_mantissa_divisible_by_10(&[0x01])); assert!(!is_big_mantissa_divisible_by_10(&[0x07])); assert!(!is_big_mantissa_divisible_by_10(&[0x0B])); assert!(!is_big_mantissa_divisible_by_10(&[0x15])); assert!(is_big_mantissa_divisible_by_10(&[0xF6])); assert!(is_big_mantissa_divisible_by_10(&[0xEC])); assert!(!is_big_mantissa_divisible_by_10(&[0xFF])); assert!(!is_big_mantissa_divisible_by_10(&[0xF9])); }
1025
1026 #[test]
1027 fn test_big_decimal_normalization_encode() {
1028 let valid = Value::Decimal {
1030 exponent: 0,
1031 mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x07])), unit: None,
1033 };
1034 let mut dict_builder = DictionaryBuilder::new();
1035 let mut writer = Writer::new();
1036 assert!(encode_value(&mut writer, &valid, &mut dict_builder).is_ok());
1037
1038 let invalid = Value::Decimal {
1040 exponent: 0,
1041 mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x0A])), unit: None,
1043 };
1044 let mut dict_builder = DictionaryBuilder::new();
1045 let mut writer = Writer::new();
1046 assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
1047
1048 let invalid_zero = Value::Decimal {
1050 exponent: 1,
1051 mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x00])),
1052 unit: None,
1053 };
1054 let mut dict_builder = DictionaryBuilder::new();
1055 let mut writer = Writer::new();
1056 assert!(encode_value(&mut writer, &invalid_zero, &mut dict_builder).is_err());
1057
1058 let valid_zero = Value::Decimal {
1060 exponent: 0,
1061 mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x00])),
1062 unit: None,
1063 };
1064 let mut dict_builder = DictionaryBuilder::new();
1065 let mut writer = Writer::new();
1066 assert!(encode_value(&mut writer, &valid_zero, &mut dict_builder).is_ok());
1067 }
1068
1069}