1use crate::errors::ParquetError;
19use crate::file::reader::{ChunkReader, Length};
20use bytes::Bytes;
21use std::fmt::Display;
22use std::ops::Range;
23
24#[derive(Debug, Clone, Default)]
50pub struct PushBuffers {
51 offset: u64,
53 file_len: u64,
55 ranges: Vec<Range<u64>>,
58 buffers: Vec<Bytes>,
61 max_len: u64,
69 buffered_bytes: u64,
72}
73
74impl Display for PushBuffers {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 writeln!(
77 f,
78 "Buffers (offset: {}, file_len: {})",
79 self.offset, self.file_len
80 )?;
81 writeln!(f, "Available Ranges (w/ offset):")?;
82 for range in &self.ranges {
83 writeln!(
84 f,
85 " {}..{} ({}..{}): {} bytes",
86 range.start,
87 range.end,
88 range.start + self.offset,
89 range.end + self.offset,
90 range.end - range.start
91 )?;
92 }
93
94 Ok(())
95 }
96}
97
98impl PushBuffers {
99 pub fn new(file_len: u64) -> Self {
105 Self {
106 offset: 0,
107 file_len,
108 ranges: Vec::new(),
109 buffers: Vec::new(),
110 max_len: 0,
111 buffered_bytes: 0,
112 }
113 }
114
115 pub fn push_ranges(
122 &mut self,
123 ranges: Vec<Range<u64>>,
124 buffers: Vec<Bytes>,
125 ) -> Result<(), ParquetError> {
126 if ranges.len() != buffers.len() {
127 return Err(general_err!(
128 "Number of ranges ({}) must match number of buffers ({})",
129 ranges.len(),
130 buffers.len()
131 ));
132 }
133 for (range, buffer) in ranges.into_iter().zip(buffers) {
134 self.push_range(range, buffer)?;
135 }
136 Ok(())
137 }
138
139 pub fn push_range(&mut self, range: Range<u64>, buffer: Bytes) -> Result<(), ParquetError> {
145 let expected = range.end.saturating_sub(range.start);
146 if expected != buffer.len() as u64 {
147 return Err(general_err!(
148 "Buffer length ({}) does not match length ({}) of range {}..{}",
149 buffer.len(),
150 expected,
151 range.start,
152 range.end
153 ));
154 }
155 let idx = self.ranges.partition_point(|r| r.start <= range.start);
158 self.max_len = self.max_len.max(expected);
159 self.buffered_bytes += expected;
160 self.ranges.insert(idx, range);
161 self.buffers.insert(idx, buffer);
162 Ok(())
163 }
164
165 pub(crate) fn has_range(&self, range: &Range<u64>) -> bool {
167 self.find(range.start, range.end).is_some()
168 }
169
170 fn find(&self, start: u64, end: u64) -> Option<usize> {
173 let candidates = self.ranges.partition_point(|r| r.start <= start);
200 self.ranges[..candidates]
201 .iter()
202 .enumerate()
203 .rev()
204 .take_while(|(_, r)| r.start.saturating_add(self.max_len) >= end)
205 .find(|(_, r)| r.end >= end)
206 .map(|(idx, _)| idx)
207 }
208
209 pub(crate) fn file_len(&self) -> u64 {
211 self.file_len
212 }
213
214 fn with_offset(mut self, offset: u64) -> Self {
216 self.offset = offset;
217 self
218 }
219
220 #[cfg(feature = "arrow")]
227 pub(crate) fn buffered_bytes(&self) -> u64 {
228 self.buffered_bytes
229 }
230
231 #[cfg(feature = "arrow")]
233 pub(crate) fn clear_ranges(&mut self, ranges_to_clear: &[Range<u64>]) {
234 let mut clear: Vec<(u64, u64)> = ranges_to_clear.iter().map(|r| (r.start, r.end)).collect();
236 if clear.is_empty() {
237 return;
238 }
239 clear.sort_unstable();
240 let mut ranges = Vec::with_capacity(self.ranges.len());
241 let mut buffers = Vec::with_capacity(self.buffers.len());
242 for (range, buffer) in self.ranges.drain(..).zip(self.buffers.drain(..)) {
243 if clear.binary_search(&(range.start, range.end)).is_err() {
244 ranges.push(range);
245 buffers.push(buffer);
246 }
247 }
248 self.ranges = ranges;
249 self.buffers = buffers;
250 self.buffered_bytes = self
251 .ranges
252 .iter()
253 .map(|r| r.end.saturating_sub(r.start))
254 .sum();
255 self.update_max_len();
256 }
257
258 #[cfg(feature = "arrow")]
264 fn update_max_len(&mut self) {
265 self.max_len = self
266 .ranges
267 .iter()
268 .map(|r| r.end.saturating_sub(r.start))
269 .max()
270 .unwrap_or(0);
271 }
272
273 #[cfg(feature = "arrow")]
285 pub(crate) fn release_ranges(&mut self, ranges: &[Range<u64>]) {
286 let release = merge_ranges(ranges);
287 if release.is_empty() {
288 return;
289 }
290 let overlaps = |range: &Range<u64>| {
293 !range.is_empty() && {
294 let first = release.partition_point(|r| r.end <= range.start);
295 release.get(first).is_some_and(|r| r.start < range.end)
296 }
297 };
298 if !self.ranges.iter().any(overlaps) {
299 return;
300 }
301 let mut split = vec![];
306 let mut emptied = false;
307 for (range, buffer) in self.ranges.iter_mut().zip(self.buffers.iter_mut()) {
308 if range.is_empty() {
309 emptied = true;
310 continue;
311 }
312 if !overlaps(range) {
313 continue;
314 }
315 let whole = range.clone();
316 let offset = |pos: u64| (pos - whole.start) as usize;
317 let first = release.partition_point(|r| r.end <= whole.start);
319 let mut kept = 0;
320 let mut first_part = None;
321 let mut keep = |part: Range<u64>| {
322 kept += part.end - part.start;
323 if first_part.is_none() {
324 first_part = Some(part);
325 } else {
326 let data = buffer.slice(offset(part.start)..offset(part.end));
327 split.push((part, data));
328 }
329 };
330 let mut start = whole.start;
331 for r in release[first..].iter().take_while(|r| r.start < whole.end) {
332 if start < r.start {
333 keep(start..r.start);
334 }
335 start = r.end;
336 }
337 if start < whole.end {
338 keep(start..whole.end);
339 }
340 self.buffered_bytes -= (whole.end - whole.start) - kept;
341 match first_part {
342 Some(part) => {
343 buffer.truncate(offset(part.end));
344 let _ = buffer.split_to(offset(part.start));
345 *range = part;
346 }
347 None => {
348 *buffer = Bytes::new();
349 *range = whole.start..whole.start;
350 emptied = true;
351 }
352 }
353 }
354 if emptied {
355 let mut keep = self.ranges.iter().map(|range| !range.is_empty());
359 self.buffers.retain(|_| keep.next().unwrap());
360 self.ranges.retain(|range| !range.is_empty());
361 }
362 for (range, buffer) in split {
363 self.ranges.push(range);
364 self.buffers.push(buffer);
365 }
366 if !self.ranges.is_sorted_by_key(|range| range.start) {
370 let mut parts: Vec<_> = std::mem::take(&mut self.ranges)
371 .into_iter()
372 .zip(std::mem::take(&mut self.buffers))
373 .collect();
374 parts.sort_by_key(|(range, _)| range.start);
375 (self.ranges, self.buffers) = parts.into_iter().unzip();
376 }
377 self.update_max_len();
378 }
379
380 #[cfg(feature = "arrow")]
385 pub(crate) fn retain_ranges(&mut self, keep: &[Range<u64>]) {
386 let mut release = vec![];
387 let mut start = 0;
388 for range in merge_ranges(keep) {
389 release.push(start..range.start);
390 start = range.end;
391 }
392 release.push(start..u64::MAX);
393 self.release_ranges(&release);
394 }
395
396 pub(crate) fn clear_all_ranges(&mut self) {
398 self.ranges.clear();
399 self.buffers.clear();
400 self.max_len = 0;
401 self.buffered_bytes = 0;
402 }
403
404 #[cfg(test)]
407 #[track_caller]
408 fn assert_invariants(&self) {
409 assert_eq!(self.ranges.len(), self.buffers.len());
410 assert!(
411 self.ranges.is_sorted_by_key(|r| r.start),
412 "not sorted: {:?}",
413 self.ranges
414 );
415 for (range, buffer) in self.ranges.iter().zip(&self.buffers) {
416 assert_eq!(range.end.saturating_sub(range.start), buffer.len() as u64);
417 }
418 let max_len = self
419 .ranges
420 .iter()
421 .map(|r| r.end.saturating_sub(r.start))
422 .max();
423 assert_eq!(self.max_len, max_len.unwrap_or(0));
424 }
425}
426
427#[cfg(feature = "arrow")]
430fn merge_ranges(ranges: &[Range<u64>]) -> Vec<Range<u64>> {
431 let mut sorted: Vec<Range<u64>> = ranges.iter().filter(|r| !r.is_empty()).cloned().collect();
432 sorted.sort_unstable_by_key(|r| r.start);
433 let mut merged: Vec<Range<u64>> = Vec::with_capacity(sorted.len());
434 for range in sorted {
435 match merged.last_mut() {
436 Some(last) if range.start <= last.end => last.end = last.end.max(range.end),
437 _ => merged.push(range),
438 }
439 }
440 merged
441}
442
443impl Length for PushBuffers {
444 fn len(&self) -> u64 {
445 self.file_len
446 }
447}
448
449impl std::io::Read for PushBuffers {
451 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
452 let found = self.find(self.offset, self.offset + buf.len() as u64);
454 if let Some(idx) = found {
455 let start_offset = (self.offset - self.ranges[idx].start) as usize;
457 let end_offset = start_offset + buf.len();
458 buf.copy_from_slice(&self.buffers[idx][start_offset..end_offset]);
459 self.offset += buf.len() as u64;
462 Ok(buf.len())
463 } else {
464 Err(std::io::Error::new(
465 std::io::ErrorKind::UnexpectedEof,
466 "No data available in Buffers",
467 ))
468 }
469 }
470}
471
472impl ChunkReader for PushBuffers {
473 type T = Self;
474
475 fn get_read(&self, start: u64) -> Result<Self::T, ParquetError> {
476 Ok(self.clone().with_offset(self.offset + start))
477 }
478
479 fn get_bytes(&self, start: u64, length: usize) -> Result<Bytes, ParquetError> {
480 if let Some(idx) = self.find(start, start + length as u64) {
482 let start_offset = (start - self.ranges[idx].start) as usize;
484 return Ok(self.buffers[idx].slice(start_offset..start_offset + length));
485 }
486 let requested_end = start + length as u64;
488 Err(ParquetError::NeedMoreDataRange(start..requested_end))
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use super::*;
495
496 #[cfg(feature = "arrow")]
498 fn release(buffers: &mut PushBuffers, range: Range<u64>) {
499 buffers.release_ranges(std::slice::from_ref(&range));
500 buffers.assert_invariants();
501 }
502
503 #[cfg(feature = "arrow")]
505 fn buffered_parts(buffers: &PushBuffers) -> Vec<(Range<u64>, Bytes)> {
506 let ranges = buffers.ranges.iter().cloned();
507 ranges.zip(buffers.buffers.iter().cloned()).collect()
508 }
509
510 #[test]
511 #[cfg(feature = "arrow")]
512 fn release_ranges_without_overlap_keeps_buffers() {
513 let mut buffers = PushBuffers::new(100);
514 buffers
515 .push_range(20..24, Bytes::from_static(b"abcd"))
516 .unwrap();
517 buffers
518 .push_range(0..10, Bytes::from_static(b"0123456789"))
519 .unwrap();
520
521 release(&mut buffers, 90..95);
522 release(&mut buffers, 10..20);
523 assert_eq!(buffers.ranges, vec![0..10, 20..24]);
524 assert_eq!(buffers.buffered_bytes(), 14);
525
526 release(&mut buffers, 22..24);
527 assert_eq!(buffers.ranges, vec![0..10, 20..22]);
528 assert_eq!(buffers.buffered_bytes(), 12);
529
530 buffers.clear_all_ranges();
531 assert_eq!(buffers.buffered_bytes(), 0);
532 }
533
534 #[test]
535 #[cfg(feature = "arrow")]
536 fn release_ranges_trims_and_splits_buffers() {
537 let mut buffers = PushBuffers::new(100);
538 buffers
539 .push_range(0..10, Bytes::from_static(b"0123456789"))
540 .unwrap();
541 buffers
542 .push_range(20..24, Bytes::from_static(b"abcd"))
543 .unwrap();
544
545 release(&mut buffers, 3..5);
547 assert_eq!(buffers.buffered_bytes(), 12);
548 assert!(buffers.has_range(&(0..3)));
549 assert!(buffers.has_range(&(5..10)));
550 assert!(!buffers.has_range(&(3..4)));
551 assert_eq!(
552 buffers.get_bytes(5, 5).unwrap(),
553 Bytes::from_static(b"56789")
554 );
555 assert!(buffers.has_range(&(20..24)));
556
557 release(&mut buffers, 8..22);
559 assert_eq!(buffers.buffered_bytes(), 3 + 3 + 2);
560 assert_eq!(buffers.get_bytes(5, 3).unwrap(), Bytes::from_static(b"567"));
561 assert_eq!(buffers.get_bytes(22, 2).unwrap(), Bytes::from_static(b"cd"));
562
563 buffers.release_ranges(&[50..60, 1..2, 0..1, 7..7]);
566 assert_eq!(buffers.buffered_bytes(), 1 + 3 + 2);
567 assert_eq!(buffers.get_bytes(2, 1).unwrap(), Bytes::from_static(b"2"));
568
569 release(&mut buffers, 0..100);
570 assert_eq!(buffers.buffered_bytes(), 0);
571 }
572
573 #[test]
574 #[cfg(feature = "arrow")]
575 fn release_ranges_removes_exact_and_overlapping_buffers() {
576 let mut buffers = PushBuffers::new(100);
577 for range in [10..20, 0..30, 10..15, 40..50] {
578 let data = Bytes::from(vec![0u8; (range.end - range.start) as usize]);
579 buffers.push_range(range, data).unwrap();
580 }
581 buffers.release_ranges(&[40..50, 10..15]);
582 buffers.assert_invariants();
583 assert_eq!(buffers.buffered_bytes(), 10 + 5 + 15);
584 assert!(buffers.has_range(&(0..10)));
585 assert!(buffers.has_range(&(15..30)));
586 assert!(!buffers.has_range(&(10..15)));
587 assert!(!buffers.has_range(&(40..50)));
588 }
589
590 #[test]
591 #[cfg(feature = "arrow")]
592 fn retain_ranges_keeps_only_the_given_bytes() {
593 let mut buffers = PushBuffers::new(100);
594 buffers
595 .push_range(0..10, Bytes::from_static(b"0123456789"))
596 .unwrap();
597 buffers
598 .push_range(20..24, Bytes::from_static(b"abcd"))
599 .unwrap();
600 buffers
601 .push_range(30..32, Bytes::from_static(b"xy"))
602 .unwrap();
603 buffers.retain_ranges(&[22..40, 2..4, 3..5, 8..9]);
605 assert_eq!(buffers.buffered_bytes(), 3 + 1 + 2 + 2);
606 assert_eq!(buffers.get_bytes(2, 3).unwrap(), Bytes::from_static(b"234"));
607 assert_eq!(buffers.get_bytes(8, 1).unwrap(), Bytes::from_static(b"8"));
608 assert!(!buffers.has_range(&(5..6)));
609 assert_eq!(buffers.get_bytes(22, 2).unwrap(), Bytes::from_static(b"cd"));
610 assert_eq!(buffers.get_bytes(30, 2).unwrap(), Bytes::from_static(b"xy"));
611 buffers.retain_ranges(&[]);
612 assert_eq!(buffers.buffered_bytes(), 0);
613 }
614
615 #[test]
618 #[cfg(feature = "arrow")]
619 fn retain_ranges_with_overlapping_buffers() {
620 let mut buffers = PushBuffers::new(100);
621 for range in [0..100, 3..70] {
622 let data = Bytes::from(vec![0u8; (range.end - range.start) as usize]);
623 buffers.push_range(range, data).unwrap();
624 }
625 buffers.retain_ranges(&[0..5, 50..60]);
626 buffers.assert_invariants();
627 assert_eq!(buffers.buffered_bytes(), 5 + 2 + 10 + 10);
628 assert!(buffers.has_range(&(0..5)));
629 assert!(buffers.has_range(&(50..60)));
630 assert!(!buffers.has_range(&(5..50)));
631 }
632
633 #[test]
634 fn push_range_accepts_matching_length() {
635 let mut buffers = PushBuffers::new(100);
636 buffers
637 .push_range(10..14, Bytes::from_static(b"abcd"))
638 .unwrap();
639 assert!(buffers.has_range(&(10..14)));
640 }
641
642 #[test]
643 fn push_range_rejects_short_buffer() {
644 let mut buffers = PushBuffers::new(100);
645 let err = buffers
646 .push_range(10..20, Bytes::from_static(b"abcd"))
647 .unwrap_err();
648 assert_eq!(
649 err.to_string(),
650 "Parquet error: Buffer length (4) does not match length (10) of range 10..20"
651 );
652 assert!(!buffers.has_range(&(10..20)));
653 }
654
655 fn file_bytes(range: Range<u64>) -> Bytes {
658 range.map(|i| (i % 251) as u8).collect::<Vec<u8>>().into()
659 }
660
661 fn push(buffers: &mut PushBuffers, range: Range<u64>) {
662 buffers
663 .push_range(range.clone(), file_bytes(range))
664 .unwrap();
665 }
666
667 #[track_caller]
670 fn assert_valid(buffers: &PushBuffers) {
671 buffers.assert_invariants();
672 for (range, buffer) in buffers.ranges.iter().zip(&buffers.buffers) {
673 assert_eq!(*buffer, file_bytes(range.clone()));
674 }
675 }
676
677 #[test]
678 fn overlapping_pushes_find_the_containing_buffer() {
679 let mut buffers = PushBuffers::new(1000);
680 for range in [50..60, 0..100, 55..58, 10..20] {
683 push(&mut buffers, range);
684 }
685 assert_valid(&buffers);
686 assert_eq!(buffers.get_bytes(55, 35).unwrap(), file_bytes(55..90));
688 assert!(buffers.has_range(&(0..100)));
689 assert!(buffers.has_range(&(99..100)));
690 assert!(!buffers.has_range(&(99..101)));
691 assert!(matches!(
692 buffers.get_bytes(90, 20),
693 Err(ParquetError::NeedMoreDataRange(r)) if r == (90..110)
694 ));
695
696 let mut reader = buffers.get_read(56).unwrap();
698 let mut out = [0u8; 30];
699 std::io::Read::read_exact(&mut reader, &mut out).unwrap();
700 assert_eq!(&out[..], &file_bytes(56..86)[..]);
701 }
702
703 #[test]
704 #[cfg(feature = "arrow")]
705 fn clear_ranges_drops_exact_matches_only() {
706 let mut buffers = PushBuffers::new(1000);
707 for range in [10..20, 0..30, 10..15, 40..50] {
708 push(&mut buffers, range);
709 }
710 buffers.clear_ranges(&[40..50, 10..15, 5..30]);
711 assert_valid(&buffers);
712 assert_eq!(buffers.ranges, vec![0..30, 10..20]);
713 }
714
715 #[test]
718 #[cfg(feature = "arrow")]
719 fn fuzz_matches_a_linear_scan() {
720 use rand::rngs::StdRng;
721 use rand::{RngExt, SeedableRng};
722
723 fn random_range(rng: &mut StdRng) -> Range<u64> {
724 let start = rng.random_range(0..500);
725 let len = [0, 1, 5, 20, 100, 300][rng.random_range(0..6)];
726 start..start + len
727 }
728
729 for seed in 0..200 {
730 let mut rng = StdRng::seed_from_u64(seed);
731 let mut buffers = PushBuffers::new(1000);
732 let mut model: Vec<Range<u64>> = vec![];
733 for _ in 0..60 {
734 match rng.random_range(0..10) {
735 0..4 => {
736 let range = random_range(&mut rng);
737 push(&mut buffers, range.clone());
738 model.push(range);
739 }
740 4..6 => {
741 let mut clear: Vec<_> = (0..rng.random_range(1..4))
743 .map(|_| random_range(&mut rng))
744 .collect();
745 if !model.is_empty() {
746 clear.push(model[rng.random_range(0..model.len())].clone());
747 }
748 buffers.clear_ranges(&clear);
749 model.retain(|r| !clear.contains(r));
750 }
751 _ => {
752 let range = random_range(&mut rng);
753 let expected = model
754 .iter()
755 .any(|r| r.start <= range.start && r.end >= range.end);
756 assert_eq!(buffers.has_range(&range), expected, "seed {seed} {range:?}");
757 if expected {
758 let len = (range.end - range.start) as usize;
759 assert_eq!(
760 buffers.get_bytes(range.start, len).unwrap(),
761 file_bytes(range.clone())
762 );
763 }
764 }
765 }
766 assert_valid(&buffers);
767 let mut actual = buffers.ranges.clone();
768 actual.sort_by_key(|r| (r.start, r.end));
769 model.sort_by_key(|r| (r.start, r.end));
770 assert_eq!(actual, model, "seed {seed}");
771 }
772 }
773 }
774
775 #[test]
776 fn push_ranges_rejects_mismatched_counts() {
777 let mut buffers = PushBuffers::new(100);
778 let err = buffers
779 .push_ranges(vec![0..4, 4..8], vec![Bytes::from_static(b"abcd")])
780 .unwrap_err();
781 assert_eq!(
782 err.to_string(),
783 "Parquet error: Number of ranges (2) must match number of buffers (1)"
784 );
785 }
786
787 #[cfg(feature = "arrow")]
789 fn release_by_rebuild(
790 buffers: &PushBuffers,
791 release: &[Range<u64>],
792 ) -> Vec<(Range<u64>, Bytes)> {
793 let release = merge_ranges(release);
794 let mut parts = vec![];
795 for (range, buffer) in &buffered_parts(buffers) {
796 let mut start = range.start;
797 let mut keep = |part: Range<u64>| {
798 let offset = |pos: u64| (pos - range.start) as usize;
799 parts.push((
800 part.clone(),
801 buffer.slice(offset(part.start)..offset(part.end)),
802 ));
803 };
804 for r in release
805 .iter()
806 .filter(|r| r.start < range.end && r.end > range.start)
807 {
808 if start < r.start {
809 keep(start..r.start);
810 }
811 start = start.max(r.end);
812 }
813 if start < range.end {
814 keep(start..range.end);
815 }
816 }
817 parts.sort_by_key(|(range, _)| range.start);
818 parts
819 }
820
821 #[test]
822 #[cfg(feature = "arrow")]
823 #[expect(clippy::reversed_empty_ranges)]
824 fn release_ranges_splits_and_removes_empty_buffers() {
825 let mut buffers = PushBuffers::new(100);
826 buffers
827 .push_range(0..20, Bytes::from_static(b"abcdefghijklmnopqrst"))
828 .unwrap();
829 buffers.push_range(30..30, Bytes::new()).unwrap();
831 buffers.push_range(10..5, Bytes::new()).unwrap();
832
833 buffers.release_ranges(&[3..5, 8..10]);
835 assert_eq!(buffers.ranges, vec![0..3, 5..8, 10..20]);
836 assert_eq!(buffers.buffered_bytes(), 16);
837 assert_eq!(buffers.get_bytes(5, 3).unwrap(), Bytes::from_static(b"fgh"));
838
839 release(&mut buffers, 5..8);
841 assert_eq!(buffers.ranges, vec![0..3, 10..20]);
842 assert_eq!(buffers.buffered_bytes(), 13);
843 }
844
845 #[test]
846 #[cfg(feature = "arrow")]
847 fn fuzz_release_ranges_matches_rebuild() {
848 use rand::{RngExt, SeedableRng, rngs::StdRng};
849 let data: Vec<u8> = (0..=255).collect();
850 let mut rng = StdRng::seed_from_u64(42);
851 for _ in 0..2000 {
852 let mut buffers = PushBuffers::new(256);
853 for _ in 0..rng.random_range(0..6) {
854 let start = rng.random_range(0..100u64);
855 if rng.random_bool(0.1) {
856 let end = start.saturating_sub(rng.random_range(1..5u64));
858 buffers.push_range(start..end, Bytes::new()).unwrap();
859 continue;
860 }
861 let end = start + rng.random_range(0..20u64);
862 let bytes = Bytes::copy_from_slice(&data[start as usize..end as usize]);
863 buffers.push_range(start..end, bytes).unwrap();
864 }
865 for _ in 0..3 {
866 let release: Vec<_> = (0..rng.random_range(0..4))
867 .map(|_| {
868 let start = rng.random_range(0..110u64);
869 start..start + rng.random_range(0..15u64)
870 })
871 .collect();
872 let overlaps = buffers.ranges.iter().any(|range| {
873 !range.is_empty()
874 && release
875 .iter()
876 .any(|r| !r.is_empty() && r.start < range.end && r.end > range.start)
877 });
878 let before = buffered_parts(&buffers);
879 let expected = release_by_rebuild(&buffers, &release);
880
881 buffers.release_ranges(&release);
882 buffers.assert_invariants();
883 let actual = buffered_parts(&buffers);
884 if overlaps {
885 let by_range = |mut parts: Vec<(Range<u64>, Bytes)>| {
888 parts.sort_by_key(|(range, _)| (range.start, range.end));
889 parts
890 };
891 assert_eq!(
892 by_range(actual.clone()),
893 by_range(expected),
894 "release {release:?} of {before:?}"
895 );
896 } else {
897 assert_eq!(actual, before, "release {release:?} of {before:?}");
898 }
899 for (range, buffer) in actual.iter().filter(|(range, _)| !range.is_empty()) {
900 assert_eq!(
901 &data[range.start as usize..range.end as usize],
902 buffer.as_ref()
903 );
904 }
905 let total: u64 = actual
906 .iter()
907 .map(|(r, _)| r.end.saturating_sub(r.start))
908 .sum();
909 assert_eq!(buffers.buffered_bytes(), total);
910 }
911 }
912 }
913}