1use crate::coding::{checked_count, checked_decode_count};
29use crate::error::{EncodingError, Result};
30
31const VALUE_TYPE_I64: u8 = 0;
33const VALUE_TYPE_U64: u8 = 1;
34const VALUE_TYPE_F64: u8 = 2;
35const VALUE_TYPE_BOOL: u8 = 3;
36const VALUE_TYPE_STRING: u8 = 4;
37
38pub struct RleEncoder;
44
45pub struct RleDecoder;
47
48impl RleEncoder {
53 pub fn encode_i64(values: &[i64]) -> Result<Vec<u8>> {
60 if values.is_empty() {
61 return Err(EncodingError::EmptyInput {
62 context: "RLE i64 encoder",
63 });
64 }
65 let num_values = checked_count(values.len())?;
66 let runs = build_runs(values, |a, b| a == b);
67 let num_runs = checked_count(runs.len())?;
68
69 let mut buf = Vec::with_capacity(9 + runs.len() * 12);
70 buf.push(VALUE_TYPE_I64);
71 buf.extend_from_slice(&num_values.to_le_bytes());
72 buf.extend_from_slice(&num_runs.to_le_bytes());
73 for (val, run_len) in &runs {
74 buf.extend_from_slice(&val.to_le_bytes());
75 buf.extend_from_slice(&run_len.to_le_bytes());
76 }
77 Ok(buf)
78 }
79
80 pub fn encode_u64(values: &[u64]) -> Result<Vec<u8>> {
86 if values.is_empty() {
87 return Err(EncodingError::EmptyInput {
88 context: "RLE u64 encoder",
89 });
90 }
91 let num_values = checked_count(values.len())?;
92 let runs = build_runs(values, |a, b| a == b);
93 let num_runs = checked_count(runs.len())?;
94
95 let mut buf = Vec::with_capacity(9 + runs.len() * 12);
96 buf.push(VALUE_TYPE_U64);
97 buf.extend_from_slice(&num_values.to_le_bytes());
98 buf.extend_from_slice(&num_runs.to_le_bytes());
99 for (val, run_len) in &runs {
100 buf.extend_from_slice(&val.to_le_bytes());
101 buf.extend_from_slice(&run_len.to_le_bytes());
102 }
103 Ok(buf)
104 }
105
106 pub fn encode_f64(values: &[f64]) -> Result<Vec<u8>> {
114 if values.is_empty() {
115 return Err(EncodingError::EmptyInput {
116 context: "RLE f64 encoder",
117 });
118 }
119 let num_values = checked_count(values.len())?;
120 let runs = build_runs(values, |a, b| a.to_bits() == b.to_bits());
121 let num_runs = checked_count(runs.len())?;
122
123 let mut buf = Vec::with_capacity(9 + runs.len() * 12);
124 buf.push(VALUE_TYPE_F64);
125 buf.extend_from_slice(&num_values.to_le_bytes());
126 buf.extend_from_slice(&num_runs.to_le_bytes());
127 for (val, run_len) in &runs {
128 buf.extend_from_slice(&val.to_bits().to_le_bytes());
129 buf.extend_from_slice(&run_len.to_le_bytes());
130 }
131 Ok(buf)
132 }
133
134 pub fn encode_bool(values: &[bool]) -> Result<Vec<u8>> {
140 if values.is_empty() {
141 return Err(EncodingError::EmptyInput {
142 context: "RLE bool encoder",
143 });
144 }
145 let num_values = checked_count(values.len())?;
146 let runs = build_runs(values, |a, b| a == b);
147 let num_runs = checked_count(runs.len())?;
148
149 let mut buf = Vec::with_capacity(9 + runs.len() * 5);
150 buf.push(VALUE_TYPE_BOOL);
151 buf.extend_from_slice(&num_values.to_le_bytes());
152 buf.extend_from_slice(&num_runs.to_le_bytes());
153 for (val, run_len) in &runs {
154 buf.push(u8::from(*val));
155 buf.extend_from_slice(&run_len.to_le_bytes());
156 }
157 Ok(buf)
158 }
159
160 pub fn encode_string(values: &[&str]) -> Result<Vec<u8>> {
166 if values.is_empty() {
167 return Err(EncodingError::EmptyInput {
168 context: "RLE string encoder",
169 });
170 }
171 let num_values = checked_count(values.len())?;
172 let runs = build_runs(values, |a, b| a == b);
173 let num_runs = checked_count(runs.len())?;
174
175 let mut buf = Vec::with_capacity(9 + runs.len() * 16);
176 buf.push(VALUE_TYPE_STRING);
177 buf.extend_from_slice(&num_values.to_le_bytes());
178 buf.extend_from_slice(&num_runs.to_le_bytes());
179 for (val, run_len) in &runs {
180 let s_bytes = val.as_bytes();
181 let s_len = checked_count(s_bytes.len())?;
182 buf.extend_from_slice(&s_len.to_le_bytes());
183 buf.extend_from_slice(s_bytes);
184 buf.extend_from_slice(&run_len.to_le_bytes());
185 }
186 Ok(buf)
187 }
188}
189
190impl RleDecoder {
195 pub fn value_type(data: &[u8]) -> Result<u8> {
206 if data.is_empty() {
207 return Err(corrupt("RLE payload empty — no value type byte"));
208 }
209 Ok(data[0])
210 }
211
212 pub fn decode_i64(data: &[u8]) -> Result<Vec<i64>> {
219 let (num_values, num_runs, mut pos) = read_header(data)?;
220 let mut result = Vec::with_capacity(num_values);
221 for _ in 0..num_runs {
222 if pos + 12 > data.len() {
223 return Err(corrupt("i64 run data truncated"));
224 }
225 let val = i64::from_le_bytes(
226 data[pos..pos + 8]
227 .try_into()
228 .map_err(|_| corrupt("i64 value bytes"))?,
229 );
230 pos += 8;
231 let run_len = u32::from_le_bytes(
232 data[pos..pos + 4]
233 .try_into()
234 .map_err(|_| corrupt("i64 run length bytes"))?,
235 ) as usize;
236 pos += 4;
237 checked_run(result.len(), run_len, num_values)?;
238 result.extend(std::iter::repeat_n(val, run_len));
239 }
240 verify_count(num_values, result.len())?;
241 Ok(result)
242 }
243
244 pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
250 let (num_values, num_runs, mut pos) = read_header(data)?;
251 let mut result = Vec::with_capacity(num_values);
252 for _ in 0..num_runs {
253 if pos + 12 > data.len() {
254 return Err(corrupt("u64 run data truncated"));
255 }
256 let val = u64::from_le_bytes(
257 data[pos..pos + 8]
258 .try_into()
259 .map_err(|_| corrupt("u64 value bytes"))?,
260 );
261 pos += 8;
262 let run_len = u32::from_le_bytes(
263 data[pos..pos + 4]
264 .try_into()
265 .map_err(|_| corrupt("u64 run length bytes"))?,
266 ) as usize;
267 pos += 4;
268 checked_run(result.len(), run_len, num_values)?;
269 result.extend(std::iter::repeat_n(val, run_len));
270 }
271 verify_count(num_values, result.len())?;
272 Ok(result)
273 }
274
275 pub fn decode_f64(data: &[u8]) -> Result<Vec<f64>> {
281 let (num_values, num_runs, mut pos) = read_header(data)?;
282 let mut result = Vec::with_capacity(num_values);
283 for _ in 0..num_runs {
284 if pos + 12 > data.len() {
285 return Err(corrupt("f64 run data truncated"));
286 }
287 let bits = u64::from_le_bytes(
288 data[pos..pos + 8]
289 .try_into()
290 .map_err(|_| corrupt("f64 value bytes"))?,
291 );
292 let val = f64::from_bits(bits);
293 pos += 8;
294 let run_len = u32::from_le_bytes(
295 data[pos..pos + 4]
296 .try_into()
297 .map_err(|_| corrupt("f64 run length bytes"))?,
298 ) as usize;
299 pos += 4;
300 checked_run(result.len(), run_len, num_values)?;
301 result.extend(std::iter::repeat_n(val, run_len));
302 }
303 verify_count(num_values, result.len())?;
304 Ok(result)
305 }
306
307 pub fn decode_bool(data: &[u8]) -> Result<Vec<bool>> {
313 let (num_values, num_runs, mut pos) = read_header(data)?;
314 let mut result = Vec::with_capacity(num_values);
315 for _ in 0..num_runs {
316 if pos + 5 > data.len() {
317 return Err(corrupt("bool run data truncated"));
318 }
319 let val = data[pos] != 0;
320 pos += 1;
321 let run_len = u32::from_le_bytes(
322 data[pos..pos + 4]
323 .try_into()
324 .map_err(|_| corrupt("bool run length bytes"))?,
325 ) as usize;
326 pos += 4;
327 checked_run(result.len(), run_len, num_values)?;
328 result.extend(std::iter::repeat_n(val, run_len));
329 }
330 verify_count(num_values, result.len())?;
331 Ok(result)
332 }
333
334 pub fn decode_string(data: &[u8]) -> Result<Vec<String>> {
341 let (num_values, num_runs, mut pos) = read_header(data)?;
342 let mut result = Vec::with_capacity(num_values);
343 for _ in 0..num_runs {
344 if pos + 4 > data.len() {
345 return Err(corrupt("string run: string_len truncated"));
346 }
347 let s_len = u32::from_le_bytes(
348 data[pos..pos + 4]
349 .try_into()
350 .map_err(|_| corrupt("string length bytes"))?,
351 ) as usize;
352 pos += 4;
353 if pos + s_len > data.len() {
354 return Err(corrupt("string run: string bytes truncated"));
355 }
356 let s = std::str::from_utf8(&data[pos..pos + s_len])
357 .map_err(|e| EncodingError::CorruptData {
358 detail: format!("RLE string: invalid UTF-8: {e}"),
359 })?
360 .to_owned();
361 pos += s_len;
362 if pos + 4 > data.len() {
363 return Err(corrupt("string run: run_length truncated"));
364 }
365 let run_len = u32::from_le_bytes(
366 data[pos..pos + 4]
367 .try_into()
368 .map_err(|_| corrupt("string run length bytes"))?,
369 ) as usize;
370 pos += 4;
371 checked_run(result.len(), run_len, num_values)?;
372 result.extend(std::iter::repeat_n(s, run_len));
373 }
374 verify_count(num_values, result.len())?;
375 Ok(result)
376 }
377}
378
379fn build_runs<T: Copy, F: Fn(&T, &T) -> bool>(values: &[T], eq: F) -> Vec<(T, u32)> {
385 let mut runs = Vec::new();
386 if values.is_empty() {
387 return runs;
388 }
389 let mut current = values[0];
390 let mut count: u32 = 1;
391 for &v in &values[1..] {
392 if eq(&v, ¤t) {
393 if count == u32::MAX {
397 runs.push((current, count));
398 count = 1;
399 } else {
400 count += 1;
401 }
402 } else {
403 runs.push((current, count));
404 current = v;
405 count = 1;
406 }
407 }
408 runs.push((current, count));
409 runs
410}
411
412const MIN_RUN_BYTES: usize = 5;
417
418fn read_header(data: &[u8]) -> Result<(usize, usize, usize)> {
429 if data.len() < 9 {
431 return Err(corrupt("RLE header too short"));
432 }
433 let num_values = checked_decode_count(
435 u32::from_le_bytes(
436 data[1..5]
437 .try_into()
438 .map_err(|_| corrupt("header num_values bytes"))?,
439 ) as usize,
440 "RLE",
441 )?;
442 let num_runs = u32::from_le_bytes(
443 data[5..9]
444 .try_into()
445 .map_err(|_| corrupt("header num_runs bytes"))?,
446 ) as usize;
447 let max_runs = (data.len() - 9) / MIN_RUN_BYTES;
448 if num_runs > max_runs {
449 return Err(corrupt(
450 "RLE block declares more runs than the payload can hold",
451 ));
452 }
453 Ok((num_values, num_runs, 9))
454}
455
456#[inline]
465fn checked_run(decoded: usize, run_len: usize, num_values: usize) -> Result<()> {
466 if decoded + run_len > num_values {
467 return Err(corrupt(
468 "RLE run lengths exceed the declared value count — header is corrupt",
469 ));
470 }
471 Ok(())
472}
473
474fn verify_count(expected: usize, actual: usize) -> Result<()> {
476 if expected != actual {
477 return Err(EncodingError::CountMismatch { expected, actual });
478 }
479 Ok(())
480}
481
482fn corrupt(detail: &str) -> EncodingError {
484 EncodingError::CorruptData {
485 detail: detail.to_string(),
486 }
487}
488
489#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
500 fn i64_constant_roundtrip() {
501 let values = vec![42_i64; 1000];
502 let encoded = RleEncoder::encode_i64(&values).unwrap();
503 assert!(encoded.len() < values.len() * 8);
505 let decoded = RleDecoder::decode_i64(&encoded).unwrap();
506 assert_eq!(values, decoded);
507 }
508
509 #[test]
510 fn i64_varied_roundtrip() {
511 let values: Vec<i64> = (0..500).collect();
512 let encoded = RleEncoder::encode_i64(&values).unwrap();
513 let decoded = RleDecoder::decode_i64(&encoded).unwrap();
514 assert_eq!(values, decoded);
515 }
516
517 #[test]
518 fn i64_runs_roundtrip() {
519 let mut values = Vec::new();
521 values.extend(std::iter::repeat_n(1_i64, 100));
522 values.extend(std::iter::repeat_n(2_i64, 200));
523 values.extend(std::iter::repeat_n(3_i64, 100));
524 let encoded = RleEncoder::encode_i64(&values).unwrap();
525 let decoded = RleDecoder::decode_i64(&encoded).unwrap();
526 assert_eq!(values, decoded);
527 }
528
529 #[test]
530 fn i64_single_value() {
531 let values = vec![99_i64];
532 let encoded = RleEncoder::encode_i64(&values).unwrap();
533 let decoded = RleDecoder::decode_i64(&encoded).unwrap();
534 assert_eq!(values, decoded);
535 }
536
537 #[test]
538 fn i64_empty_error() {
539 assert!(RleEncoder::encode_i64(&[]).is_err());
540 }
541
542 #[test]
543 fn i64_negative_values() {
544 let values = vec![-1_i64; 50];
545 let encoded = RleEncoder::encode_i64(&values).unwrap();
546 let decoded = RleDecoder::decode_i64(&encoded).unwrap();
547 assert_eq!(values, decoded);
548 }
549
550 #[test]
553 fn u64_constant_roundtrip() {
554 let values = vec![100_u64; 500];
555 let encoded = RleEncoder::encode_u64(&values).unwrap();
556 let decoded = RleDecoder::decode_u64(&encoded).unwrap();
557 assert_eq!(values, decoded);
558 }
559
560 #[test]
561 fn u64_varied_roundtrip() {
562 let values: Vec<u64> = (0..200).collect();
563 let encoded = RleEncoder::encode_u64(&values).unwrap();
564 let decoded = RleDecoder::decode_u64(&encoded).unwrap();
565 assert_eq!(values, decoded);
566 }
567
568 #[test]
569 fn u64_empty_error() {
570 assert!(RleEncoder::encode_u64(&[]).is_err());
571 }
572
573 #[test]
576 fn f64_constant_roundtrip() {
577 let values = vec![2.75_f64; 1000];
578 let encoded = RleEncoder::encode_f64(&values).unwrap();
579 assert!(encoded.len() < values.len() * 8);
580 let decoded = RleDecoder::decode_f64(&encoded).unwrap();
581 assert_eq!(values.len(), decoded.len());
582 for (a, b) in values.iter().zip(decoded.iter()) {
583 assert_eq!(a.to_bits(), b.to_bits());
584 }
585 }
586
587 #[test]
588 fn f64_nan_roundtrip() {
589 let values = vec![f64::NAN; 100];
590 let encoded = RleEncoder::encode_f64(&values).unwrap();
591 let decoded = RleDecoder::decode_f64(&encoded).unwrap();
592 assert_eq!(values.len(), decoded.len());
593 for v in &decoded {
594 assert!(v.is_nan());
595 }
596 }
597
598 #[test]
599 fn f64_mixed_special_values() {
600 let values = vec![
601 f64::INFINITY,
602 f64::INFINITY,
603 f64::NEG_INFINITY,
604 f64::NEG_INFINITY,
605 f64::NAN,
606 0.0,
607 -0.0, ];
609 let encoded = RleEncoder::encode_f64(&values).unwrap();
610 let decoded = RleDecoder::decode_f64(&encoded).unwrap();
611 assert_eq!(values.len(), decoded.len());
612 for (a, b) in values.iter().zip(decoded.iter()) {
613 assert_eq!(a.to_bits(), b.to_bits());
614 }
615 }
616
617 #[test]
618 fn f64_empty_error() {
619 assert!(RleEncoder::encode_f64(&[]).is_err());
620 }
621
622 #[test]
625 fn bool_constant_roundtrip() {
626 let values = vec![true; 500];
627 let encoded = RleEncoder::encode_bool(&values).unwrap();
628 assert!(encoded.len() < values.len());
629 let decoded = RleDecoder::decode_bool(&encoded).unwrap();
630 assert_eq!(values, decoded);
631 }
632
633 #[test]
634 fn bool_alternating_roundtrip() {
635 let values: Vec<bool> = (0..100).map(|i| i % 2 == 0).collect();
636 let encoded = RleEncoder::encode_bool(&values).unwrap();
637 let decoded = RleDecoder::decode_bool(&encoded).unwrap();
638 assert_eq!(values, decoded);
639 }
640
641 #[test]
642 fn bool_runs_roundtrip() {
643 let mut values = Vec::new();
644 values.extend(std::iter::repeat_n(true, 50));
645 values.extend(std::iter::repeat_n(false, 100));
646 values.extend(std::iter::repeat_n(true, 30));
647 let encoded = RleEncoder::encode_bool(&values).unwrap();
648 let decoded = RleDecoder::decode_bool(&encoded).unwrap();
649 assert_eq!(values, decoded);
650 }
651
652 #[test]
653 fn bool_empty_error() {
654 assert!(RleEncoder::encode_bool(&[]).is_err());
655 }
656
657 #[test]
660 fn string_constant_roundtrip() {
661 let values = vec!["us-east-1"; 200];
662 let encoded = RleEncoder::encode_string(&values).unwrap();
663 let decoded = RleDecoder::decode_string(&encoded).unwrap();
664 let expected: Vec<String> = values.iter().map(|s| (*s).to_owned()).collect();
665 assert_eq!(expected, decoded);
666 }
667
668 #[test]
669 fn string_runs_roundtrip() {
670 let values = vec!["a", "a", "a", "b", "b", "c", "c", "c", "c"];
671 let encoded = RleEncoder::encode_string(&values).unwrap();
672 let decoded = RleDecoder::decode_string(&encoded).unwrap();
673 let expected: Vec<String> = values.iter().map(|s| (*s).to_owned()).collect();
674 assert_eq!(expected, decoded);
675 }
676
677 #[test]
678 fn string_varied_roundtrip() {
679 let owned: Vec<String> = (0..50).map(|i| format!("val-{i}")).collect();
680 let refs: Vec<&str> = owned.iter().map(String::as_str).collect();
681 let encoded = RleEncoder::encode_string(&refs).unwrap();
682 let decoded = RleDecoder::decode_string(&encoded).unwrap();
683 assert_eq!(owned, decoded);
684 }
685
686 #[test]
687 fn string_empty_strings() {
688 let values = vec![""; 100];
689 let encoded = RleEncoder::encode_string(&values).unwrap();
690 let decoded = RleDecoder::decode_string(&encoded).unwrap();
691 let expected: Vec<String> = values.iter().map(|s| (*s).to_owned()).collect();
692 assert_eq!(expected, decoded);
693 }
694
695 #[test]
696 fn string_empty_error() {
697 let empty: &[&str] = &[];
698 assert!(RleEncoder::encode_string(empty).is_err());
699 }
700
701 #[test]
704 fn decode_truncated_header() {
705 assert!(RleDecoder::decode_i64(&[0, 1, 2, 3]).is_err());
706 }
707
708 #[test]
709 fn decode_truncated_run() {
710 let mut data = vec![VALUE_TYPE_I64];
712 data.extend_from_slice(&1_u32.to_le_bytes());
713 data.extend_from_slice(&1_u32.to_le_bytes());
714 assert!(RleDecoder::decode_i64(&data).is_err());
715 }
716
717 #[test]
718 fn decode_count_mismatch() {
719 let mut data = vec![VALUE_TYPE_I64];
721 data.extend_from_slice(&999_u32.to_le_bytes()); data.extend_from_slice(&1_u32.to_le_bytes()); data.extend_from_slice(&42_i64.to_le_bytes()); data.extend_from_slice(&1_u32.to_le_bytes()); assert!(RleDecoder::decode_i64(&data).is_err());
726 }
727
728 #[test]
731 fn constant_column_achieves_high_compression() {
732 let values = vec![42_i64; 10_000];
733 let encoded = RleEncoder::encode_i64(&values).unwrap();
734 let ratio = (values.len() * 8) as f64 / encoded.len() as f64;
736 assert!(ratio > 100.0, "Expected >100× compression, got {ratio:.1}×");
737 }
738}
739
740#[cfg(test)]
741mod proptests {
742 use super::*;
743 use proptest::prelude::*;
744
745 proptest! {
746 #[test]
747 fn roundtrip_i64(values in proptest::collection::vec(any::<i64>(), 1..300)) {
748 let encoded = RleEncoder::encode_i64(&values).unwrap();
749 let decoded = RleDecoder::decode_i64(&encoded).unwrap();
750 prop_assert_eq!(&values, &decoded);
751 }
752
753 #[test]
754 fn roundtrip_u64(values in proptest::collection::vec(any::<u64>(), 1..300)) {
755 let encoded = RleEncoder::encode_u64(&values).unwrap();
756 let decoded = RleDecoder::decode_u64(&encoded).unwrap();
757 prop_assert_eq!(&values, &decoded);
758 }
759
760 #[test]
761 fn roundtrip_f64(values in proptest::collection::vec(any::<f64>(), 1..300)) {
762 let encoded = RleEncoder::encode_f64(&values).unwrap();
763 let decoded = RleDecoder::decode_f64(&encoded).unwrap();
764 prop_assert_eq!(values.len(), decoded.len());
766 for (a, b) in values.iter().zip(decoded.iter()) {
767 prop_assert_eq!(a.to_bits(), b.to_bits());
768 }
769 }
770
771 #[test]
772 fn roundtrip_bool(values in proptest::collection::vec(any::<bool>(), 1..300)) {
773 let encoded = RleEncoder::encode_bool(&values).unwrap();
774 let decoded = RleDecoder::decode_bool(&encoded).unwrap();
775 prop_assert_eq!(&values, &decoded);
776 }
777
778 #[test]
779 fn roundtrip_string(values in proptest::collection::vec("[a-z]{0,8}", 1..100)) {
780 let refs: Vec<&str> = values.iter().map(String::as_str).collect();
781 let encoded = RleEncoder::encode_string(&refs).unwrap();
782 let decoded = RleDecoder::decode_string(&encoded).unwrap();
783 prop_assert_eq!(&values, &decoded);
784 }
785 }
786}
787
788#[cfg(test)]
789mod bomb_tests {
790 use super::*;
791
792 #[test]
800 fn a_declared_value_count_cannot_size_an_allocation() {
801 let mut payload = vec![VALUE_TYPE_I64];
802 payload.extend_from_slice(&u32::MAX.to_le_bytes()); payload.extend_from_slice(&0u32.to_le_bytes()); let err = RleDecoder::decode_i64(&payload).expect_err("must refuse the count");
805 assert!(
806 err.to_string().contains("ceiling"),
807 "expected the decode ceiling to reject it, got: {err}"
808 );
809 }
810
811 #[test]
819 fn a_run_length_cannot_expand_past_the_declared_count() {
820 let mut payload = vec![VALUE_TYPE_I64];
821 payload.extend_from_slice(&3u32.to_le_bytes()); payload.extend_from_slice(&1u32.to_le_bytes()); payload.extend_from_slice(&7i64.to_le_bytes()); payload.extend_from_slice(&u32::MAX.to_le_bytes()); let err = RleDecoder::decode_i64(&payload).expect_err("must refuse the run");
826 assert!(
827 err.to_string().contains("exceed the declared value count"),
828 "expected the run guard to reject it, got: {err}"
829 );
830 }
831
832 #[test]
834 fn a_string_run_cannot_expand_past_the_declared_count() {
835 let mut payload = vec![VALUE_TYPE_STRING];
836 payload.extend_from_slice(&1u32.to_le_bytes()); payload.extend_from_slice(&1u32.to_le_bytes()); payload.extend_from_slice(&1u32.to_le_bytes()); payload.push(b'x');
840 payload.extend_from_slice(&u32::MAX.to_le_bytes()); let err = RleDecoder::decode_string(&payload).expect_err("must refuse the run");
842 assert!(
843 err.to_string().contains("exceed the declared value count"),
844 "expected the run guard to reject it, got: {err}"
845 );
846 }
847
848 #[test]
851 fn a_run_count_beyond_the_payload_is_rejected() {
852 let mut payload = vec![VALUE_TYPE_BOOL];
853 payload.extend_from_slice(&10u32.to_le_bytes()); payload.extend_from_slice(&1_000_000u32.to_le_bytes()); let err = RleDecoder::decode_bool(&payload).expect_err("must refuse the run count");
856 assert!(
857 err.to_string().contains("more runs than the payload"),
858 "expected the run-count guard to reject it, got: {err}"
859 );
860 }
861
862 #[test]
865 fn legitimate_blocks_still_round_trip() {
866 let values: Vec<i64> = std::iter::repeat_n(4, 1000)
867 .chain(std::iter::repeat_n(9, 500))
868 .collect();
869 let encoded = RleEncoder::encode_i64(&values).expect("encode");
870 assert_eq!(RleDecoder::decode_i64(&encoded).expect("decode"), values);
871 }
872}