1use bytes::{Buf, BufMut, Bytes, BytesMut};
32
33pub const ALIGNMENT: usize = 8;
35
36pub const NULL_ROW_MARKER: u64 = u64::MAX;
39
40pub const MAX_ROWS_PER_ROWSET: u64 = 5 * 1024 * 1024;
42
43pub const MAX_VALUES_PER_ROW: u64 = 1024;
45
46pub const MAX_VALUE_LENGTH: u32 = 16 * 1024 * 1024;
48
49pub const MAX_ROWSET_SIZE: usize = crate::bus::packet::MAX_PART_SIZE as usize;
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58#[repr(u8)]
59pub enum ValueType {
60 Null = 0x02,
61 Int64 = 0x03,
62 Uint64 = 0x04,
63 Double = 0x05,
64 Boolean = 0x06,
65 String = 0x10,
66 Any = 0x11,
67 Composite = 0x12,
68}
69
70impl ValueType {
71 fn from_wire(value: u8) -> Option<Self> {
72 match value {
73 0x02 => Some(Self::Null),
74 0x03 => Some(Self::Int64),
75 0x04 => Some(Self::Uint64),
76 0x05 => Some(Self::Double),
77 0x06 => Some(Self::Boolean),
78 0x10 => Some(Self::String),
79 0x11 => Some(Self::Any),
80 0x12 => Some(Self::Composite),
81 _ => None,
82 }
83 }
84
85 pub fn is_string_like(self) -> bool {
94 matches!(self, Self::String | Self::Any | Self::Composite)
95 }
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub enum Value {
101 Null,
102 Int64(i64),
103 Uint64(u64),
104 Double(f64),
105 Boolean(bool),
106 String(Bytes),
107 Any(Bytes),
109 Composite(Bytes),
111}
112
113impl Value {
114 pub fn value_type(&self) -> ValueType {
115 match self {
116 Self::Null => ValueType::Null,
117 Self::Int64(_) => ValueType::Int64,
118 Self::Uint64(_) => ValueType::Uint64,
119 Self::Double(_) => ValueType::Double,
120 Self::Boolean(_) => ValueType::Boolean,
121 Self::String(_) => ValueType::String,
122 Self::Any(_) => ValueType::Any,
123 Self::Composite(_) => ValueType::Composite,
124 }
125 }
126
127 fn blob(&self) -> Option<&Bytes> {
128 match self {
129 Self::String(bytes) | Self::Any(bytes) | Self::Composite(bytes) => Some(bytes),
130 _ => None,
131 }
132 }
133
134 fn scalar(&self) -> Option<u64> {
136 match self {
137 Self::Int64(value) => Some(*value as u64),
138 Self::Uint64(value) => Some(*value),
139 Self::Double(value) => Some(value.to_bits()),
140 Self::Boolean(value) => Some(u64::from(*value)),
141 _ => None,
142 }
143 }
144
145 pub fn wire_size(&self) -> usize {
147 let payload = match self {
148 Self::Null => 0,
149 Self::String(bytes) | Self::Any(bytes) | Self::Composite(bytes) => {
150 bytes.len() + padding_for(bytes.len())
151 }
152 _ => ALIGNMENT,
153 };
154 ALIGNMENT + payload
155 }
156}
157
158#[derive(Debug, Clone, PartialEq)]
160pub struct UnversionedValue {
161 pub id: u16,
163 pub aggregate: bool,
165 pub value: Value,
166}
167
168impl UnversionedValue {
169 pub fn new(id: u16, value: Value) -> Self {
170 Self {
171 id,
172 aggregate: false,
173 value,
174 }
175 }
176
177 pub fn wire_size(&self) -> usize {
178 self.value.wire_size()
179 }
180}
181
182pub type Row = Vec<UnversionedValue>;
184
185pub type MaybeRow = Option<Row>;
189
190#[derive(Debug, thiserror::Error, PartialEq, Eq)]
192pub enum WireError {
193 #[error("rowset is truncated: need {needed} more bytes at offset {offset}")]
194 Truncated { offset: usize, needed: usize },
195 #[error("rowset declares {count} rows, more than the {MAX_ROWS_PER_ROWSET} allowed")]
196 TooManyRows { count: u64 },
197 #[error("row {row} declares {count} values, more than the {MAX_VALUES_PER_ROW} allowed")]
198 TooManyValues { row: usize, count: u64 },
199 #[error("value is {length} bytes, more than the {MAX_VALUE_LENGTH} allowed")]
200 ValueTooLong { length: u32 },
201 #[error("rowset is {size} bytes, more than the {MAX_ROWSET_SIZE}-byte RPC attachment limit")]
202 RowsetTooLarge { size: usize },
203 #[error("could not reserve {size} bytes for a rowset")]
204 AllocationFailed { size: usize },
205 #[error("unknown value type {0:#04x}")]
206 UnknownValueType(u8),
207 #[error("{0} bytes are left over after the last row")]
208 TrailingBytes(usize),
209}
210
211const fn padding_for(length: usize) -> usize {
212 (ALIGNMENT - (length % ALIGNMENT)) % ALIGNMENT
213}
214
215pub fn encoded_size(rows: &[MaybeRow]) -> usize {
217 let mut size = ALIGNMENT;
218 for row in rows {
219 size += ALIGNMENT;
220 if let Some(row) = row {
221 size += row.iter().map(UnversionedValue::wire_size).sum::<usize>();
222 }
223 }
224 size
225}
226
227pub fn encode_rowset(rows: &[MaybeRow]) -> Result<Bytes, WireError> {
236 let size = validate_and_measure(rows)?;
237 let mut storage = Vec::new();
240 storage
241 .try_reserve_exact(size)
242 .map_err(|_| WireError::AllocationFailed { size })?;
243 let mut buffer = BytesMut::from(Bytes::from(storage));
246 encode_rowset_unchecked(rows, &mut buffer);
247 Ok(buffer.freeze())
248}
249
250pub fn encode_rowset_into(rows: &[MaybeRow], out: &mut BytesMut) -> Result<(), WireError> {
254 validate_and_measure(rows)?;
255
256 encode_rowset_unchecked(rows, out);
257 Ok(())
258}
259
260fn validate_and_measure(rows: &[MaybeRow]) -> Result<usize, WireError> {
264 if rows.len() as u64 > MAX_ROWS_PER_ROWSET {
265 return Err(WireError::TooManyRows {
266 count: rows.len() as u64,
267 });
268 }
269
270 let mut size = ALIGNMENT;
271 for (index, row) in rows.iter().enumerate() {
272 add_to_rowset_size(&mut size, ALIGNMENT)?;
273 let Some(row) = row else {
274 continue;
275 };
276 if row.len() as u64 > MAX_VALUES_PER_ROW {
277 return Err(WireError::TooManyValues {
278 row: index,
279 count: row.len() as u64,
280 });
281 }
282 for value in row {
283 validate_value(value)?;
284 add_to_rowset_size(&mut size, value.wire_size())?;
285 }
286 }
287 Ok(size)
288}
289
290fn add_to_rowset_size(size: &mut usize, additional: usize) -> Result<(), WireError> {
291 *size = size
292 .checked_add(additional)
293 .ok_or(WireError::RowsetTooLarge { size: usize::MAX })?;
294 if *size > MAX_ROWSET_SIZE {
295 return Err(WireError::RowsetTooLarge { size: *size });
296 }
297 Ok(())
298}
299
300fn encode_rowset_unchecked(rows: &[MaybeRow], out: &mut BytesMut) {
301 out.put_u64_le(rows.len() as u64);
302 for row in rows {
303 let Some(row) = row else {
304 out.put_u64_le(NULL_ROW_MARKER);
305 continue;
306 };
307 out.put_u64_le(row.len() as u64);
308 for value in row {
309 encode_value_unchecked(value, out);
310 }
311 }
312}
313
314fn validate_value(value: &UnversionedValue) -> Result<(), WireError> {
315 let blob = value.value.blob();
316 if let Some(blob) = blob
317 && blob.len() as u64 > u64::from(MAX_VALUE_LENGTH)
318 {
319 return Err(WireError::ValueTooLong {
324 length: blob.len().min(u32::MAX as usize) as u32,
325 });
326 }
327 Ok(())
328}
329
330fn encode_value_unchecked(value: &UnversionedValue, out: &mut BytesMut) {
331 let blob = value.value.blob();
332
333 out.put_u16_le(value.id);
334 out.put_u8(value.value.value_type() as u8);
335 out.put_u8(u8::from(value.aggregate));
336 out.put_u32_le(blob.map_or(0, |bytes| bytes.len() as u32));
339
340 if let Some(scalar) = value.value.scalar() {
341 out.put_u64_le(scalar);
342 } else if let Some(blob) = blob {
343 out.put_slice(blob);
344 out.put_bytes(0, padding_for(blob.len()));
348 }
349}
350
351pub fn decode_rowset(input: &Bytes) -> Result<Vec<MaybeRow>, WireError> {
355 let mut reader = Reader { input, offset: 0 };
356
357 let row_count = reader.read_u64()?;
358 if row_count > MAX_ROWS_PER_ROWSET {
359 return Err(WireError::TooManyRows { count: row_count });
360 }
361
362 let mut rows = Vec::new();
365 for index in 0..row_count as usize {
366 let value_count = reader.read_u64()?;
367 if value_count == NULL_ROW_MARKER {
368 rows.push(None);
369 continue;
370 }
371 if value_count > MAX_VALUES_PER_ROW {
372 return Err(WireError::TooManyValues {
373 row: index,
374 count: value_count,
375 });
376 }
377 let mut row = Row::with_capacity(value_count as usize);
378 for _ in 0..value_count {
379 row.push(reader.read_value()?);
380 }
381 rows.push(Some(row));
382 }
383
384 if reader.offset != input.len() {
385 return Err(WireError::TrailingBytes(input.len() - reader.offset));
386 }
387
388 Ok(rows)
389}
390
391struct Reader<'a> {
392 input: &'a Bytes,
393 offset: usize,
394}
395
396impl Reader<'_> {
397 fn need(&self, count: usize) -> Result<(), WireError> {
398 if self.input.len() - self.offset < count {
399 return Err(WireError::Truncated {
400 offset: self.offset,
401 needed: count - (self.input.len() - self.offset),
402 });
403 }
404 Ok(())
405 }
406
407 fn read_u64(&mut self) -> Result<u64, WireError> {
408 self.need(8)?;
409 let mut slice = &self.input[self.offset..self.offset + 8];
410 self.offset += 8;
411 Ok(slice.get_u64_le())
412 }
413
414 fn read_value(&mut self) -> Result<UnversionedValue, WireError> {
415 self.need(8)?;
416 let header = &self.input[self.offset..self.offset + 8];
417 let id = u16::from_le_bytes(header[0..2].try_into().unwrap());
418 let raw_type = header[2];
419 let aggregate = header[3] != 0;
420 let length = u32::from_le_bytes(header[4..8].try_into().unwrap());
421 self.offset += 8;
422
423 let value_type =
424 ValueType::from_wire(raw_type).ok_or(WireError::UnknownValueType(raw_type))?;
425
426 let value = match value_type {
427 ValueType::Null => Value::Null,
428 ValueType::Int64 | ValueType::Uint64 | ValueType::Double | ValueType::Boolean => {
429 let word = self.read_u64()?;
430 match value_type {
431 ValueType::Int64 => Value::Int64(word as i64),
432 ValueType::Uint64 => Value::Uint64(word),
433 ValueType::Double => Value::Double(f64::from_bits(word)),
434 _ => Value::Boolean(word != 0),
436 }
437 }
438 ValueType::String | ValueType::Any | ValueType::Composite => {
439 if length > MAX_VALUE_LENGTH {
440 return Err(WireError::ValueTooLong { length });
441 }
442 let length = length as usize;
443 let padded = length + padding_for(length);
444 self.need(padded)?;
445 let blob = self.input.slice(self.offset..self.offset + length);
446 self.offset += padded;
447 match value_type {
448 ValueType::String => Value::String(blob),
449 ValueType::Any => Value::Any(blob),
450 _ => Value::Composite(blob),
451 }
452 }
453 };
454
455 Ok(UnversionedValue {
456 id,
457 aggregate,
458 value,
459 })
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 fn unwrap_encode(rows: &[MaybeRow]) -> Bytes {
469 encode_rowset(rows).expect("this rowset is within every limit")
470 }
471
472 fn round_trip(rows: &[MaybeRow]) -> Vec<MaybeRow> {
473 let encoded = unwrap_encode(rows);
474 assert_eq!(
475 encoded.len(),
476 encoded_size(rows),
477 "encoded_size disagrees with what encode_rowset wrote"
478 );
479 assert_eq!(
480 encoded.len() % ALIGNMENT,
481 0,
482 "a rowset is 8-byte aligned throughout"
483 );
484 decode_rowset(&encoded).expect("what this encoder wrote must decode")
485 }
486
487 fn sample_row() -> Row {
488 vec![
491 UnversionedValue::new(1, Value::Null),
492 UnversionedValue::new(2, Value::Boolean(true)),
493 UnversionedValue::new(3, Value::Boolean(false)),
494 UnversionedValue::new(4, Value::Int64(-42)),
495 UnversionedValue::new(5, Value::Uint64(42)),
496 UnversionedValue::new(6, Value::Double(1.25)),
497 UnversionedValue::new(7, Value::String(Bytes::from_static(b"foobar"))),
498 UnversionedValue::new(8, Value::Any(Bytes::from_static(b"[1;2;3]"))),
499 UnversionedValue::new(9, Value::String(Bytes::new())),
500 ]
501 }
502
503 #[test]
504 fn value_types_carry_the_documented_numbers() {
505 assert_eq!(ValueType::Null as u8, 0x02);
506 assert_eq!(ValueType::Int64 as u8, 0x03);
507 assert_eq!(ValueType::Uint64 as u8, 0x04);
508 assert_eq!(ValueType::Double as u8, 0x05);
509 assert_eq!(ValueType::Boolean as u8, 0x06);
510 assert_eq!(ValueType::String as u8, 0x10);
511 assert_eq!(ValueType::Any as u8, 0x11);
512 assert_eq!(ValueType::Composite as u8, 0x12);
513 }
514
515 #[test]
516 fn the_rowset_and_row_headers_are_single_words() {
517 let encoded = unwrap_encode(&[Some(vec![UnversionedValue::new(0, Value::Int64(7))])]);
518 assert_eq!(&encoded[0..8], &1u64.to_le_bytes(), "row count");
519 assert_eq!(&encoded[8..16], &1u64.to_le_bytes(), "value count");
520 assert_eq!(encoded.len(), 8 + 8 + 8 + 8);
521 }
522
523 #[test]
524 fn a_value_header_is_id_type_aggregate_length() {
525 let value = UnversionedValue {
526 id: 0x1234,
527 aggregate: true,
528 value: Value::String(Bytes::from_static(b"abc")),
529 };
530 let encoded = unwrap_encode(&[Some(vec![value])]);
531 let header = &encoded[16..24];
532 assert_eq!(&header[0..2], &0x1234u16.to_le_bytes(), "id");
533 assert_eq!(header[2], ValueType::String as u8, "type");
534 assert_eq!(header[3], 1, "aggregate");
535 assert_eq!(&header[4..8], &3u32.to_le_bytes(), "length");
536 assert_eq!(&encoded[24..27], b"abc");
537 assert_eq!(
538 &encoded[27..32],
539 &[0, 0, 0, 0, 0],
540 "padded to eight with zeroes"
541 );
542 }
543
544 #[test]
545 fn a_null_value_has_no_payload_at_all() {
546 let encoded = unwrap_encode(&[Some(vec![UnversionedValue::new(1, Value::Null)])]);
547 assert_eq!(encoded.len(), 24);
549 assert_eq!(
550 &encoded[20..24],
551 &0u32.to_le_bytes(),
552 "length word stays zero"
553 );
554 }
555
556 #[test]
557 fn scalars_occupy_exactly_one_word() {
558 for value in [
559 Value::Int64(-1),
560 Value::Uint64(u64::MAX),
561 Value::Double(-0.0),
562 Value::Boolean(true),
563 ] {
564 let encoded = unwrap_encode(&[Some(vec![UnversionedValue::new(0, value.clone())])]);
565 assert_eq!(
566 encoded.len(),
567 32,
568 "{value:?} should be header plus one word"
569 );
570 assert_eq!(
571 &encoded[20..24],
572 &0u32.to_le_bytes(),
573 "{value:?} must leave the length word zero"
574 );
575 }
576 }
577
578 #[test]
579 fn everything_round_trips() {
580 let rows = vec![None, Some(Vec::new()), Some(sample_row())];
581 assert_eq!(round_trip(&rows), rows);
582 }
583
584 #[test]
585 fn a_null_row_is_not_an_empty_row() {
586 let encoded_null = unwrap_encode(&[None]);
587 let encoded_empty = unwrap_encode(&[Some(Vec::new())]);
588 assert_ne!(encoded_null, encoded_empty);
589 assert_eq!(&encoded_null[8..16], &NULL_ROW_MARKER.to_le_bytes());
590 assert_eq!(&encoded_empty[8..16], &0u64.to_le_bytes());
591
592 assert_eq!(decode_rowset(&encoded_null).unwrap(), vec![None]);
593 assert_eq!(
594 decode_rowset(&encoded_empty).unwrap(),
595 vec![Some(Vec::new())]
596 );
597 }
598
599 #[test]
600 fn strings_of_every_length_modulo_eight_round_trip() {
601 for length in 0..24usize {
604 let blob = Bytes::from(vec![b'x'; length]);
605 let rows = vec![Some(vec![UnversionedValue::new(
606 0,
607 Value::String(blob.clone()),
608 )])];
609 let encoded = unwrap_encode(&rows);
610 assert_eq!(
611 encoded.len() % ALIGNMENT,
612 0,
613 "length {length} left the stream unaligned"
614 );
615 assert_eq!(
616 round_trip(&rows),
617 rows,
618 "length {length} did not round-trip"
619 );
620 }
621 }
622
623 #[test]
627 fn composite_values_keep_their_payload() {
628 let rows = vec![Some(vec![UnversionedValue::new(
629 3,
630 Value::Composite(Bytes::from_static(b"[1;2;3]")),
631 )])];
632 let decoded = round_trip(&rows);
633 assert_eq!(decoded, rows);
634 match &decoded[0].as_ref().unwrap()[0].value {
635 Value::Composite(blob) => assert_eq!(blob, &Bytes::from_static(b"[1;2;3]")),
636 other => panic!("expected a composite value, got {other:?}"),
637 }
638 }
639
640 #[test]
641 fn doubles_survive_bit_for_bit() {
642 for value in [
643 0.0,
644 -0.0,
645 1.25,
646 f64::MIN,
647 f64::MAX,
648 f64::INFINITY,
649 f64::NEG_INFINITY,
650 ] {
651 let rows = vec![Some(vec![UnversionedValue::new(0, Value::Double(value))])];
652 let decoded = round_trip(&rows);
653 match decoded[0].as_ref().unwrap()[0].value {
654 Value::Double(read) => assert_eq!(read.to_bits(), value.to_bits()),
655 ref other => panic!("expected a double, got {other:?}"),
656 }
657 }
658 let rows = vec![Some(vec![UnversionedValue::new(
660 0,
661 Value::Double(f64::NAN),
662 )])];
663 let encoded = unwrap_encode(&rows);
664 match decode_rowset(&encoded).unwrap()[0].as_ref().unwrap()[0].value {
665 Value::Double(read) => assert!(read.is_nan()),
666 ref other => panic!("expected a double, got {other:?}"),
667 }
668 }
669
670 #[test]
671 fn negative_integers_use_two_s_complement_in_the_word() {
672 let rows = vec![Some(vec![UnversionedValue::new(0, Value::Int64(-42))])];
673 let encoded = unwrap_encode(&rows);
674 assert_eq!(&encoded[24..32], &(-42i64 as u64).to_le_bytes());
675 assert_eq!(round_trip(&rows), rows);
676 }
677
678 #[test]
679 fn the_aggregate_flag_survives() {
680 let rows = vec![Some(vec![UnversionedValue {
681 id: 5,
682 aggregate: true,
683 value: Value::Int64(1),
684 }])];
685 assert_eq!(round_trip(&rows), rows);
686 }
687
688 #[test]
693 fn truncated_input_is_an_error_not_a_panic() {
694 let rows = vec![Some(sample_row())];
695 let whole = unwrap_encode(&rows);
696 for length in 0..whole.len() {
697 let truncated = whole.slice(0..length);
698 match decode_rowset(&truncated) {
699 Err(WireError::Truncated { offset, needed }) => {
700 assert!(
701 needed > 0,
702 "a truncation that needs no more bytes is not one"
703 );
704 assert!(
705 offset <= length,
706 "reported offset {offset} is past the {length} bytes given"
707 );
708 }
709 Err(other) => panic!("cut to {length} bytes gave {other:?}, not a truncation"),
710 Ok(rows) => panic!("a rowset cut to {length} bytes decoded to {rows:?}"),
711 }
712 }
713 }
714
715 #[test]
716 fn trailing_bytes_are_rejected() {
717 let mut encoded = BytesMut::from(&unwrap_encode(&[Some(sample_row())])[..]);
718 encoded.put_u64_le(0);
719 assert_eq!(
720 decode_rowset(&encoded.freeze()),
721 Err(WireError::TrailingBytes(8))
722 );
723 }
724
725 #[test]
726 fn an_unknown_value_type_is_rejected() {
727 let mut encoded = BytesMut::from(
728 &unwrap_encode(&[Some(vec![UnversionedValue::new(0, Value::Int64(1))])])[..],
729 );
730 encoded[18] = 0x7f;
731 assert_eq!(
732 decode_rowset(&encoded.freeze()),
733 Err(WireError::UnknownValueType(0x7f))
734 );
735 }
736
737 #[test]
738 fn an_absurd_row_count_is_rejected_before_anything_is_reserved() {
739 let mut buffer = BytesMut::new();
740 buffer.put_u64_le(MAX_ROWS_PER_ROWSET + 1);
741 assert_eq!(
742 decode_rowset(&buffer.freeze()),
743 Err(WireError::TooManyRows {
744 count: MAX_ROWS_PER_ROWSET + 1
745 })
746 );
747 }
748
749 #[test]
750 fn an_absurd_value_count_is_rejected() {
751 let mut buffer = BytesMut::new();
752 buffer.put_u64_le(1);
753 buffer.put_u64_le(MAX_VALUES_PER_ROW + 1);
754 assert_eq!(
755 decode_rowset(&buffer.freeze()),
756 Err(WireError::TooManyValues {
757 row: 0,
758 count: MAX_VALUES_PER_ROW + 1
759 })
760 );
761 }
762
763 #[test]
764 fn an_absurd_value_length_is_rejected_before_the_bytes_are_read() {
765 let mut buffer = BytesMut::new();
766 buffer.put_u64_le(1);
767 buffer.put_u64_le(1);
768 buffer.put_u16_le(0);
769 buffer.put_u8(ValueType::String as u8);
770 buffer.put_u8(0);
771 buffer.put_u32_le(MAX_VALUE_LENGTH + 1);
772 assert_eq!(
773 decode_rowset(&buffer.freeze()),
774 Err(WireError::ValueTooLong {
775 length: MAX_VALUE_LENGTH + 1
776 })
777 );
778 }
779
780 #[test]
784 fn a_large_but_legal_row_count_with_no_rows_behind_it_fails_cheaply() {
785 let mut buffer = BytesMut::new();
786 buffer.put_u64_le(MAX_ROWS_PER_ROWSET);
787 assert!(matches!(
788 decode_rowset(&buffer.freeze()),
789 Err(WireError::Truncated { .. })
790 ));
791 }
792
793 #[test]
797 fn the_encoder_refuses_what_the_decoder_would() {
798 let too_many_values = vec![Some(
799 (0..MAX_VALUES_PER_ROW as u16 + 1)
800 .map(|id| UnversionedValue::new(id, Value::Int64(0)))
801 .collect::<Row>(),
802 )];
803 assert_eq!(
804 encode_rowset(&too_many_values),
805 Err(WireError::TooManyValues {
806 row: 0,
807 count: MAX_VALUES_PER_ROW + 1
808 })
809 );
810
811 let too_long = vec![Some(vec![UnversionedValue::new(
812 0,
813 Value::String(Bytes::from(vec![0u8; MAX_VALUE_LENGTH as usize + 1])),
814 )])];
815 assert_eq!(
816 encode_rowset(&too_long),
817 Err(WireError::ValueTooLong {
818 length: MAX_VALUE_LENGTH + 1
819 })
820 );
821 }
822
823 #[test]
827 fn too_many_shared_large_values_are_refused_before_allocation() {
828 let shared = Bytes::from(vec![0; MAX_VALUE_LENGTH as usize]);
829 let row = (0..=MAX_VALUES_PER_ROW)
830 .map(|_| UnversionedValue::new(0, Value::String(shared.clone())))
831 .collect::<Row>();
832
833 assert_eq!(
834 encode_rowset(&[Some(row)]),
835 Err(WireError::TooManyValues {
836 row: 0,
837 count: MAX_VALUES_PER_ROW + 1,
838 })
839 );
840 }
841
842 #[test]
846 fn a_rowset_larger_than_one_rpc_attachment_is_refused_before_allocation() {
847 let shared = Bytes::from(vec![0; MAX_VALUE_LENGTH as usize]);
848 let row = (0..65)
849 .map(|_| UnversionedValue::new(0, Value::String(shared.clone())))
850 .collect::<Row>();
851
852 assert!(matches!(
853 encode_rowset(&[Some(row)]),
854 Err(WireError::RowsetTooLarge { .. })
855 ));
856 }
857
858 #[test]
859 fn a_rejected_encode_does_not_append_a_partial_rowset() {
860 let mut output = BytesMut::from(&b"prefix"[..]);
861 let rows = vec![Some(
862 (0..=MAX_VALUES_PER_ROW as u16)
863 .map(|id| UnversionedValue::new(id, Value::Int64(0)))
864 .collect::<Row>(),
865 )];
866
867 assert!(matches!(
868 encode_rowset_into(&rows, &mut output),
869 Err(WireError::TooManyValues { .. })
870 ));
871 assert_eq!(&output[..], b"prefix");
872 }
873
874 #[test]
875 fn a_big_rowset_round_trips() {
876 let rows: Vec<MaybeRow> = (0..1000)
877 .map(|index| {
878 Some(vec![
879 UnversionedValue::new(0, Value::Int64(index)),
880 UnversionedValue::new(1, Value::String(Bytes::from(format!("row {index}")))),
881 ])
882 })
883 .collect();
884 assert_eq!(round_trip(&rows), rows);
885 }
886}