grc_20/codec/
value.rs

1//! Value encoding/decoding for GRC-20 binary format.
2//!
3//! Implements the wire format for property values (spec Section 6.5).
4
5use 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
15// =============================================================================
16// DECODING
17// =============================================================================
18
19/// Decodes a Value from the reader based on the data type (zero-copy).
20pub 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            // Validate minimal encoding
102            if !bytes.is_empty() {
103                let first = bytes[0];
104                // Check for redundant sign extension
105                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    // Validate normalization
124    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
163/// Checks if a big-endian two's complement mantissa represents zero.
164fn is_big_mantissa_zero(bytes: &[u8]) -> bool {
165    bytes.iter().all(|&b| b == 0)
166}
167
168/// Checks if a big-endian two's complement mantissa is divisible by 10.
169///
170/// A number is divisible by 10 if its remainder when divided by 10 is 0.
171/// For big-endian bytes, we compute: sum(byte[i] * 256^(n-1-i)) mod 10.
172/// Since 256 mod 10 = 6, we can compute iteratively: (carry * 6 + byte) mod 10.
173///
174/// For negative numbers (high bit set), we need to handle two's complement.
175fn is_big_mantissa_divisible_by_10(bytes: &[u8]) -> bool {
176    if bytes.is_empty() {
177        return true; // Zero is divisible by 10
178    }
179
180    // Check if negative (high bit set)
181    let is_negative = bytes[0] & 0x80 != 0;
182
183    if is_negative {
184        // For negative two's complement, compute the absolute value first
185        // by inverting bits and adding 1, then check divisibility
186        let abs_mod = twos_complement_abs_mod_10(bytes);
187        abs_mod == 0
188    } else {
189        // Positive: just compute mod 10 directly
190        // 256 mod 10 = 6, so we iterate: remainder = (remainder * 6 + byte) mod 10
191        let mut remainder = 0u32;
192        for &byte in bytes {
193            // remainder * 256 + byte, mod 10
194            // Since 256 = 25 * 10 + 6, we have: (r * 256) mod 10 = (r * 6) mod 10
195            remainder = (remainder * 6 + byte as u32) % 10;
196        }
197        remainder == 0
198    }
199}
200
201/// Computes |x| mod 10 for a negative two's complement number.
202fn twos_complement_abs_mod_10(bytes: &[u8]) -> u32 {
203    // Two's complement negation: invert all bits and add 1
204    // To get |x| mod 10, we compute (-x) mod 10
205    //
206    // For a two's complement negative number x (represented in bytes),
207    // -x = ~x + 1 (bit inversion plus one)
208    //
209    // We compute (inverted bytes) mod 10, then add 1 mod 10
210
211    // First, compute (inverted bytes as big-endian unsigned) mod 10
212    let mut remainder = 0u32;
213    for &byte in bytes {
214        let inverted = !byte;
215        remainder = (remainder * 6 + inverted as u32) % 10;
216    }
217
218    // Add 1 (for two's complement)
219    (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, // +1 for index 0
235            });
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    // DATE: 6 bytes (int32 days + int16 offset_min), little-endian
258    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    // Validate offset_min range
263    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    // TIME: 8 bytes (int48 time_us + int16 offset_min), little-endian
274    let bytes = reader.read_bytes(8, "time")?;
275
276    // Read int48 as 6 bytes, sign-extend to i64
277    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    // Sign-extend from 48 bits
281    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    // Validate time_us range
290    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    // Validate offset_min range
297    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    // DATETIME: 10 bytes (int64 epoch_us + int16 offset_min), little-endian
308    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    // Validate offset_min range
316    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    // RFC 5545 iCalendar format - basic validation
328    // Full validation would require a complete iCalendar parser
329    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    // Read in wire order: longitude, latitude, altitude (optional)
342    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    // Validate bounds
351    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    // Validate no NaN in float32 embeddings
395    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    // Validate binary embedding has zeros in unused bits
405    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
419/// Decodes a PropertyValue (property index + value + optional language).
420pub 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
439// =============================================================================
440// ENCODING
441// =============================================================================
442
443/// Encodes a Value to the writer.
444pub 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            // Validate offset_min range
481            if *offset_min < -1440 || *offset_min > 1440 {
482                return Err(EncodeError::InvalidInput {
483                    context: "DATE offset_min outside range [-1440, +1440]",
484                });
485            }
486            // DATE: 6 bytes (int32 days + int16 offset_min), little-endian
487            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            // Validate time_us range
492            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            // Validate offset_min range
498            if *offset_min < -1440 || *offset_min > 1440 {
499                return Err(EncodeError::InvalidInput {
500                    context: "TIME offset_min outside range [-1440, +1440]",
501                });
502            }
503            // TIME: 8 bytes (int48 time_us + int16 offset_min), little-endian
504            // Write int48 as 6 bytes
505            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            // Validate offset_min range
511            if *offset_min < -1440 || *offset_min > 1440 {
512                return Err(EncodeError::InvalidInput {
513                    context: "DATETIME offset_min outside range [-1440, +1440]",
514                });
515            }
516            // DATETIME: 10 bytes (int64 epoch_us + int16 offset_min), little-endian
517            writer.write_bytes(&epoch_us.to_le_bytes());
518            writer.write_bytes(&offset_min.to_le_bytes());
519        }
520        Value::Schedule(s) => {
521            // RFC 5545 iCalendar format
522            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            // Write ordinate_count: 2 for 2D, 3 for 3D
540            let ordinate_count = if alt.is_some() { 3u8 } else { 2u8 };
541            writer.write_byte(ordinate_count);
542            // Write in wire order: longitude, latitude, altitude (optional)
543            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            // Check for NaN in float32
559            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    // Validate normalization
581    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
619/// Encodes a PropertyValue (property index + value + optional language).
620pub 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
632/// Validates a position string according to spec rules.
633pub 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
645/// Decodes a position string with validation (zero-copy).
646pub 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        // Build dicts for decoding
723        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        // Compare inner values since one is Owned and one is Borrowed
729        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        // 2D point (no altitude)
741        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        // 3D point (with altitude)
754        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        // Latitude out of range
769        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        // Longitude out of range
776        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        // NaN in altitude
783        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        // Simple iCalendar event (single occurrence)
796        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]), // 4 dims * 4 bytes
818        };
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        // Compare inner values since one is Owned and one is Borrowed
829        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        // Valid: 12.34 = 1234 * 10^-2
845        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        // Invalid: has trailing zeros
855        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        // Test various date values
871        let test_cases = [
872            (0, 0),           // Unix epoch, UTC
873            (19797, 0),       // March 15, 2024 UTC
874            (19797, 330),     // March 15, 2024 +05:30
875            (-36524, 0),      // 100 BCE
876            (i32::MAX, 0),    // Far future
877            (i32::MIN, 0),    // Far past
878        ];
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        // Test various time values
899        let test_cases = [
900            (0, 0),                      // Midnight UTC
901            (52_200_000_000, 0),         // 14:30:00 UTC
902            (52_200_500_000, 330),       // 14:30:00.500 +05:30
903            (86_399_999_999, 0),         // 23:59:59.999999 UTC
904            (0, -300),                   // Midnight -05:00
905        ];
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        // Test various datetime values
926        let test_cases = [
927            (0, 0),                          // Unix epoch UTC
928            (1_710_513_000_000_000, 0),      // 2024-03-15T14:30:00Z
929            (1_710_493_200_000_000, 330),    // 2024-03-15T14:30:00+05:30
930            (-1_000_000_000_000, 0),         // Before epoch
931            (i64::MAX / 2, 0),               // Far future (within safe range)
932        ];
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        // DATE should reject offset_min outside range
952        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        // TIME should reject time_us outside range
966        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        // TIME should reject offset_min outside range
975        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        // DATETIME should reject offset_min outside range
985        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        // Test is_big_mantissa_zero
997        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        // Test is_big_mantissa_divisible_by_10 for positive numbers
1004        // 10 in big-endian = [0x0A]
1005        assert!(is_big_mantissa_divisible_by_10(&[0x0A])); // 10
1006        assert!(is_big_mantissa_divisible_by_10(&[0x14])); // 20
1007        assert!(is_big_mantissa_divisible_by_10(&[0x64])); // 100
1008        assert!(is_big_mantissa_divisible_by_10(&[0x01, 0xF4])); // 500
1009
1010        assert!(!is_big_mantissa_divisible_by_10(&[0x01])); // 1
1011        assert!(!is_big_mantissa_divisible_by_10(&[0x07])); // 7
1012        assert!(!is_big_mantissa_divisible_by_10(&[0x0B])); // 11
1013        assert!(!is_big_mantissa_divisible_by_10(&[0x15])); // 21
1014
1015        // Test negative numbers (two's complement)
1016        // -10 in two's complement (1 byte): 0xF6
1017        assert!(is_big_mantissa_divisible_by_10(&[0xF6])); // -10
1018        // -20 in two's complement (1 byte): 0xEC
1019        assert!(is_big_mantissa_divisible_by_10(&[0xEC])); // -20
1020        // -1 in two's complement (1 byte): 0xFF
1021        assert!(!is_big_mantissa_divisible_by_10(&[0xFF])); // -1
1022        // -7 in two's complement (1 byte): 0xF9
1023        assert!(!is_big_mantissa_divisible_by_10(&[0xF9])); // -7
1024    }
1025
1026    #[test]
1027    fn test_big_decimal_normalization_encode() {
1028        // Valid: mantissa not divisible by 10
1029        let valid = Value::Decimal {
1030            exponent: 0,
1031            mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x07])), // 7
1032            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        // Invalid: mantissa is 10 (divisible by 10)
1039        let invalid = Value::Decimal {
1040            exponent: 0,
1041            mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x0A])), // 10
1042            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        // Invalid: zero mantissa with non-zero exponent
1049        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        // Valid: zero mantissa with zero exponent
1059        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}