1use std::fmt;
4
5use crate::{
6 CanonicalBytes, CanonicalList, CanonicalRecord, CanonicalString, CanonicalValue,
7 CanonicalVector, CurrencyCode, Date, Decimal, DecimalSpec, EnumTypeId, EnumVariantId, FieldId,
8 Money, Timestamp,
9 limits::{
10 MAX_ATOMIC_COMMAND_REQUEST_BYTES_V2, MAX_BYTES_VALUE_BYTES, MAX_CANONICAL_DOCUMENT_BYTES,
11 MAX_LIST_ENTRIES, MAX_NESTING_DEPTH, MAX_RECORD_FIELDS, MAX_STRING_BYTES,
12 },
13};
14
15#[path = "codec_decode_capacity.rs"]
16mod decode_capacity;
17pub use decode_capacity::*;
18
19pub const CANONICAL_VALUE_VERSION: u8 = 0x01;
21
22const TAG_NULL: u8 = 0x00;
23const TAG_BOOL: u8 = 0x01;
24const TAG_I64: u8 = 0x02;
25const TAG_U64: u8 = 0x03;
26const TAG_DECIMAL: u8 = 0x04;
27const TAG_MONEY: u8 = 0x05;
28const TAG_STRING: u8 = 0x06;
29const TAG_BYTES: u8 = 0x07;
30const TAG_TIMESTAMP: u8 = 0x08;
31const TAG_DATE: u8 = 0x09;
32const TAG_UUID: u8 = 0x0a;
33const TAG_ENUM: u8 = 0x0b;
34const TAG_LIST: u8 = 0x0c;
35const TAG_RECORD: u8 = 0x0d;
36const TAG_VECTOR: u8 = 0x0e;
37
38pub fn encode_canonical_value(value: &CanonicalValue) -> Result<Vec<u8>, CanonicalCodecError> {
43 let mut output = Vec::with_capacity(reserve_hint(canonical_value_encoded_len(value)));
44 encode_canonical_value_into(&mut output, value)?;
45 Ok(output)
46}
47
48const fn reserve_hint(length: Result<usize, CanonicalCodecError>) -> usize {
56 match length {
57 Ok(length) => length,
58 Err(_) => 0,
59 }
60}
61
62pub fn encode_canonical_value_into(
72 output: &mut Vec<u8>,
73 value: &CanonicalValue,
74) -> Result<(), CanonicalCodecError> {
75 let document_start = output.len();
76 let mut encoder = Encoder {
77 output: std::mem::take(output),
78 document_start,
79 maximum_document_bytes: MAX_CANONICAL_DOCUMENT_BYTES,
80 };
81 let result = encoder.encode_value(value, 0);
82 *output = encoder.output;
83 if result.is_err() {
84 output.truncate(document_start);
85 }
86 result
87}
88
89pub fn canonical_value_encoded_len(value: &CanonicalValue) -> Result<usize, CanonicalCodecError> {
95 let mut counter = LengthCounter::new(MAX_CANONICAL_DOCUMENT_BYTES);
96 counter.encode_value(value, 0)?;
97 Ok(counter.len)
98}
99
100pub fn canonical_record_encoded_len(
108 record: &CanonicalRecord,
109) -> Result<usize, CanonicalCodecError> {
110 let mut counter = LengthCounter::new(MAX_CANONICAL_DOCUMENT_BYTES);
111 counter.encode_record_value(record, 0)?;
112 Ok(counter.len)
113}
114
115pub fn encode_canonical_record(record: &CanonicalRecord) -> Result<Vec<u8>, CanonicalCodecError> {
122 let mut encoder = Encoder {
123 output: Vec::with_capacity(reserve_hint(canonical_record_encoded_len(record))),
124 document_start: 0,
125 maximum_document_bytes: MAX_CANONICAL_DOCUMENT_BYTES,
126 };
127 encoder.encode_record_value(record, 0)?;
128 Ok(encoder.output)
129}
130
131pub fn encode_canonical_record_into(
135 output: &mut Vec<u8>,
136 record: &CanonicalRecord,
137) -> Result<(), CanonicalCodecError> {
138 let document_start = output.len();
139 let mut encoder = Encoder {
140 output: std::mem::take(output),
141 document_start,
142 maximum_document_bytes: MAX_CANONICAL_DOCUMENT_BYTES,
143 };
144 let result = encoder.encode_record_value(record, 0);
145 *output = encoder.output;
146 if result.is_err() {
147 output.truncate(document_start);
148 }
149 result
150}
151
152pub fn canonical_command_input_encoded_len(
158 record: &CanonicalRecord,
159) -> Result<usize, CanonicalCodecError> {
160 let mut counter = LengthCounter::new(MAX_ATOMIC_COMMAND_REQUEST_BYTES_V2);
161 counter.encode_record_value(record, 0)?;
162 Ok(counter.len)
163}
164
165pub fn encode_canonical_command_input(
170 record: &CanonicalRecord,
171) -> Result<Vec<u8>, CanonicalCodecError> {
172 let mut encoder = Encoder {
173 output: Vec::with_capacity(reserve_hint(canonical_command_input_encoded_len(record))),
174 document_start: 0,
175 maximum_document_bytes: MAX_ATOMIC_COMMAND_REQUEST_BYTES_V2,
176 };
177 encoder.encode_record_value(record, 0)?;
178 Ok(encoder.output)
179}
180
181pub fn decode_canonical_value(input: &[u8]) -> Result<CanonicalValue, CanonicalCodecError> {
183 if input.len() > MAX_CANONICAL_DOCUMENT_BYTES {
184 return Err(CanonicalCodecError::DocumentTooLarge {
185 actual: input.len(),
186 maximum: MAX_CANONICAL_DOCUMENT_BYTES,
187 });
188 }
189
190 let mut decoder = Decoder { input, position: 0 };
191 let value = decoder.decode_value(0)?;
192 if decoder.position != input.len() {
193 return Err(CanonicalCodecError::TrailingBytes {
194 count: input.len() - decoder.position,
195 });
196 }
197 Ok(value)
198}
199
200struct Encoder {
201 output: Vec<u8>,
202 document_start: usize,
204 maximum_document_bytes: usize,
205}
206
207struct LengthCounter {
208 len: usize,
209 maximum_document_bytes: usize,
210}
211
212impl LengthCounter {
213 const fn new(maximum_document_bytes: usize) -> Self {
214 Self {
215 len: 0,
216 maximum_document_bytes,
217 }
218 }
219}
220
221impl Encoder {
222 fn validate_depth(depth: usize) -> Result<(), CanonicalCodecError> {
223 if depth > MAX_NESTING_DEPTH {
224 return Err(CanonicalCodecError::NestingTooDeep {
225 depth,
226 maximum: MAX_NESTING_DEPTH,
227 });
228 }
229 Ok(())
230 }
231
232 fn encode_value(
233 &mut self,
234 value: &CanonicalValue,
235 depth: usize,
236 ) -> Result<(), CanonicalCodecError> {
237 Self::validate_depth(depth)?;
238
239 self.write(&[CANONICAL_VALUE_VERSION])?;
240 match value {
241 CanonicalValue::Null => self.write(&[TAG_NULL]),
242 CanonicalValue::Bool(value) => self.write(&[TAG_BOOL, u8::from(*value)]),
243 CanonicalValue::I64(value) => {
244 self.write(&[TAG_I64])?;
245 self.write(&value.to_be_bytes())
246 }
247 CanonicalValue::U64(value) => {
248 self.write(&[TAG_U64])?;
249 self.write(&value.to_be_bytes())
250 }
251 CanonicalValue::Decimal(value) => {
252 self.write(&[TAG_DECIMAL])?;
253 self.encode_decimal(value)
254 }
255 CanonicalValue::Money(value) => {
256 self.write(&[TAG_MONEY])?;
257 self.write(value.currency().as_bytes())?;
258 self.encode_decimal(&value.amount())
259 }
260 CanonicalValue::String(value) => {
261 self.write(&[TAG_STRING])?;
262 self.encode_bounded_bytes(value.as_str().as_bytes(), BoundedKind::String)
263 }
264 CanonicalValue::Bytes(value) => {
265 self.write(&[TAG_BYTES])?;
266 self.encode_bounded_bytes(value.as_bytes(), BoundedKind::Bytes)
267 }
268 CanonicalValue::Timestamp(value) => {
269 self.write(&[TAG_TIMESTAMP])?;
270 self.write(&value.seconds().to_be_bytes())?;
271 self.write(&value.nanoseconds().to_be_bytes())
272 }
273 CanonicalValue::Date(value) => {
274 self.write(&[TAG_DATE])?;
275 self.write(&value.days_since_unix_epoch().to_be_bytes())
276 }
277 CanonicalValue::Uuid(value) => {
278 self.write(&[TAG_UUID])?;
279 self.write(value)
280 }
281 CanonicalValue::Enum {
282 type_id,
283 variant_id,
284 } => {
285 self.write(&[TAG_ENUM])?;
286 self.write(&type_id.get().to_be_bytes())?;
287 self.write(&variant_id.get().to_be_bytes())
288 }
289 CanonicalValue::List(values) => {
290 if values.len() > MAX_LIST_ENTRIES {
291 return Err(CanonicalCodecError::TooManyEntries {
292 kind: CollectionKind::List,
293 actual: values.len(),
294 maximum: MAX_LIST_ENTRIES,
295 });
296 }
297 self.write(&[TAG_LIST])?;
298 self.write(&(values.len() as u32).to_be_bytes())?;
299 for value in values.values() {
300 self.encode_value(value, depth + 1)?;
301 }
302 Ok(())
303 }
304 CanonicalValue::Record(record) => self.encode_record_payload(record, depth),
305 CanonicalValue::Vector(vector) => {
306 self.write(&[TAG_VECTOR])?;
307 self.write(&vector.dimension().to_be_bytes())?;
308 for component in vector.components() {
309 self.write(&component.to_be_bytes())?;
310 }
311 Ok(())
312 }
313 }
314 }
315
316 fn encode_record_value(
317 &mut self,
318 record: &CanonicalRecord,
319 depth: usize,
320 ) -> Result<(), CanonicalCodecError> {
321 Self::validate_depth(depth)?;
322 self.write(&[CANONICAL_VALUE_VERSION])?;
323 self.encode_record_payload(record, depth)
324 }
325
326 fn encode_record_payload(
327 &mut self,
328 record: &CanonicalRecord,
329 depth: usize,
330 ) -> Result<(), CanonicalCodecError> {
331 if record.len() > MAX_RECORD_FIELDS {
332 return Err(CanonicalCodecError::TooManyEntries {
333 kind: CollectionKind::Record,
334 actual: record.len(),
335 maximum: MAX_RECORD_FIELDS,
336 });
337 }
338 self.write(&[TAG_RECORD])?;
339 self.write(&(record.len() as u32).to_be_bytes())?;
340 let mut previous = None;
341 for (field_id, value) in record.fields() {
342 if previous.is_some_and(|id| id >= field_id.get()) {
343 return Err(CanonicalCodecError::NonCanonicalRecordOrder);
344 }
345 previous = Some(field_id.get());
346 self.write(&field_id.get().to_be_bytes())?;
347 self.encode_value(value, depth + 1)?;
348 }
349 Ok(())
350 }
351
352 fn encode_decimal(&mut self, value: &Decimal) -> Result<(), CanonicalCodecError> {
353 self.write(&[value.spec().precision(), value.spec().scale()])?;
354 self.write(&value.coefficient().to_be_bytes())
355 }
356
357 fn encode_bounded_bytes(
358 &mut self,
359 value: &[u8],
360 kind: BoundedKind,
361 ) -> Result<(), CanonicalCodecError> {
362 let maximum = match kind {
363 BoundedKind::String => MAX_STRING_BYTES,
364 BoundedKind::Bytes => MAX_BYTES_VALUE_BYTES,
365 };
366 if value.len() > maximum {
367 return Err(match kind {
368 BoundedKind::String => CanonicalCodecError::StringTooLarge {
369 actual: value.len(),
370 maximum,
371 },
372 BoundedKind::Bytes => CanonicalCodecError::BytesTooLarge {
373 actual: value.len(),
374 maximum,
375 },
376 });
377 }
378 self.write(&(value.len() as u32).to_be_bytes())?;
379 self.write(value)
380 }
381
382 fn write(&mut self, bytes: &[u8]) -> Result<(), CanonicalCodecError> {
383 let document_len = self.output.len().saturating_sub(self.document_start);
384 let new_length =
385 document_len
386 .checked_add(bytes.len())
387 .ok_or(CanonicalCodecError::DocumentTooLarge {
388 actual: usize::MAX,
389 maximum: self.maximum_document_bytes,
390 })?;
391 if new_length > self.maximum_document_bytes {
392 return Err(CanonicalCodecError::DocumentTooLarge {
393 actual: new_length,
394 maximum: self.maximum_document_bytes,
395 });
396 }
397 self.output.extend_from_slice(bytes);
398 Ok(())
399 }
400}
401
402impl LengthCounter {
403 fn validate_depth(depth: usize) -> Result<(), CanonicalCodecError> {
404 Encoder::validate_depth(depth)
405 }
406
407 fn encode_record_value(
409 &mut self,
410 record: &CanonicalRecord,
411 depth: usize,
412 ) -> Result<(), CanonicalCodecError> {
413 Self::validate_depth(depth)?;
414 self.add(1)?;
415 self.encode_record_payload(record, depth)
416 }
417
418 fn encode_value(
419 &mut self,
420 value: &CanonicalValue,
421 depth: usize,
422 ) -> Result<(), CanonicalCodecError> {
423 Self::validate_depth(depth)?;
424 self.add(1)?; match value {
426 CanonicalValue::Null => self.add(1),
427 CanonicalValue::Bool(_) => self.add(2),
428 CanonicalValue::I64(_) => self.add(1 + 8),
429 CanonicalValue::U64(_) => self.add(1 + 8),
430 CanonicalValue::Decimal(value) => {
431 self.add(1)?;
432 self.encode_decimal(value)
433 }
434 CanonicalValue::Money(value) => {
435 self.add(1 + 3)?;
436 self.encode_decimal(&value.amount())
437 }
438 CanonicalValue::String(value) => {
439 self.add(1)?;
440 self.encode_bounded_bytes(value.as_str().as_bytes(), BoundedKind::String)
441 }
442 CanonicalValue::Bytes(value) => {
443 self.add(1)?;
444 self.encode_bounded_bytes(value.as_bytes(), BoundedKind::Bytes)
445 }
446 CanonicalValue::Timestamp(_) => self.add(1 + 8 + 4),
447 CanonicalValue::Date(_) => self.add(1 + 4),
448 CanonicalValue::Uuid(_) => self.add(1 + 16),
449 CanonicalValue::Enum { .. } => self.add(1 + 4 + 4),
450 CanonicalValue::List(values) => {
451 if values.len() > MAX_LIST_ENTRIES {
452 return Err(CanonicalCodecError::TooManyEntries {
453 kind: CollectionKind::List,
454 actual: values.len(),
455 maximum: MAX_LIST_ENTRIES,
456 });
457 }
458 self.add(1 + 4)?;
459 for value in values.values() {
460 self.encode_value(value, depth + 1)?;
461 }
462 Ok(())
463 }
464 CanonicalValue::Record(record) => self.encode_record_payload(record, depth),
465 CanonicalValue::Vector(vector) => {
466 self.add(1 + 4 + (vector.dimension() as usize) * 4)?;
468 Ok(())
469 }
470 }
471 }
472
473 fn encode_record_payload(
474 &mut self,
475 record: &CanonicalRecord,
476 depth: usize,
477 ) -> Result<(), CanonicalCodecError> {
478 if record.len() > MAX_RECORD_FIELDS {
479 return Err(CanonicalCodecError::TooManyEntries {
480 kind: CollectionKind::Record,
481 actual: record.len(),
482 maximum: MAX_RECORD_FIELDS,
483 });
484 }
485 self.add(1 + 4)?;
486 let mut previous = None;
487 for (field_id, value) in record.fields() {
488 if previous.is_some_and(|id| id >= field_id.get()) {
489 return Err(CanonicalCodecError::NonCanonicalRecordOrder);
490 }
491 previous = Some(field_id.get());
492 self.add(4)?;
493 self.encode_value(value, depth + 1)?;
494 }
495 Ok(())
496 }
497
498 fn encode_decimal(&mut self, value: &Decimal) -> Result<(), CanonicalCodecError> {
499 let _ = value;
500 self.add(2 + 16)
501 }
502
503 fn encode_bounded_bytes(
504 &mut self,
505 value: &[u8],
506 kind: BoundedKind,
507 ) -> Result<(), CanonicalCodecError> {
508 let maximum = match kind {
509 BoundedKind::String => MAX_STRING_BYTES,
510 BoundedKind::Bytes => MAX_BYTES_VALUE_BYTES,
511 };
512 if value.len() > maximum {
513 return Err(match kind {
514 BoundedKind::String => CanonicalCodecError::StringTooLarge {
515 actual: value.len(),
516 maximum,
517 },
518 BoundedKind::Bytes => CanonicalCodecError::BytesTooLarge {
519 actual: value.len(),
520 maximum,
521 },
522 });
523 }
524 self.add(4 + value.len())
525 }
526
527 fn add(&mut self, bytes: usize) -> Result<(), CanonicalCodecError> {
528 let new_length =
529 self.len
530 .checked_add(bytes)
531 .ok_or(CanonicalCodecError::DocumentTooLarge {
532 actual: usize::MAX,
533 maximum: self.maximum_document_bytes,
534 })?;
535 if new_length > self.maximum_document_bytes {
536 return Err(CanonicalCodecError::DocumentTooLarge {
537 actual: new_length,
538 maximum: self.maximum_document_bytes,
539 });
540 }
541 self.len = new_length;
542 Ok(())
543 }
544}
545
546struct Decoder<'a> {
547 input: &'a [u8],
548 position: usize,
549}
550
551impl Decoder<'_> {
552 fn decode_value(&mut self, depth: usize) -> Result<CanonicalValue, CanonicalCodecError> {
553 if depth > MAX_NESTING_DEPTH {
554 return Err(CanonicalCodecError::NestingTooDeep {
555 depth,
556 maximum: MAX_NESTING_DEPTH,
557 });
558 }
559
560 let version = self.read_u8()?;
561 if version != CANONICAL_VALUE_VERSION {
562 return Err(CanonicalCodecError::UnsupportedVersion { version });
563 }
564 let tag = self.read_u8()?;
565 match tag {
566 TAG_NULL => Ok(CanonicalValue::Null),
567 TAG_BOOL => match self.read_u8()? {
568 0 => Ok(CanonicalValue::Bool(false)),
569 1 => Ok(CanonicalValue::Bool(true)),
570 value => Err(CanonicalCodecError::InvalidBoolean { value }),
571 },
572 TAG_I64 => Ok(CanonicalValue::I64(i64::from_be_bytes(self.read_array()?))),
573 TAG_U64 => Ok(CanonicalValue::U64(u64::from_be_bytes(self.read_array()?))),
574 TAG_DECIMAL => Ok(CanonicalValue::Decimal(self.decode_decimal()?)),
575 TAG_MONEY => {
576 let currency_bytes: [u8; 3] = self.read_array()?;
577 let currency_text = std::str::from_utf8(¤cy_bytes)
578 .map_err(|_| CanonicalCodecError::InvalidCurrency)?;
579 let currency = CurrencyCode::new(currency_text)
580 .map_err(|_| CanonicalCodecError::InvalidCurrency)?;
581 let amount = self.decode_decimal()?;
582 Ok(CanonicalValue::Money(Money::new(currency, amount)))
583 }
584 TAG_STRING => {
585 let bytes = self.decode_bounded_bytes(BoundedKind::String)?;
586 let value = std::str::from_utf8(bytes)
587 .map_err(|_| CanonicalCodecError::InvalidUtf8)?
588 .to_owned();
589 Ok(CanonicalValue::String(
590 CanonicalString::new(value).map_err(|_| {
591 CanonicalCodecError::StringTooLarge {
592 actual: bytes.len(),
593 maximum: MAX_STRING_BYTES,
594 }
595 })?,
596 ))
597 }
598 TAG_BYTES => {
599 let bytes = self.decode_bounded_bytes(BoundedKind::Bytes)?;
600 Ok(CanonicalValue::Bytes(
601 CanonicalBytes::new(bytes.to_vec()).map_err(|_| {
602 CanonicalCodecError::BytesTooLarge {
603 actual: bytes.len(),
604 maximum: MAX_BYTES_VALUE_BYTES,
605 }
606 })?,
607 ))
608 }
609 TAG_TIMESTAMP => {
610 let seconds = i64::from_be_bytes(self.read_array()?);
611 let nanos = u32::from_be_bytes(self.read_array()?);
612 Timestamp::new(seconds, nanos)
613 .map(CanonicalValue::Timestamp)
614 .map_err(|_| CanonicalCodecError::InvalidTimestamp)
615 }
616 TAG_DATE => Ok(CanonicalValue::Date(Date::from_days_since_unix_epoch(
617 i32::from_be_bytes(self.read_array()?),
618 ))),
619 TAG_UUID => Ok(CanonicalValue::Uuid(self.read_array()?)),
620 TAG_ENUM => {
621 let type_id = EnumTypeId::new(u32::from_be_bytes(self.read_array()?))
622 .ok_or(CanonicalCodecError::ZeroEnumTypeId)?;
623 let variant_id = EnumVariantId::new(u32::from_be_bytes(self.read_array()?))
624 .ok_or(CanonicalCodecError::ZeroEnumVariantId)?;
625 Ok(CanonicalValue::Enum {
626 type_id,
627 variant_id,
628 })
629 }
630 TAG_LIST => {
631 let count = self.read_collection_count(CollectionKind::List)?;
632 let mut values = Vec::with_capacity(count);
633 for _ in 0..count {
634 values.push(self.decode_value(depth + 1)?);
635 }
636 Ok(CanonicalValue::List(CanonicalList::new(values).map_err(
637 |_| CanonicalCodecError::TooManyEntries {
638 kind: CollectionKind::List,
639 actual: count,
640 maximum: MAX_LIST_ENTRIES,
641 },
642 )?))
643 }
644 TAG_RECORD => {
645 let count = self.read_collection_count(CollectionKind::Record)?;
646 let mut fields = Vec::with_capacity(count);
647 let mut previous = None;
648 for _ in 0..count {
649 let raw_field_id = u32::from_be_bytes(self.read_array()?);
650 if previous.is_some_and(|id| id >= raw_field_id) {
651 return Err(CanonicalCodecError::NonCanonicalRecordOrder);
652 }
653 previous = Some(raw_field_id);
654 let field_id =
655 FieldId::new(raw_field_id).ok_or(CanonicalCodecError::ZeroFieldId)?;
656 fields.push((field_id, self.decode_value(depth + 1)?));
657 }
658 Ok(CanonicalValue::Record(
659 CanonicalRecord::from_canonical_fields(fields),
660 ))
661 }
662 TAG_VECTOR => {
663 let dimension = u32::from_be_bytes(self.read_array()?);
664 if dimension == 0 || dimension > crate::MAX_VECTOR_DIMENSION {
665 return Err(CanonicalCodecError::VectorDimensionOutOfRange {
667 actual: dimension,
668 maximum: crate::MAX_VECTOR_DIMENSION,
669 });
670 }
671 let mut components = Vec::with_capacity(dimension as usize);
672 for index in 0..dimension as usize {
673 let bits = u32::from_be_bytes(self.read_array()?);
674 let component = f32::from_bits(bits);
675 if !component.is_finite() || bits == (-0.0_f32).to_bits() {
681 return Err(CanonicalCodecError::NonCanonicalVectorComponent { index });
682 }
683 components.push(component);
684 }
685 CanonicalVector::new(components)
690 .map(CanonicalValue::Vector)
691 .map_err(|error| match error {
692 crate::CanonicalVectorError::DimensionOutOfRange { .. } => {
693 CanonicalCodecError::VectorDimensionOutOfRange {
694 actual: dimension,
695 maximum: crate::MAX_VECTOR_DIMENSION,
696 }
697 }
698 crate::CanonicalVectorError::NonFiniteComponent { index } => {
699 CanonicalCodecError::NonCanonicalVectorComponent { index }
700 }
701 })
702 }
703 tag => Err(CanonicalCodecError::UnknownTag { tag }),
704 }
705 }
706
707 fn decode_decimal(&mut self) -> Result<Decimal, CanonicalCodecError> {
708 let precision = self.read_u8()?;
709 let scale = self.read_u8()?;
710 let coefficient = i128::from_be_bytes(self.read_array()?);
711 let spec =
712 DecimalSpec::new(precision, scale).map_err(|_| CanonicalCodecError::InvalidDecimal)?;
713 Decimal::new(spec, coefficient).map_err(|_| CanonicalCodecError::InvalidDecimal)
714 }
715
716 fn decode_bounded_bytes(&mut self, kind: BoundedKind) -> Result<&[u8], CanonicalCodecError> {
717 let length = u32::from_be_bytes(self.read_array()?) as usize;
718 let maximum = match kind {
719 BoundedKind::String => MAX_STRING_BYTES,
720 BoundedKind::Bytes => MAX_BYTES_VALUE_BYTES,
721 };
722 if length > maximum {
723 return Err(match kind {
724 BoundedKind::String => CanonicalCodecError::StringTooLarge {
725 actual: length,
726 maximum,
727 },
728 BoundedKind::Bytes => CanonicalCodecError::BytesTooLarge {
729 actual: length,
730 maximum,
731 },
732 });
733 }
734 self.read(length)
735 }
736
737 fn read_collection_count(
738 &mut self,
739 kind: CollectionKind,
740 ) -> Result<usize, CanonicalCodecError> {
741 let count = u32::from_be_bytes(self.read_array()?) as usize;
742 let maximum = match kind {
743 CollectionKind::List => MAX_LIST_ENTRIES,
744 CollectionKind::Record => MAX_RECORD_FIELDS,
745 };
746 if count > maximum {
747 return Err(CanonicalCodecError::TooManyEntries {
748 kind,
749 actual: count,
750 maximum,
751 });
752 }
753 Ok(count)
754 }
755
756 fn read_u8(&mut self) -> Result<u8, CanonicalCodecError> {
757 Ok(self.read(1)?[0])
758 }
759
760 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], CanonicalCodecError> {
761 self.read(N)?
762 .try_into()
763 .map_err(|_| CanonicalCodecError::UnexpectedEnd)
764 }
765
766 fn read(&mut self, length: usize) -> Result<&[u8], CanonicalCodecError> {
767 let end = self
768 .position
769 .checked_add(length)
770 .ok_or(CanonicalCodecError::UnexpectedEnd)?;
771 let value = self
772 .input
773 .get(self.position..end)
774 .ok_or(CanonicalCodecError::UnexpectedEnd)?;
775 self.position = end;
776 Ok(value)
777 }
778}
779
780#[derive(Clone, Copy, Debug, Eq, PartialEq)]
781enum BoundedKind {
782 String,
783 Bytes,
784}
785
786#[derive(Clone, Copy, Debug, Eq, PartialEq)]
788pub enum CollectionKind {
789 List,
791 Record,
793}
794
795#[derive(Clone, Debug, Eq, PartialEq)]
797pub enum CanonicalCodecError {
798 DocumentTooLarge {
800 actual: usize,
802 maximum: usize,
804 },
805 StringTooLarge {
807 actual: usize,
809 maximum: usize,
811 },
812 BytesTooLarge {
814 actual: usize,
816 maximum: usize,
818 },
819 TooManyEntries {
821 kind: CollectionKind,
823 actual: usize,
825 maximum: usize,
827 },
828 NestingTooDeep {
830 depth: usize,
832 maximum: usize,
834 },
835 UnsupportedVersion {
837 version: u8,
839 },
840 UnknownTag {
842 tag: u8,
844 },
845 VectorDimensionOutOfRange {
847 actual: u32,
849 maximum: u32,
851 },
852 NonCanonicalVectorComponent {
855 index: usize,
857 },
858 InvalidBoolean {
860 value: u8,
862 },
863 InvalidDecimal,
865 InvalidCurrency,
867 InvalidTimestamp,
869 InvalidUtf8,
871 NonCanonicalRecordOrder,
873 ZeroEnumTypeId,
875 ZeroEnumVariantId,
877 ZeroFieldId,
879 UnexpectedEnd,
881 TrailingBytes {
883 count: usize,
885 },
886}
887
888impl fmt::Display for CanonicalCodecError {
889 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
890 match self {
891 Self::DocumentTooLarge { actual, maximum } => write!(
892 formatter,
893 "canonical document has {actual} bytes; maximum is {maximum}"
894 ),
895 Self::StringTooLarge { actual, maximum } => {
896 write!(formatter, "string has {actual} bytes; maximum is {maximum}")
897 }
898 Self::BytesTooLarge { actual, maximum } => {
899 write!(
900 formatter,
901 "byte value has {actual} bytes; maximum is {maximum}"
902 )
903 }
904 Self::TooManyEntries {
905 kind,
906 actual,
907 maximum,
908 } => write!(
909 formatter,
910 "{kind:?} has {actual} entries; maximum is {maximum}"
911 ),
912 Self::NestingTooDeep { depth, maximum } => {
913 write!(formatter, "nesting depth is {depth}; maximum is {maximum}")
914 }
915 Self::UnsupportedVersion { version } => {
916 write!(formatter, "unsupported canonical value version {version}")
917 }
918 Self::UnknownTag { tag } => write!(formatter, "unknown canonical value tag {tag}"),
919 Self::VectorDimensionOutOfRange { actual, maximum } => write!(
920 formatter,
921 "vector dimension {actual} is outside 1..={maximum}"
922 ),
923 Self::NonCanonicalVectorComponent { index } => write!(
924 formatter,
925 "vector component {index} is not in canonical form (NaN, infinity, or -0.0)"
926 ),
927 Self::InvalidBoolean { value } => {
928 write!(formatter, "invalid Boolean payload {value}")
929 }
930 Self::InvalidDecimal => formatter.write_str("invalid canonical decimal"),
931 Self::InvalidCurrency => formatter.write_str("invalid canonical currency"),
932 Self::InvalidTimestamp => formatter.write_str("invalid canonical timestamp"),
933 Self::InvalidUtf8 => formatter.write_str("invalid canonical UTF-8"),
934 Self::NonCanonicalRecordOrder => {
935 formatter.write_str("record fields are not in canonical order")
936 }
937 Self::ZeroEnumTypeId => formatter.write_str("enum type ID must be nonzero"),
938 Self::ZeroEnumVariantId => formatter.write_str("enum variant ID must be nonzero"),
939 Self::ZeroFieldId => formatter.write_str("record field ID must be nonzero"),
940 Self::UnexpectedEnd => formatter.write_str("canonical document ended unexpectedly"),
941 Self::TrailingBytes { count } => {
942 write!(formatter, "canonical document has {count} trailing bytes")
943 }
944 }
945 }
946}
947
948impl std::error::Error for CanonicalCodecError {}
949
950#[cfg(test)]
951mod tests {
952 use super::*;
953
954 fn one_bytes_field(length: usize) -> CanonicalRecord {
955 CanonicalRecord::new(vec![(
956 FieldId::new(1).expect("nonzero field ID"),
957 CanonicalValue::bytes(vec![0xa5; length]).expect("bounded byte value"),
958 )])
959 .expect("bounded canonical record")
960 }
961
962 #[test]
963 fn borrowed_record_encoder_preserves_complete_value_record_bytes() {
964 let record = CanonicalRecord::new(vec![
965 (
966 FieldId::new(2).expect("field ID"),
967 CanonicalValue::Bool(true),
968 ),
969 (FieldId::new(1).expect("field ID"), CanonicalValue::U64(42)),
970 ])
971 .expect("canonical record");
972
973 assert_eq!(
974 encode_canonical_record(&record),
975 encode_canonical_value(&CanonicalValue::Record(record.clone()))
976 );
977 assert_eq!(
978 encode_canonical_record(&record).expect("borrowed encoding")[..2],
979 [CANONICAL_VALUE_VERSION, TAG_RECORD]
980 );
981 }
982
983 #[test]
984 fn atomic_command_input_envelope_is_additive_and_preserves_v1_bytes() {
985 let legacy = one_bytes_field(128);
986 assert_eq!(
987 encode_canonical_command_input(&legacy).expect("command input"),
988 encode_canonical_record(&legacy).expect("legacy record")
989 );
990
991 let large = CanonicalRecord::new(vec![
992 (
993 FieldId::new(1).expect("field ID"),
994 CanonicalValue::bytes(vec![0xa5; 900_000]).expect("first value"),
995 ),
996 (
997 FieldId::new(2).expect("field ID"),
998 CanonicalValue::bytes(vec![0x5a; 900_000]).expect("second value"),
999 ),
1000 ])
1001 .expect("large command input");
1002 assert!(matches!(
1003 encode_canonical_record(&large),
1004 Err(CanonicalCodecError::DocumentTooLarge {
1005 maximum: MAX_CANONICAL_DOCUMENT_BYTES,
1006 ..
1007 })
1008 ));
1009 let encoded = encode_canonical_command_input(&large).expect("large command input encode");
1010 assert_eq!(
1011 canonical_command_input_encoded_len(&large).expect("large command input length"),
1012 encoded.len()
1013 );
1014 assert!(encoded.len() > MAX_CANONICAL_DOCUMENT_BYTES);
1015 assert!(encoded.len() <= MAX_ATOMIC_COMMAND_REQUEST_BYTES_V2);
1016 }
1017
1018 #[test]
1019 fn borrowed_record_encoder_accepts_exact_document_maximum_and_rejects_one_over() {
1020 const RECORD_OVERHEAD: usize = 16;
1023 let exact = one_bytes_field(MAX_CANONICAL_DOCUMENT_BYTES - RECORD_OVERHEAD);
1024 assert_eq!(
1025 encode_canonical_record(&exact)
1026 .expect("exact maximum canonical record")
1027 .len(),
1028 MAX_CANONICAL_DOCUMENT_BYTES
1029 );
1030
1031 let over = one_bytes_field(MAX_CANONICAL_DOCUMENT_BYTES - RECORD_OVERHEAD + 1);
1032 assert_eq!(
1033 encode_canonical_record(&over),
1034 Err(CanonicalCodecError::DocumentTooLarge {
1035 actual: MAX_CANONICAL_DOCUMENT_BYTES + 1,
1036 maximum: MAX_CANONICAL_DOCUMENT_BYTES,
1037 })
1038 );
1039 }
1040
1041 #[test]
1042 fn vector_decode_rejects_non_canonical_bytes() {
1043 let zero_dimension = [1u8, TAG_VECTOR, 0, 0, 0, 0];
1045 assert_eq!(
1046 decode_canonical_value(&zero_dimension),
1047 Err(CanonicalCodecError::VectorDimensionOutOfRange {
1048 actual: 0,
1049 maximum: crate::MAX_VECTOR_DIMENSION,
1050 })
1051 );
1052 let mut oversized = vec![1u8, TAG_VECTOR];
1054 oversized.extend((crate::MAX_VECTOR_DIMENSION + 1).to_be_bytes());
1055 assert_eq!(
1056 decode_canonical_value(&oversized),
1057 Err(CanonicalCodecError::VectorDimensionOutOfRange {
1058 actual: crate::MAX_VECTOR_DIMENSION + 1,
1059 maximum: crate::MAX_VECTOR_DIMENSION,
1060 })
1061 );
1062 for (bits, index) in [
1064 (f32::NAN.to_bits(), 1u32),
1065 (f32::INFINITY.to_bits(), 1),
1066 (f32::NEG_INFINITY.to_bits(), 1),
1067 ((-0.0_f32).to_bits(), 1),
1068 ] {
1069 let mut document = vec![1u8, TAG_VECTOR, 0, 0, 0, 2];
1070 document.extend(1.0_f32.to_be_bytes());
1071 document.extend(bits.to_be_bytes());
1072 assert_eq!(
1073 decode_canonical_value(&document),
1074 Err(CanonicalCodecError::NonCanonicalVectorComponent {
1075 index: index as usize
1076 }),
1077 "bit pattern {bits:#010x} must be rejected"
1078 );
1079 }
1080 for nan_bits in [0x7fc0_0000u32, 0x7fc0_0001] {
1083 let mut document = vec![1u8, TAG_VECTOR, 0, 0, 0, 1];
1084 document.extend(nan_bits.to_be_bytes());
1085 assert!(decode_canonical_value(&document).is_err());
1086 }
1087 }
1088
1089 #[test]
1090 fn rejects_malformed_documents_without_panicking() {
1091 let cases: &[&[u8]] = &[
1092 &[],
1093 &[2, TAG_NULL],
1094 &[1, 0xff],
1095 &[1, TAG_BOOL, 2],
1096 &[1, TAG_U64],
1097 &[1, TAG_STRING, 0, 0, 0, 1, 0xff],
1098 &[1, TAG_NULL, 0],
1099 ];
1100 for case in cases {
1101 assert!(decode_canonical_value(case).is_err(), "accepted {case:?}");
1102 }
1103 }
1104
1105 #[test]
1106 fn encoded_len_matches_encode_across_variants_and_nesting() {
1107 use crate::{
1108 CurrencyCode, Date, Decimal, DecimalSpec, EnumTypeId, EnumVariantId, Money, Timestamp,
1109 };
1110
1111 let decimal = Decimal::new(DecimalSpec::new(10, 2).expect("spec"), 1_234).expect("decimal");
1112 let money = Money::new(CurrencyCode::new("USD").expect("currency"), decimal);
1113 let nested_record = CanonicalRecord::new(vec![
1114 (FieldId::new(1).expect("id"), CanonicalValue::U64(7)),
1115 (
1116 FieldId::new(3).expect("id"),
1117 CanonicalValue::String(CanonicalString::new("nested").expect("string")),
1118 ),
1119 ])
1120 .expect("record");
1121 let nested_list = CanonicalList::new(vec![
1122 CanonicalValue::Bool(true),
1123 CanonicalValue::I64(-9),
1124 CanonicalValue::Record(nested_record.clone()),
1125 ])
1126 .expect("list");
1127 let samples = [
1128 CanonicalValue::Null,
1129 CanonicalValue::Bool(false),
1130 CanonicalValue::Bool(true),
1131 CanonicalValue::I64(i64::MIN),
1132 CanonicalValue::I64(0),
1133 CanonicalValue::I64(i64::MAX),
1134 CanonicalValue::U64(0),
1135 CanonicalValue::U64(u64::MAX),
1136 CanonicalValue::Decimal(decimal),
1137 CanonicalValue::Money(money),
1138 CanonicalValue::String(CanonicalString::new("").expect("empty")),
1139 CanonicalValue::String(CanonicalString::new("hello ✓").expect("utf8")),
1140 CanonicalValue::Bytes(CanonicalBytes::new(vec![]).expect("empty bytes")),
1141 CanonicalValue::Bytes(CanonicalBytes::new(vec![0, 1, 2, 255]).expect("bytes")),
1142 CanonicalValue::Timestamp(Timestamp::new(1_700_000_000, 123).expect("ts")),
1143 CanonicalValue::Date(Date::from_days_since_unix_epoch(20_000)),
1144 CanonicalValue::Uuid([0x11; 16]),
1145 CanonicalValue::Enum {
1146 type_id: EnumTypeId::new(4).expect("type"),
1147 variant_id: EnumVariantId::new(2).expect("variant"),
1148 },
1149 CanonicalValue::List(CanonicalList::new(vec![]).expect("empty list")),
1150 CanonicalValue::List(nested_list),
1151 CanonicalValue::Record(CanonicalRecord::new(vec![]).expect("empty record")),
1152 CanonicalValue::Record(nested_record),
1153 CanonicalValue::Vector(CanonicalVector::new(vec![1.0]).expect("finite")),
1154 CanonicalValue::Vector(
1155 CanonicalVector::new(vec![0.0, -2.5, 3.25, f32::MIN, f32::MAX]).expect("finite"),
1156 ),
1157 ];
1158 for value in samples {
1159 let encoded = encode_canonical_value(&value).expect("encode");
1160 let len = canonical_value_encoded_len(&value).expect("length");
1161 assert_eq!(
1162 len,
1163 encoded.len(),
1164 "encoded_len diverged for {value:?}: len={len} encode={}",
1165 encoded.len()
1166 );
1167 let mut into = Vec::new();
1169 encode_canonical_value_into(&mut into, &value).expect("encode_into");
1170 assert_eq!(
1171 into, encoded,
1172 "encode_into diverged from encode_canonical_value for {value:?}"
1173 );
1174 let mut packed = vec![0xAA, 0xBB];
1176 encode_canonical_value_into(&mut packed, &value).expect("append encode_into");
1177 assert_eq!(&packed[2..], encoded.as_slice());
1178 assert_eq!(
1180 encoded.capacity(),
1181 encoded.len(),
1182 "encode_canonical_value over-or-under reserved for {value:?}"
1183 );
1184 }
1185 }
1186
1187 #[test]
1190 fn record_encoded_len_is_exact_and_reserves_once() {
1191 use crate::{
1192 CurrencyCode, Date, Decimal, DecimalSpec, EnumTypeId, EnumVariantId, Money, Timestamp,
1193 };
1194
1195 let decimal = Decimal::new(DecimalSpec::new(10, 2).expect("spec"), 1_234).expect("decimal");
1196 let inner = CanonicalRecord::new(vec![
1197 (FieldId::new(2).expect("id"), CanonicalValue::I64(-1)),
1198 (
1199 FieldId::new(5).expect("id"),
1200 CanonicalValue::Vector(CanonicalVector::new(vec![1.0, -2.0, 3.5]).expect("finite")),
1201 ),
1202 ])
1203 .expect("inner record");
1204 let records = [
1205 CanonicalRecord::new(vec![]).expect("empty record"),
1206 CanonicalRecord::new(vec![(FieldId::new(1).expect("id"), CanonicalValue::Null)])
1207 .expect("single null"),
1208 CanonicalRecord::new(vec![
1209 (FieldId::new(1).expect("id"), CanonicalValue::Uuid([7; 16])),
1210 (
1211 FieldId::new(2).expect("id"),
1212 CanonicalValue::String(
1213 CanonicalString::new("wide title ✓".repeat(24)).expect("string"),
1214 ),
1215 ),
1216 (
1217 FieldId::new(3).expect("id"),
1218 CanonicalValue::Timestamp(Timestamp::new(1_700_000_000, 7).expect("ts")),
1219 ),
1220 (
1221 FieldId::new(4).expect("id"),
1222 CanonicalValue::Decimal(decimal),
1223 ),
1224 (
1225 FieldId::new(5).expect("id"),
1226 CanonicalValue::Money(Money::new(
1227 CurrencyCode::new("EUR").expect("currency"),
1228 decimal,
1229 )),
1230 ),
1231 (
1232 FieldId::new(6).expect("id"),
1233 CanonicalValue::Date(Date::from_days_since_unix_epoch(19_000)),
1234 ),
1235 (
1236 FieldId::new(7).expect("id"),
1237 CanonicalValue::Enum {
1238 type_id: EnumTypeId::new(3).expect("type"),
1239 variant_id: EnumVariantId::new(1).expect("variant"),
1240 },
1241 ),
1242 (
1243 FieldId::new(8).expect("id"),
1244 CanonicalValue::Bytes(CanonicalBytes::new(vec![9; 300]).expect("bytes")),
1245 ),
1246 (
1247 FieldId::new(9).expect("id"),
1248 CanonicalValue::List(
1249 CanonicalList::new(vec![
1250 CanonicalValue::Record(inner.clone()),
1251 CanonicalValue::Bool(true),
1252 ])
1253 .expect("list"),
1254 ),
1255 ),
1256 ])
1257 .expect("wide record"),
1258 ];
1259 for record in records {
1260 let encoded = encode_canonical_record(&record).expect("encode");
1261 let len = canonical_record_encoded_len(&record).expect("length");
1262 assert_eq!(len, encoded.len(), "record length diverged from encode");
1263 assert_eq!(
1264 encoded.capacity(),
1265 encoded.len(),
1266 "encode_canonical_record grew or over-reserved its buffer"
1267 );
1268 let as_value = encode_canonical_value(&CanonicalValue::Record(record.clone()))
1270 .expect("value encode");
1271 assert_eq!(as_value, encoded, "record and value encodings diverged");
1272 assert_eq!(
1273 canonical_value_encoded_len(&CanonicalValue::Record(record)).expect("value length"),
1274 len,
1275 "record and value lengths diverged"
1276 );
1277 }
1278 }
1279
1280 #[test]
1283 fn encode_into_byte_identity_detects_divergence() {
1284 let value = CanonicalValue::I64(42);
1285 let expected = encode_canonical_value(&value).expect("encode");
1286 let mut into = Vec::new();
1287 encode_canonical_value_into(&mut into, &value).expect("encode_into");
1288 assert_eq!(into, expected);
1289 let mut corrupt = into.clone();
1291 corrupt.push(0xFF);
1292 assert_ne!(
1293 corrupt, expected,
1294 "appended garbage must break byte-identity"
1295 );
1296 }
1297
1298 #[test]
1302 fn failed_encode_into_leaves_the_buffer_untouched() {
1303 const RECORD_OVERHEAD: usize = 16;
1304 let over = CanonicalValue::Record(one_bytes_field(
1305 MAX_CANONICAL_DOCUMENT_BYTES - RECORD_OVERHEAD + 1,
1306 ));
1307 let prefix = vec![0xAA, 0xBB, 0xCC];
1308 let mut buffer = prefix.clone();
1309 let error = encode_canonical_value_into(&mut buffer, &over)
1310 .expect_err("oversized document must be rejected");
1311 assert_eq!(
1312 error,
1313 CanonicalCodecError::DocumentTooLarge {
1314 actual: MAX_CANONICAL_DOCUMENT_BYTES + 1,
1315 maximum: MAX_CANONICAL_DOCUMENT_BYTES,
1316 }
1317 );
1318 assert_eq!(
1319 buffer, prefix,
1320 "failed encode must restore the caller's buffer exactly"
1321 );
1322
1323 encode_canonical_value_into(&mut buffer, &CanonicalValue::I64(7))
1325 .expect("encode after err");
1326 assert_eq!(
1327 &buffer[..3],
1328 prefix.as_slice(),
1329 "prefix must survive the recovery append"
1330 );
1331 assert_eq!(
1332 &buffer[3..],
1333 encode_canonical_value(&CanonicalValue::I64(7))
1334 .expect("encode")
1335 .as_slice()
1336 );
1337 }
1338}