1use std::{
2 cmp::Ordering,
3 collections::{BinaryHeap, binary_heap::PeekMut},
4 mem,
5};
6
7use bytes::{Buf, Bytes, BytesMut};
8
9use crate::range_set::ArrayRangeSet;
10
11#[derive(Debug, Default)]
13pub(super) struct Assembler {
14 state: State,
15 data: BinaryHeap<Buffer>,
16 buffered: usize,
18 allocated: usize,
20 bytes_read: u64,
24 end: u64,
25}
26
27impl Assembler {
28 pub(super) fn new() -> Self {
29 Self::default()
30 }
31
32 pub(super) fn reinit(&mut self) {
34 let old_data = mem::take(&mut self.data);
35 *self = Self::default();
36 self.data = old_data;
37 self.data.clear();
38 }
39
40 pub(super) fn is_ordered(&self) -> bool {
41 self.state.is_ordered()
42 }
43
44 pub(super) fn ensure_ordering(&mut self, ordered: bool) -> Result<(), IllegalOrderedRead> {
45 if ordered && !self.state.is_ordered() {
46 return Err(IllegalOrderedRead);
47 } else if !ordered && self.state.is_ordered() {
48 if !self.data.is_empty() {
50 self.defragment();
52 }
53 let mut recvd = ArrayRangeSet::new();
54 recvd.insert(0..self.bytes_read);
55 for chunk in &self.data {
56 recvd.insert(chunk.offset..chunk.offset + chunk.bytes.len() as u64);
57 }
58 self.state = State::Unordered { recvd };
59 }
60 Ok(())
61 }
62
63 pub(super) fn read(&mut self, max_length: usize, ordered: bool) -> Option<Chunk> {
65 loop {
66 let mut chunk = self.data.peek_mut()?;
67
68 if ordered {
69 if chunk.offset > self.bytes_read {
70 return None;
72 } else if (chunk.offset + chunk.bytes.len() as u64) <= self.bytes_read {
73 self.buffered -= chunk.bytes.len();
75 self.allocated -= chunk.allocation_size;
76 PeekMut::pop(chunk);
77 continue;
78 }
79
80 let start = (self.bytes_read - chunk.offset) as usize;
82 if start > 0 {
83 chunk.bytes.advance(start);
84 chunk.offset += start as u64;
85 self.buffered -= start;
86 }
87 }
88
89 return Some(if max_length < chunk.bytes.len() {
90 self.bytes_read += max_length as u64;
91 let offset = chunk.offset;
92 chunk.offset += max_length as u64;
93 self.buffered -= max_length;
94 Chunk::new(offset, chunk.bytes.split_to(max_length))
95 } else {
96 self.bytes_read += chunk.bytes.len() as u64;
97 self.buffered -= chunk.bytes.len();
98 self.allocated -= chunk.allocation_size;
99 let chunk = PeekMut::pop(chunk);
100 Chunk::new(chunk.offset, chunk.bytes)
101 });
102 }
103 }
104
105 fn defragment(&mut self) {
110 let new = BinaryHeap::with_capacity(self.data.len());
111 let old = mem::replace(&mut self.data, new);
112 let mut buffers = old.into_sorted_vec();
113 self.buffered = 0;
114 let mut fragmented_buffered = 0;
115 let mut offset = self.bytes_read;
116 for chunk in buffers.iter_mut().rev() {
117 chunk.try_mark_defragment(offset);
118 let size = chunk.bytes.len();
119 offset = chunk.offset + size as u64;
120 self.buffered += size;
121 if !chunk.defragmented {
122 fragmented_buffered += size;
123 }
124 }
125 self.allocated = self.buffered;
126 let mut buffer = BytesMut::with_capacity(fragmented_buffered);
127 let mut offset = self.bytes_read;
128 for chunk in buffers.into_iter().rev() {
129 if chunk.defragmented {
130 if !chunk.bytes.is_empty() {
132 self.data.push(chunk);
133 }
134 continue;
135 }
136 if chunk.offset != offset + (buffer.len() as u64) {
138 if !buffer.is_empty() {
139 self.data
140 .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
141 }
142 offset = chunk.offset;
143 }
144 buffer.extend_from_slice(&chunk.bytes);
145 }
146 if !buffer.is_empty() {
147 self.data
148 .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
149 }
150 }
151
152 pub(super) fn insert(&mut self, mut offset: u64, mut bytes: Bytes, allocation_size: usize) {
155 debug_assert!(
156 bytes.len() <= allocation_size,
157 "allocation_size less than bytes.len(): {:?} < {:?}",
158 allocation_size,
159 bytes.len()
160 );
161 self.end = self.end.max(offset + bytes.len() as u64);
162 if let State::Unordered { ref mut recvd } = self.state {
163 let range = offset..offset + bytes.len() as u64;
165 for duplicate in recvd.iter_range(range.clone()) {
166 if duplicate.start > offset {
167 let buffer = Buffer::new(
168 offset,
169 bytes.split_to((duplicate.start - offset) as usize),
170 allocation_size,
171 );
172 self.buffered += buffer.bytes.len();
173 self.allocated += buffer.allocation_size;
174 self.data.push(buffer);
175 offset = duplicate.start;
176 }
177 bytes.advance((duplicate.end - offset) as usize);
178 offset = duplicate.end;
179 }
180 recvd.insert(range);
181 } else if offset < self.bytes_read {
182 if (offset + bytes.len() as u64) <= self.bytes_read {
183 return;
184 } else {
185 let diff = self.bytes_read - offset;
186 offset += diff;
187 bytes.advance(diff as usize);
188 }
189 }
190
191 if bytes.is_empty() {
192 return;
193 }
194 let buffer = Buffer::new(offset, bytes, allocation_size);
195 self.buffered += buffer.bytes.len();
196 self.allocated += buffer.allocation_size;
197 self.data.push(buffer);
198 let buffered = self.buffered.min((self.end - self.bytes_read) as usize);
203 let over_allocation = self.allocated - buffered;
204 let threshold = 32768.max(buffered * 3 / 2);
212 if over_allocation > threshold {
213 self.defragment()
214 }
215 }
216
217 pub(super) fn bytes_read(&self) -> u64 {
219 self.bytes_read
220 }
221
222 pub(super) fn clear(&mut self) {
224 self.data.clear();
225 self.buffered = 0;
226 self.allocated = 0;
227 }
228}
229
230#[derive(Debug, PartialEq, Eq)]
232pub struct Chunk {
233 pub offset: u64,
235 pub bytes: Bytes,
237}
238
239impl Chunk {
240 fn new(offset: u64, bytes: Bytes) -> Self {
241 Self { offset, bytes }
242 }
243}
244
245#[derive(Debug, Eq)]
246struct Buffer {
247 offset: u64,
248 bytes: Bytes,
249 allocation_size: usize,
253 defragmented: bool,
254}
255
256impl Buffer {
257 fn new(offset: u64, bytes: Bytes, allocation_size: usize) -> Self {
259 Self {
260 offset,
261 bytes,
262 allocation_size,
263 defragmented: false,
264 }
265 }
266
267 fn new_defragmented(offset: u64, bytes: Bytes) -> Self {
269 let allocation_size = bytes.len();
270 Self {
271 offset,
272 bytes,
273 allocation_size,
274 defragmented: true,
275 }
276 }
277
278 fn try_mark_defragment(&mut self, offset: u64) {
280 let duplicate = offset.saturating_sub(self.offset) as usize;
281 self.offset = self.offset.max(offset);
282 if duplicate >= self.bytes.len() {
283 self.bytes = Bytes::new();
285 self.defragmented = true;
286 self.allocation_size = 0;
287 return;
288 }
289 self.bytes.advance(duplicate);
290 self.defragmented = self.defragmented || self.bytes.len() * 6 / 5 >= self.allocation_size;
293 if self.defragmented {
294 self.allocation_size = self.bytes.len();
296 }
297 }
298}
299
300impl Ord for Buffer {
301 fn cmp(&self, other: &Self) -> Ordering {
304 self.offset
305 .cmp(&other.offset)
306 .reverse()
307 .then(self.bytes.len().cmp(&other.bytes.len()))
308 }
309}
310
311impl PartialOrd for Buffer {
312 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
313 Some(self.cmp(other))
314 }
315}
316
317impl PartialEq for Buffer {
318 fn eq(&self, other: &Self) -> bool {
319 (self.offset, self.bytes.len()) == (other.offset, other.bytes.len())
320 }
321}
322
323#[derive(Debug, Default)]
324enum State {
325 #[default]
326 Ordered,
327 Unordered {
328 recvd: ArrayRangeSet,
331 },
332}
333
334impl State {
335 fn is_ordered(&self) -> bool {
336 matches!(self, Self::Ordered)
337 }
338}
339
340#[derive(Debug)]
342pub(crate) struct IllegalOrderedRead;
343
344#[cfg(test)]
345mod test {
346 use super::*;
347 use assert_matches::assert_matches;
348
349 #[test]
350 fn assemble_ordered() {
351 let mut x = Assembler::new();
352 assert_matches!(next(&mut x, 32), None);
353 x.insert(0, Bytes::from_static(b"123"), 3);
354 assert_matches!(next(&mut x, 1), Some(ref y) if &y[..] == b"1");
355 assert_matches!(next(&mut x, 3), Some(ref y) if &y[..] == b"23");
356 x.insert(3, Bytes::from_static(b"456"), 3);
357 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
358 x.insert(6, Bytes::from_static(b"789"), 3);
359 x.insert(9, Bytes::from_static(b"10"), 2);
360 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"789");
361 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"10");
362 assert_matches!(next(&mut x, 32), None);
363 }
364
365 #[test]
366 fn assemble_unordered() {
367 let mut x = Assembler::new();
368 x.ensure_ordering(false).unwrap();
369 x.insert(3, Bytes::from_static(b"456"), 3);
370 assert_matches!(next(&mut x, 32), None);
371 x.insert(0, Bytes::from_static(b"123"), 3);
372 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
373 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
374 assert_matches!(next(&mut x, 32), None);
375 }
376
377 #[test]
378 fn assemble_duplicate() {
379 let mut x = Assembler::new();
380 x.insert(0, Bytes::from_static(b"123"), 3);
381 x.insert(0, Bytes::from_static(b"123"), 3);
382 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
383 assert_matches!(next(&mut x, 32), None);
384 }
385
386 #[test]
387 fn assemble_duplicate_compact() {
388 let mut x = Assembler::new();
389 x.insert(0, Bytes::from_static(b"123"), 3);
390 x.insert(0, Bytes::from_static(b"123"), 3);
391 x.defragment();
392 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
393 assert_matches!(next(&mut x, 32), None);
394 }
395
396 #[test]
397 fn assemble_contained() {
398 let mut x = Assembler::new();
399 x.insert(0, Bytes::from_static(b"12345"), 5);
400 x.insert(1, Bytes::from_static(b"234"), 3);
401 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
402 assert_matches!(next(&mut x, 32), None);
403 }
404
405 #[test]
406 fn assemble_contained_compact() {
407 let mut x = Assembler::new();
408 x.insert(0, Bytes::from_static(b"12345"), 5);
409 x.insert(1, Bytes::from_static(b"234"), 3);
410 x.defragment();
411 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
412 assert_matches!(next(&mut x, 32), None);
413 }
414
415 #[test]
416 fn assemble_contains() {
417 let mut x = Assembler::new();
418 x.insert(1, Bytes::from_static(b"234"), 3);
419 x.insert(0, Bytes::from_static(b"12345"), 5);
420 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
421 assert_matches!(next(&mut x, 32), None);
422 }
423
424 #[test]
425 fn assemble_contains_compact() {
426 let mut x = Assembler::new();
427 x.insert(1, Bytes::from_static(b"234"), 3);
428 x.insert(0, Bytes::from_static(b"12345"), 5);
429 x.defragment();
430 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
431 assert_matches!(next(&mut x, 32), None);
432 }
433
434 #[test]
435 fn assemble_overlapping() {
436 let mut x = Assembler::new();
437 x.insert(0, Bytes::from_static(b"123"), 3);
438 x.insert(1, Bytes::from_static(b"234"), 3);
439 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
440 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"4");
441 assert_matches!(next(&mut x, 32), None);
442 }
443
444 #[test]
445 fn assemble_overlapping_compact() {
446 let mut x = Assembler::new();
447 x.insert(0, Bytes::from_static(b"123"), 4);
448 x.insert(1, Bytes::from_static(b"234"), 4);
449 x.defragment();
450 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
451 assert_matches!(next(&mut x, 32), None);
452 }
453
454 #[test]
455 fn assemble_complex() {
456 let mut x = Assembler::new();
457 x.insert(0, Bytes::from_static(b"1"), 1);
458 x.insert(2, Bytes::from_static(b"3"), 1);
459 x.insert(4, Bytes::from_static(b"5"), 1);
460 x.insert(0, Bytes::from_static(b"123456"), 6);
461 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
462 assert_matches!(next(&mut x, 32), None);
463 }
464
465 #[test]
466 fn assemble_complex_compact() {
467 let mut x = Assembler::new();
468 x.insert(0, Bytes::from_static(b"1"), 1);
469 x.insert(2, Bytes::from_static(b"3"), 1);
470 x.insert(4, Bytes::from_static(b"5"), 1);
471 x.insert(0, Bytes::from_static(b"123456"), 6);
472 x.defragment();
473 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
474 assert_matches!(next(&mut x, 32), None);
475 }
476
477 #[test]
478 fn assemble_old() {
479 let mut x = Assembler::new();
480 x.insert(0, Bytes::from_static(b"1234"), 4);
481 assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
482 x.insert(0, Bytes::from_static(b"1234"), 4);
483 assert_matches!(next(&mut x, 32), None);
484 }
485
486 #[test]
487 fn compact() {
488 let mut x = Assembler::new();
489 x.insert(0, Bytes::from_static(b"abc"), 4);
490 x.insert(3, Bytes::from_static(b"def"), 4);
491 x.insert(9, Bytes::from_static(b"jkl"), 4);
492 x.insert(12, Bytes::from_static(b"mno"), 4);
493 x.defragment();
494 assert_eq!(
495 next_unordered(&mut x),
496 Chunk::new(0, Bytes::from_static(b"abcdef"))
497 );
498 assert_eq!(
499 next_unordered(&mut x),
500 Chunk::new(9, Bytes::from_static(b"jklmno"))
501 );
502 }
503
504 #[test]
505 fn defrag_with_missing_prefix() {
506 let mut x = Assembler::new();
507 x.insert(3, Bytes::from_static(b"def"), 3);
508 x.defragment();
509 assert_eq!(
510 next_unordered(&mut x),
511 Chunk::new(3, Bytes::from_static(b"def"))
512 );
513 }
514
515 #[test]
516 fn defrag_read_chunk() {
517 let mut x = Assembler::new();
518 x.insert(3, Bytes::from_static(b"def"), 4);
519 x.insert(0, Bytes::from_static(b"abc"), 4);
520 x.insert(7, Bytes::from_static(b"hij"), 4);
521 x.insert(11, Bytes::from_static(b"lmn"), 4);
522 x.defragment();
523 assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"abcdef");
524 x.insert(5, Bytes::from_static(b"fghijklmn"), 9);
525 assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"ghijklmn");
526 x.insert(13, Bytes::from_static(b"nopq"), 4);
527 assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"opq");
528 x.insert(15, Bytes::from_static(b"pqrs"), 4);
529 assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"rs");
530 assert_matches!(x.read(usize::MAX, true), None);
531 }
532
533 #[test]
534 fn unordered_happy_path() {
535 let mut x = Assembler::new();
536 x.ensure_ordering(false).unwrap();
537 x.insert(0, Bytes::from_static(b"abc"), 3);
538 assert_eq!(
539 next_unordered(&mut x),
540 Chunk::new(0, Bytes::from_static(b"abc"))
541 );
542 assert_eq!(x.read(usize::MAX, false), None);
543 x.insert(3, Bytes::from_static(b"def"), 3);
544 assert_eq!(
545 next_unordered(&mut x),
546 Chunk::new(3, Bytes::from_static(b"def"))
547 );
548 assert_eq!(x.read(usize::MAX, false), None);
549 }
550
551 #[test]
552 fn unordered_dedup() {
553 let mut x = Assembler::new();
554 x.ensure_ordering(false).unwrap();
555 x.insert(3, Bytes::from_static(b"def"), 3);
556 assert_eq!(
557 next_unordered(&mut x),
558 Chunk::new(3, Bytes::from_static(b"def"))
559 );
560 assert_eq!(x.read(usize::MAX, false), None);
561 x.insert(0, Bytes::from_static(b"a"), 1);
562 x.insert(0, Bytes::from_static(b"abcdefghi"), 9);
563 x.insert(0, Bytes::from_static(b"abcd"), 4);
564 assert_eq!(
565 next_unordered(&mut x),
566 Chunk::new(0, Bytes::from_static(b"a"))
567 );
568 assert_eq!(
569 next_unordered(&mut x),
570 Chunk::new(1, Bytes::from_static(b"bc"))
571 );
572 assert_eq!(
573 next_unordered(&mut x),
574 Chunk::new(6, Bytes::from_static(b"ghi"))
575 );
576 assert_eq!(x.read(usize::MAX, false), None);
577 x.insert(8, Bytes::from_static(b"ijkl"), 4);
578 assert_eq!(
579 next_unordered(&mut x),
580 Chunk::new(9, Bytes::from_static(b"jkl"))
581 );
582 assert_eq!(x.read(usize::MAX, false), None);
583 x.insert(12, Bytes::from_static(b"mno"), 3);
584 assert_eq!(
585 next_unordered(&mut x),
586 Chunk::new(12, Bytes::from_static(b"mno"))
587 );
588 assert_eq!(x.read(usize::MAX, false), None);
589 x.insert(2, Bytes::from_static(b"cde"), 3);
590 assert_eq!(x.read(usize::MAX, false), None);
591 }
592
593 #[test]
594 fn chunks_dedup() {
595 let mut x = Assembler::new();
596 x.insert(3, Bytes::from_static(b"def"), 3);
597 assert_eq!(x.read(usize::MAX, true), None);
598 x.insert(0, Bytes::from_static(b"a"), 1);
599 x.insert(1, Bytes::from_static(b"bcdefghi"), 9);
600 x.insert(0, Bytes::from_static(b"abcd"), 4);
601 assert_eq!(
602 x.read(usize::MAX, true),
603 Some(Chunk::new(0, Bytes::from_static(b"abcd")))
604 );
605 assert_eq!(
606 x.read(usize::MAX, true),
607 Some(Chunk::new(4, Bytes::from_static(b"efghi")))
608 );
609 assert_eq!(x.read(usize::MAX, true), None);
610 x.insert(8, Bytes::from_static(b"ijkl"), 4);
611 assert_eq!(
612 x.read(usize::MAX, true),
613 Some(Chunk::new(9, Bytes::from_static(b"jkl")))
614 );
615 assert_eq!(x.read(usize::MAX, true), None);
616 x.insert(12, Bytes::from_static(b"mno"), 3);
617 assert_eq!(
618 x.read(usize::MAX, true),
619 Some(Chunk::new(12, Bytes::from_static(b"mno")))
620 );
621 assert_eq!(x.read(usize::MAX, true), None);
622 x.insert(2, Bytes::from_static(b"cde"), 3);
623 assert_eq!(x.read(usize::MAX, true), None);
624 }
625
626 #[test]
627 fn ordered_eager_discard() {
628 let mut x = Assembler::new();
629 x.insert(0, Bytes::from_static(b"abc"), 3);
630 assert_eq!(x.data.len(), 1);
631 assert_eq!(
632 x.read(usize::MAX, true),
633 Some(Chunk::new(0, Bytes::from_static(b"abc")))
634 );
635 x.insert(0, Bytes::from_static(b"ab"), 2);
636 assert_eq!(x.data.len(), 0);
637 x.insert(2, Bytes::from_static(b"cd"), 2);
638 assert_eq!(
639 x.data.peek(),
640 Some(&Buffer::new(3, Bytes::from_static(b"d"), 2))
641 );
642 }
643
644 #[test]
645 fn ordered_insert_unordered_read() {
646 let mut x = Assembler::new();
647 x.insert(0, Bytes::from_static(b"abc"), 3);
648 x.insert(0, Bytes::from_static(b"abc"), 3);
649 x.ensure_ordering(false).unwrap();
650 assert_eq!(
651 x.read(3, false),
652 Some(Chunk::new(0, Bytes::from_static(b"abc")))
653 );
654 assert_eq!(x.read(3, false), None);
655 }
656
657 #[test]
658 fn no_duplicate_after_mode_switch() {
659 let mut x = Assembler::new();
662 x.insert(0, Bytes::from_static(b"a"), 1);
663 x.insert(0, Bytes::from_static(b"a"), 1); assert_eq!(
665 x.read(1, true),
666 Some(Chunk::new(0, Bytes::from_static(b"a")))
667 );
668 x.ensure_ordering(false).unwrap();
669 assert_eq!(x.read(1, false), None); }
671
672 fn next_unordered(x: &mut Assembler) -> Chunk {
673 x.read(usize::MAX, false).unwrap()
674 }
675
676 fn next(x: &mut Assembler, size: usize) -> Option<Bytes> {
677 x.read(size, true).map(|chunk| chunk.bytes)
678 }
679}
680
681#[cfg(all(test, not(target_family = "wasm")))]
682mod proptests {
683 use proptest::prelude::*;
684 use rand::RngExt;
685 use test_strategy::{Arbitrary, proptest};
686
687 use super::*;
688
689 const MAX_OFFSET: u64 = 512;
690 const MAX_LEN: usize = 64;
691
692 #[derive(Debug, Clone, Arbitrary)]
693 enum Op {
694 #[weight(10)]
695 Insert {
696 #[strategy(0..MAX_OFFSET)]
697 offset: u64,
698 #[strategy(1..MAX_LEN)]
699 len: usize,
700 },
701 #[weight(10)]
702 Read {
703 #[strategy(1..MAX_LEN)]
704 max_len: usize,
705 },
706 #[weight(1)]
707 EnsureOrdering { ordered: bool },
708 #[weight(1)]
709 Defragment,
710 }
711
712 struct RefState {
714 received: Vec<bool>,
715 returned: Vec<bool>,
716 ordered: bool,
717 }
718
719 fn set_range(bits: &mut [bool], start: u64, len: usize) {
720 for i in start..(start + len as u64).min(bits.len() as u64) {
721 bits[i as usize] = true;
722 }
723 }
724
725 impl RefState {
726 fn new() -> Self {
727 Self {
728 received: vec![false; MAX_OFFSET as usize],
729 returned: vec![false; MAX_OFFSET as usize],
730 ordered: true,
731 }
732 }
733
734 fn insert(&mut self, offset: u64, len: usize) {
735 set_range(&mut self.received, offset, len);
736 }
737
738 fn ensure_ordering(&mut self, ordered: bool) -> bool {
739 if ordered && !self.ordered {
740 return false;
741 }
742 self.ordered = ordered;
743 true
744 }
745
746 fn bytes_read(&self) -> u64 {
747 self.returned.iter().filter(|&&x| x).count() as u64
748 }
749 }
750
751 fn make_data() -> Vec<u8> {
752 use rand::SeedableRng;
753 let mut rng = rand::rngs::StdRng::seed_from_u64(0xDEADBEEF);
754 let mut data = vec![0u8; MAX_OFFSET as usize];
755 rng.fill(data.as_mut_slice());
756 data
757 }
758
759 fn get_slice(data: &[u8], offset: u64, len: usize) -> Bytes {
760 let start = offset as usize;
761 let end = (start + len).min(data.len());
762 Bytes::copy_from_slice(&data[start..end])
763 }
764
765 fn verify_chunk(data: &[u8], chunk: &Chunk) -> bool {
766 let start = chunk.offset as usize;
767 chunk.bytes[..] == data[start..start + chunk.bytes.len()]
768 }
769
770 #[proptest]
771 fn assembler_matches_reference(
772 #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
773 ) {
774 let data = make_data();
775 let mut asm = Assembler::new();
776 let mut reference = RefState::new();
777
778 for op in ops {
779 match op {
780 Op::Insert { offset, len } => {
781 let bytes = get_slice(&data, offset, len);
782 asm.insert(offset, bytes, len);
783 reference.insert(offset, len);
784 }
785 Op::Read { max_len } => {
786 let ordered = reference.ordered;
787 let actual = asm.read(max_len, ordered);
788
789 match actual {
790 None => {
791 let has_available = if ordered {
793 reference
795 .returned
796 .iter()
797 .position(|&x| !x)
798 .is_some_and(|pos| reference.received[pos])
799 } else {
800 reference
802 .received
803 .iter()
804 .zip(&reference.returned)
805 .any(|(&r, &ret)| r && !ret)
806 };
807 prop_assert!(
808 !has_available,
809 "read returned None but data was available"
810 );
811 }
812 Some(chunk) => {
813 prop_assert!(chunk.bytes.len() <= max_len, "chunk exceeds max_len");
814 prop_assert!(verify_chunk(&data, &chunk), "data corruption");
815 for i in 0..chunk.bytes.len() {
817 let offset = chunk.offset as usize + i;
818 prop_assert!(
819 reference.received[offset],
820 "returned unreceived byte at {offset}"
821 );
822 prop_assert!(
823 !reference.returned[offset],
824 "duplicate byte at {offset}"
825 );
826 reference.returned[offset] = true;
827 }
828 }
829 }
830 }
831 Op::EnsureOrdering { ordered } => {
832 let actual = asm.ensure_ordering(ordered).is_ok();
833 let expected = reference.ensure_ordering(ordered);
834 prop_assert_eq!(actual, expected, "ensure_ordering result mismatch");
835 }
836 Op::Defragment => {
837 if asm.state.is_ordered() {
838 asm.defragment();
839 }
840 }
841 }
842 }
843
844 prop_assert_eq!(
846 asm.bytes_read(),
847 reference.bytes_read(),
848 "bytes_read mismatch"
849 );
850 }
851}