1use crate::errors::ParquetError;
19use crate::file::reader::{ChunkReader, Length};
20use bytes::Bytes;
21use std::cmp::Ordering;
22use std::collections::VecDeque;
23use std::fmt::Display;
24use std::ops::Range;
25
26#[derive(Debug, Clone, Default)]
52pub struct PushBuffers {
53 offset: u64,
55 file_len: u64,
57 ranges: VecDeque<Range<u64>>,
63 buffers: VecDeque<Bytes>,
66 max_len: MaxLen,
74 buffered_bytes: u64,
77}
78
79impl Display for PushBuffers {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 writeln!(
82 f,
83 "Buffers (offset: {}, file_len: {})",
84 self.offset, self.file_len
85 )?;
86 writeln!(f, "Available Ranges (w/ offset):")?;
87 for range in &self.ranges {
88 writeln!(
89 f,
90 " {}..{} ({}..{}): {} bytes",
91 range.start,
92 range.end,
93 range.start + self.offset,
94 range.end + self.offset,
95 range.end - range.start
96 )?;
97 }
98
99 Ok(())
100 }
101}
102
103impl PushBuffers {
104 pub fn new(file_len: u64) -> Self {
110 Self {
111 offset: 0,
112 file_len,
113 ranges: VecDeque::new(),
114 buffers: VecDeque::new(),
115 max_len: MaxLen::default(),
116 buffered_bytes: 0,
117 }
118 }
119
120 pub fn push_ranges(
127 &mut self,
128 ranges: Vec<Range<u64>>,
129 buffers: Vec<Bytes>,
130 ) -> Result<(), ParquetError> {
131 if ranges.len() != buffers.len() {
132 return Err(general_err!(
133 "Number of ranges ({}) must match number of buffers ({})",
134 ranges.len(),
135 buffers.len()
136 ));
137 }
138 for (range, buffer) in ranges.into_iter().zip(buffers) {
139 self.push_range(range, buffer)?;
140 }
141 Ok(())
142 }
143
144 pub fn push_range(&mut self, range: Range<u64>, buffer: Bytes) -> Result<(), ParquetError> {
153 let expected = range.end.saturating_sub(range.start);
154 if expected != buffer.len() as u64 {
155 return Err(general_err!(
156 "Buffer length ({}) does not match length ({}) of range {}..{}",
157 buffer.len(),
158 expected,
159 range.start,
160 range.end
161 ));
162 }
163 if expected == 0 {
164 return Ok(());
165 }
166 let idx = self.ranges.partition_point(|r| r.start <= range.start);
169 self.max_len.add(expected);
170 self.buffered_bytes += expected;
171 self.ranges.insert(idx, range);
172 self.buffers.insert(idx, buffer);
173 Ok(())
174 }
175
176 pub(crate) fn has_range(&self, range: &Range<u64>) -> bool {
180 range.is_empty() || self.find(range.start, range.end).is_some()
181 }
182
183 fn find(&self, start: u64, end: u64) -> Option<usize> {
186 let candidates = self.ranges.partition_point(|r| r.start <= start);
213 self.ranges
214 .range(..candidates)
215 .enumerate()
216 .rev()
217 .take_while(|(_, r)| r.start.saturating_add(self.max_len.get()) >= end)
218 .find(|(_, r)| r.end >= end)
219 .map(|(idx, _)| idx)
220 }
221
222 pub(crate) fn file_len(&self) -> u64 {
224 self.file_len
225 }
226
227 fn with_offset(mut self, offset: u64) -> Self {
229 self.offset = offset;
230 self
231 }
232
233 #[cfg(feature = "arrow")]
240 pub(crate) fn buffered_bytes(&self) -> u64 {
241 self.buffered_bytes
242 }
243
244 #[cfg(feature = "arrow")]
249 pub(crate) fn clear_ranges(&mut self, ranges_to_clear: &[Range<u64>]) {
250 let mut remove = vec![];
253 for clear in ranges_to_clear {
254 let first = self.ranges.partition_point(|r| r.start < clear.start);
255 let same_start = self
256 .ranges
257 .range(first..)
258 .take_while(|r| r.start == clear.start);
259 remove.extend(
260 same_start
261 .enumerate()
262 .filter(|(_, r)| r.end == clear.end)
263 .map(|(idx, _)| first + idx),
264 );
265 }
266 remove.sort_unstable();
269 remove.dedup();
270 let (Some(&first), Some(&last)) = (remove.first(), remove.last()) else {
271 return;
272 };
273 let mut kept = first;
277 let mut remove = remove.into_iter().peekable();
278 for idx in first..=last {
279 if remove.next_if_eq(&idx).is_some() {
280 let len = self.ranges[idx].end.saturating_sub(self.ranges[idx].start);
281 self.buffered_bytes -= len;
282 self.max_len.remove(len);
283 } else {
284 self.ranges.swap(kept, idx);
285 self.buffers.swap(kept, idx);
286 kept += 1;
287 }
288 }
289 self.ranges.drain(kept..=last);
290 self.buffers.drain(kept..=last);
291 self.max_len
293 .rescan_if_stale(self.ranges.iter().map(|r| r.end.saturating_sub(r.start)));
294 }
295
296 #[cfg(feature = "arrow")]
311 pub(crate) fn release_ranges(&mut self, ranges: &[Range<u64>]) {
312 let release = merge_ranges(ranges);
313 if release.is_empty() {
314 return;
315 }
316 let overlaps = |range: &Range<u64>| {
317 !range.is_empty() && {
318 let first = release.partition_point(|r| r.end <= range.start);
319 release.get(first).is_some_and(|r| r.start < range.end)
320 }
321 };
322 let mut overlapping = vec![];
333 let mut visited = 0;
334 for r in &release {
335 let max_len = self.max_len.get();
336 let first = self
337 .ranges
338 .partition_point(|b| b.start.saturating_add(max_len) <= r.start);
339 let end = self.ranges.partition_point(|b| b.start < r.end);
340 overlapping
343 .extend((first.max(visited)..end).filter(|&idx| overlaps(&self.ranges[idx])));
344 visited = visited.max(end);
345 }
346 let (Some(&first), Some(&last)) = (overlapping.first(), overlapping.last()) else {
349 return;
350 };
351 let mut split = vec![];
356 let mut emptied = false;
357 for idx in overlapping {
358 let (range, buffer) = (&mut self.ranges[idx], &mut self.buffers[idx]);
359 let whole = range.clone();
360 self.max_len.remove(whole.end - whole.start);
362 let offset = |pos: u64| (pos - whole.start) as usize;
363 let first = release.partition_point(|r| r.end <= whole.start);
365 let mut kept = 0;
366 let mut first_part = None;
367 let mut keep = |part: Range<u64>| {
368 kept += part.end - part.start;
369 if first_part.is_none() {
370 first_part = Some(part);
371 } else {
372 let data = buffer.slice(offset(part.start)..offset(part.end));
373 split.push((part, data));
374 }
375 };
376 let mut start = whole.start;
377 for r in release[first..].iter().take_while(|r| r.start < whole.end) {
378 if start < r.start {
379 keep(start..r.start);
380 }
381 start = r.end;
382 }
383 if start < whole.end {
384 keep(start..whole.end);
385 }
386 self.buffered_bytes -= (whole.end - whole.start) - kept;
387 match first_part {
388 Some(part) => {
389 buffer.truncate(offset(part.end));
390 let _ = buffer.split_to(offset(part.start));
391 *range = part;
392 }
393 None => {
394 *buffer = Bytes::new();
395 *range = whole.start..whole.start;
396 emptied = true;
397 }
398 }
399 }
400 if emptied {
401 let span = first..last + 1;
403 let mut kept = span.start;
404 for idx in span.clone() {
405 if !self.ranges[idx].is_empty() {
406 self.ranges.swap(kept, idx);
407 self.buffers.swap(kept, idx);
408 kept += 1;
409 }
410 }
411 self.ranges.drain(kept..span.end);
412 self.buffers.drain(kept..span.end);
413 }
414 if !split.is_empty() {
420 let mut parts: Vec<_> = std::mem::take(&mut self.ranges)
421 .into_iter()
422 .zip(std::mem::take(&mut self.buffers))
423 .chain(split)
424 .collect();
425 parts.sort_by_key(|(range, _)| range.start);
426 (self.ranges, self.buffers) = parts.into_iter().unzip();
427 }
428 self.max_len
432 .rescan_if_stale(self.ranges.iter().map(|r| r.end.saturating_sub(r.start)));
433 }
434
435 #[cfg(feature = "arrow")]
440 pub(crate) fn retain_ranges(&mut self, keep: &[Range<u64>]) {
441 let mut release = vec![];
442 let mut start = 0;
443 for range in merge_ranges(keep) {
444 release.push(start..range.start);
445 start = range.end;
446 }
447 release.push(start..u64::MAX);
448 self.release_ranges(&release);
449 }
450
451 pub(crate) fn clear_all_ranges(&mut self) {
453 self.ranges.clear();
454 self.buffers.clear();
455 self.max_len = MaxLen::default();
456 self.buffered_bytes = 0;
457 }
458
459 #[cfg(test)]
462 #[track_caller]
463 fn assert_invariants(&self) {
464 assert_eq!(self.ranges.len(), self.buffers.len());
465 assert!(
466 self.ranges.iter().is_sorted_by_key(|r| r.start),
467 "not sorted: {:?}",
468 self.ranges
469 );
470 for (range, buffer) in self.ranges.iter().zip(&self.buffers) {
471 assert_eq!(range.end.saturating_sub(range.start), buffer.len() as u64);
472 }
473 let max_len = self
474 .ranges
475 .iter()
476 .map(|r| r.end.saturating_sub(r.start))
477 .max();
478 let max_len = max_len.unwrap_or(0);
479 assert_eq!(self.max_len.len, max_len);
480 let is_longest = |r: &&Range<u64>| r.end.saturating_sub(r.start) == max_len;
481 assert_eq!(
482 self.max_len.count,
483 self.ranges.iter().filter(is_longest).count()
484 );
485 assert!(
486 self.ranges.iter().all(|r| !r.is_empty()),
487 "empty buffer: {:?}",
488 self.ranges
489 );
490 }
491}
492
493#[cfg(feature = "arrow")]
496fn merge_ranges(ranges: &[Range<u64>]) -> Vec<Range<u64>> {
497 let mut sorted: Vec<Range<u64>> = ranges.iter().filter(|r| !r.is_empty()).cloned().collect();
498 sorted.sort_unstable_by_key(|r| r.start);
499 let mut merged: Vec<Range<u64>> = Vec::with_capacity(sorted.len());
500 for range in sorted {
501 match merged.last_mut() {
502 Some(last) if range.start <= last.end => last.end = last.end.max(range.end),
503 _ => merged.push(range),
504 }
505 }
506 merged
507}
508
509impl Length for PushBuffers {
510 fn len(&self) -> u64 {
511 self.file_len
512 }
513}
514
515impl std::io::Read for PushBuffers {
517 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
518 if buf.is_empty() {
519 return Ok(0);
520 }
521 let found = self.find(self.offset, self.offset + buf.len() as u64);
523 if let Some(idx) = found {
524 let start_offset = (self.offset - self.ranges[idx].start) as usize;
526 let end_offset = start_offset + buf.len();
527 buf.copy_from_slice(&self.buffers[idx][start_offset..end_offset]);
528 self.offset += buf.len() as u64;
531 Ok(buf.len())
532 } else {
533 Err(std::io::Error::new(
534 std::io::ErrorKind::UnexpectedEof,
535 "No data available in Buffers",
536 ))
537 }
538 }
539}
540
541impl ChunkReader for PushBuffers {
542 type T = Self;
543
544 fn get_read(&self, start: u64) -> Result<Self::T, ParquetError> {
545 Ok(self.clone().with_offset(self.offset + start))
546 }
547
548 fn get_bytes(&self, start: u64, length: usize) -> Result<Bytes, ParquetError> {
549 if length == 0 {
551 return Ok(Bytes::new());
552 }
553 if let Some(idx) = self.find(start, start + length as u64) {
555 let start_offset = (start - self.ranges[idx].start) as usize;
557 return Ok(self.buffers[idx].slice(start_offset..start_offset + length));
558 }
559 let requested_end = start + length as u64;
561 Err(ParquetError::NeedMoreDataRange(start..requested_end))
562 }
563}
564
565#[derive(Debug, Default, Clone, Copy)]
572struct MaxLen {
573 len: u64,
574 count: usize,
575}
576
577impl MaxLen {
578 #[inline]
582 fn get(&self) -> u64 {
583 self.len
584 }
585
586 #[inline]
588 fn add(&mut self, len: u64) {
589 match len.cmp(&self.len) {
590 Ordering::Greater => (self.len, self.count) = (len, 1),
591 Ordering::Equal => self.count += 1,
592 Ordering::Less => {}
593 }
594 }
595
596 #[cfg(feature = "arrow")]
599 #[inline]
600 fn remove(&mut self, len: u64) {
601 if len == self.len {
602 self.count -= 1;
603 }
604 }
605
606 #[cfg(feature = "arrow")]
613 #[inline]
614 fn rescan_if_stale(&mut self, lens: impl IntoIterator<Item = u64>) {
615 if self.count == 0 {
616 *self = Self::default();
617 lens.into_iter().for_each(|len| self.add(len));
618 }
619 }
620}
621
622#[cfg(test)]
623mod tests {
624 use super::*;
625
626 #[cfg(feature = "arrow")]
628 fn release(buffers: &mut PushBuffers, range: Range<u64>) {
629 buffers.release_ranges(std::slice::from_ref(&range));
630 buffers.assert_invariants();
631 }
632
633 #[cfg(feature = "arrow")]
635 fn buffered_parts(buffers: &PushBuffers) -> Vec<(Range<u64>, Bytes)> {
636 let ranges = buffers.ranges.iter().cloned();
637 ranges.zip(buffers.buffers.iter().cloned()).collect()
638 }
639
640 #[test]
641 #[cfg(feature = "arrow")]
642 fn release_ranges_without_overlap_keeps_buffers() {
643 let mut buffers = PushBuffers::new(100);
644 buffers
645 .push_range(20..24, Bytes::from_static(b"abcd"))
646 .unwrap();
647 buffers
648 .push_range(0..10, Bytes::from_static(b"0123456789"))
649 .unwrap();
650
651 release(&mut buffers, 90..95);
652 release(&mut buffers, 10..20);
653 assert_eq!(buffers.ranges, vec![0..10, 20..24]);
654 assert_eq!(buffers.buffered_bytes(), 14);
655
656 release(&mut buffers, 22..24);
657 assert_eq!(buffers.ranges, vec![0..10, 20..22]);
658 assert_eq!(buffers.buffered_bytes(), 12);
659
660 buffers.clear_all_ranges();
661 assert_eq!(buffers.buffered_bytes(), 0);
662 }
663
664 #[test]
665 #[cfg(feature = "arrow")]
666 fn release_ranges_trims_and_splits_buffers() {
667 let mut buffers = PushBuffers::new(100);
668 buffers
669 .push_range(0..10, Bytes::from_static(b"0123456789"))
670 .unwrap();
671 buffers
672 .push_range(20..24, Bytes::from_static(b"abcd"))
673 .unwrap();
674
675 release(&mut buffers, 3..5);
677 assert_eq!(buffers.buffered_bytes(), 12);
678 assert!(buffers.has_range(&(0..3)));
679 assert!(buffers.has_range(&(5..10)));
680 assert!(!buffers.has_range(&(3..4)));
681 assert_eq!(
682 buffers.get_bytes(5, 5).unwrap(),
683 Bytes::from_static(b"56789")
684 );
685 assert!(buffers.has_range(&(20..24)));
686
687 release(&mut buffers, 8..22);
689 assert_eq!(buffers.buffered_bytes(), 3 + 3 + 2);
690 assert_eq!(buffers.get_bytes(5, 3).unwrap(), Bytes::from_static(b"567"));
691 assert_eq!(buffers.get_bytes(22, 2).unwrap(), Bytes::from_static(b"cd"));
692
693 buffers.release_ranges(&[50..60, 1..2, 0..1, 7..7]);
696 assert_eq!(buffers.buffered_bytes(), 1 + 3 + 2);
697 assert_eq!(buffers.get_bytes(2, 1).unwrap(), Bytes::from_static(b"2"));
698
699 release(&mut buffers, 0..100);
700 assert_eq!(buffers.buffered_bytes(), 0);
701 }
702
703 #[test]
704 #[cfg(feature = "arrow")]
705 fn release_ranges_removes_exact_and_overlapping_buffers() {
706 let mut buffers = PushBuffers::new(100);
707 for range in [10..20, 0..30, 10..15, 40..50] {
708 let data = Bytes::from(vec![0u8; (range.end - range.start) as usize]);
709 buffers.push_range(range, data).unwrap();
710 }
711 buffers.release_ranges(&[40..50, 10..15]);
712 buffers.assert_invariants();
713 assert_eq!(buffers.buffered_bytes(), 10 + 5 + 15);
714 assert!(buffers.has_range(&(0..10)));
715 assert!(buffers.has_range(&(15..30)));
716 assert!(!buffers.has_range(&(10..15)));
717 assert!(!buffers.has_range(&(40..50)));
718 }
719
720 #[test]
721 #[cfg(feature = "arrow")]
722 fn retain_ranges_keeps_only_the_given_bytes() {
723 let mut buffers = PushBuffers::new(100);
724 buffers
725 .push_range(0..10, Bytes::from_static(b"0123456789"))
726 .unwrap();
727 buffers
728 .push_range(20..24, Bytes::from_static(b"abcd"))
729 .unwrap();
730 buffers
731 .push_range(30..32, Bytes::from_static(b"xy"))
732 .unwrap();
733 buffers.retain_ranges(&[22..40, 2..4, 3..5, 8..9]);
735 assert_eq!(buffers.buffered_bytes(), 3 + 1 + 2 + 2);
736 assert_eq!(buffers.get_bytes(2, 3).unwrap(), Bytes::from_static(b"234"));
737 assert_eq!(buffers.get_bytes(8, 1).unwrap(), Bytes::from_static(b"8"));
738 assert!(!buffers.has_range(&(5..6)));
739 assert_eq!(buffers.get_bytes(22, 2).unwrap(), Bytes::from_static(b"cd"));
740 assert_eq!(buffers.get_bytes(30, 2).unwrap(), Bytes::from_static(b"xy"));
741 buffers.retain_ranges(&[]);
742 assert_eq!(buffers.buffered_bytes(), 0);
743 }
744
745 #[test]
748 #[cfg(feature = "arrow")]
749 fn retain_ranges_with_overlapping_buffers() {
750 let mut buffers = PushBuffers::new(100);
751 for range in [0..100, 3..70] {
752 let data = Bytes::from(vec![0u8; (range.end - range.start) as usize]);
753 buffers.push_range(range, data).unwrap();
754 }
755 buffers.retain_ranges(&[0..5, 50..60]);
756 buffers.assert_invariants();
757 assert_eq!(buffers.buffered_bytes(), 5 + 2 + 10 + 10);
758 assert!(buffers.has_range(&(0..5)));
759 assert!(buffers.has_range(&(50..60)));
760 assert!(!buffers.has_range(&(5..50)));
761 }
762
763 #[test]
764 fn push_range_accepts_matching_length() {
765 let mut buffers = PushBuffers::new(100);
766 buffers
767 .push_range(10..14, Bytes::from_static(b"abcd"))
768 .unwrap();
769 assert!(buffers.has_range(&(10..14)));
770 }
771
772 #[test]
773 fn push_range_rejects_short_buffer() {
774 let mut buffers = PushBuffers::new(100);
775 let err = buffers
776 .push_range(10..20, Bytes::from_static(b"abcd"))
777 .unwrap_err();
778 assert_eq!(
779 err.to_string(),
780 "Parquet error: Buffer length (4) does not match length (10) of range 10..20"
781 );
782 assert!(!buffers.has_range(&(10..20)));
783 }
784
785 fn file_bytes(range: Range<u64>) -> Bytes {
788 range.map(|i| (i % 251) as u8).collect::<Vec<u8>>().into()
789 }
790
791 fn push(buffers: &mut PushBuffers, range: Range<u64>) {
792 buffers
793 .push_range(range.clone(), file_bytes(range))
794 .unwrap();
795 }
796
797 #[track_caller]
800 fn assert_valid(buffers: &PushBuffers) {
801 buffers.assert_invariants();
802 for (range, buffer) in buffers.ranges.iter().zip(&buffers.buffers) {
803 assert_eq!(*buffer, file_bytes(range.clone()));
804 }
805 }
806
807 #[test]
808 fn overlapping_pushes_find_the_containing_buffer() {
809 let mut buffers = PushBuffers::new(1000);
810 for range in [50..60, 0..100, 55..58, 10..20] {
813 push(&mut buffers, range);
814 }
815 assert_valid(&buffers);
816 assert_eq!(buffers.get_bytes(55, 35).unwrap(), file_bytes(55..90));
818 assert!(buffers.has_range(&(0..100)));
819 assert!(buffers.has_range(&(99..100)));
820 assert!(!buffers.has_range(&(99..101)));
821 assert!(matches!(
822 buffers.get_bytes(90, 20),
823 Err(ParquetError::NeedMoreDataRange(r)) if r == (90..110)
824 ));
825
826 let mut reader = buffers.get_read(56).unwrap();
828 let mut out = [0u8; 30];
829 std::io::Read::read_exact(&mut reader, &mut out).unwrap();
830 assert_eq!(&out[..], &file_bytes(56..86)[..]);
831 }
832
833 #[test]
834 #[cfg(feature = "arrow")]
835 fn clear_ranges_drops_exact_matches_only() {
836 let mut buffers = PushBuffers::new(1000);
837 for range in [10..20, 0..30, 10..15, 40..50] {
838 push(&mut buffers, range);
839 }
840 buffers.clear_ranges(&[40..50, 10..15, 5..30]);
841 assert_valid(&buffers);
842 assert_eq!(buffers.ranges, vec![0..30, 10..20]);
843 }
844
845 #[test]
846 #[cfg(feature = "arrow")]
847 fn clear_ranges_with_buffers_of_the_same_start() {
848 let mut buffers = PushBuffers::new(1000);
849 for range in [0..5, 10..20, 10..15, 10..20, 10..30, 10..12, 40..50] {
850 push(&mut buffers, range);
851 }
852 buffers.clear_ranges(&[10..30, 10..20, 10..20, 60..70]);
855 assert_valid(&buffers);
856 assert_eq!(buffers.ranges, vec![0..5, 10..15, 10..12, 40..50]);
857 assert_eq!(buffers.buffered_bytes(), 22);
858
859 buffers.clear_ranges(&[10..20, 0..50]);
861 assert_valid(&buffers);
862 assert_eq!(buffers.ranges, vec![0..5, 10..15, 10..12, 40..50]);
863 }
864
865 #[test]
868 #[cfg(feature = "arrow")]
869 fn fuzz_matches_a_linear_scan() {
870 use rand::rngs::StdRng;
871 use rand::{RngExt, SeedableRng};
872
873 fn random_range(rng: &mut StdRng) -> Range<u64> {
874 let start = rng.random_range(0..500);
875 let len = [0, 1, 5, 20, 100, 300][rng.random_range(0..6)];
876 start..start + len
877 }
878
879 for seed in 0..200 {
880 let mut rng = StdRng::seed_from_u64(seed);
881 let mut buffers = PushBuffers::new(1000);
882 let mut model: Vec<Range<u64>> = vec![];
883 for _ in 0..60 {
884 match rng.random_range(0..10) {
885 0..4 => {
886 let range = random_range(&mut rng);
887 push(&mut buffers, range.clone());
888 if !range.is_empty() {
890 model.push(range);
891 }
892 }
893 4..6 => {
894 let mut clear: Vec<_> = (0..rng.random_range(1..4))
896 .map(|_| random_range(&mut rng))
897 .collect();
898 if !model.is_empty() {
899 clear.push(model[rng.random_range(0..model.len())].clone());
900 }
901 buffers.clear_ranges(&clear);
902 model.retain(|r| !clear.contains(r));
903 }
904 _ => {
905 let range = random_range(&mut rng);
906 let expected = range.is_empty()
907 || model
908 .iter()
909 .any(|r| r.start <= range.start && r.end >= range.end);
910 assert_eq!(buffers.has_range(&range), expected, "seed {seed} {range:?}");
911 if expected {
912 let len = (range.end - range.start) as usize;
913 assert_eq!(
914 buffers.get_bytes(range.start, len).unwrap(),
915 file_bytes(range.clone())
916 );
917 }
918 }
919 }
920 assert_valid(&buffers);
921 let mut actual = Vec::from(buffers.ranges.clone());
922 actual.sort_by_key(|r| (r.start, r.end));
923 model.sort_by_key(|r| (r.start, r.end));
924 assert_eq!(actual, model, "seed {seed}");
925 }
926 }
927 }
928
929 #[test]
930 fn push_ranges_rejects_mismatched_counts() {
931 let mut buffers = PushBuffers::new(100);
932 let err = buffers
933 .push_ranges(vec![0..4, 4..8], vec![Bytes::from_static(b"abcd")])
934 .unwrap_err();
935 assert_eq!(
936 err.to_string(),
937 "Parquet error: Number of ranges (2) must match number of buffers (1)"
938 );
939 }
940
941 #[cfg(feature = "arrow")]
943 fn release_by_rebuild(
944 buffers: &PushBuffers,
945 release: &[Range<u64>],
946 ) -> Vec<(Range<u64>, Bytes)> {
947 let release = merge_ranges(release);
948 let mut parts = vec![];
949 for (range, buffer) in &buffered_parts(buffers) {
950 let mut start = range.start;
951 let mut keep = |part: Range<u64>| {
952 let offset = |pos: u64| (pos - range.start) as usize;
953 parts.push((
954 part.clone(),
955 buffer.slice(offset(part.start)..offset(part.end)),
956 ));
957 };
958 for r in release
959 .iter()
960 .filter(|r| r.start < range.end && r.end > range.start)
961 {
962 if start < r.start {
963 keep(start..r.start);
964 }
965 start = start.max(r.end);
966 }
967 if start < range.end {
968 keep(start..range.end);
969 }
970 }
971 parts.sort_by_key(|(range, _)| range.start);
972 parts
973 }
974
975 #[test]
976 #[expect(clippy::reversed_empty_ranges)]
977 fn empty_ranges() {
978 use std::io::Read;
979 let mut buffers = PushBuffers::new(100);
981 assert!(buffers.has_range(&(50..50)));
982 assert_eq!(buffers.get_bytes(50, 0).unwrap(), Bytes::new());
983 assert_eq!(buffers.get_read(50).unwrap().read(&mut []).unwrap(), 0);
984
985 buffers.push_range(30..30, Bytes::new()).unwrap();
987 buffers.push_range(10..5, Bytes::new()).unwrap();
988 assert!(buffers.ranges.is_empty());
989 assert!(buffers.buffers.is_empty());
990 buffers.assert_invariants();
991
992 buffers
994 .push_range(20..25, Bytes::from_static(b"abcde"))
995 .unwrap();
996 buffers.push_range(22..22, Bytes::new()).unwrap();
997 buffers.assert_invariants();
998 assert_eq!(buffers.ranges, vec![20..25]);
999 assert!(buffers.has_range(&(21..24)));
1000 assert_eq!(
1001 buffers.get_bytes(21, 3).unwrap(),
1002 Bytes::from_static(b"bcd")
1003 );
1004 let mut buf = [0; 2];
1005 buffers.get_read(23).unwrap().read_exact(&mut buf).unwrap();
1006 assert_eq!(&buf, b"de");
1007 }
1008
1009 #[test]
1010 #[cfg(feature = "arrow")]
1011 #[expect(clippy::reversed_empty_ranges)]
1012 fn release_ranges_splits_and_removes_empty_buffers() {
1013 let mut buffers = PushBuffers::new(100);
1014 buffers
1015 .push_range(0..20, Bytes::from_static(b"abcdefghijklmnopqrst"))
1016 .unwrap();
1017 buffers.push_range(30..30, Bytes::new()).unwrap();
1019 buffers.push_range(10..5, Bytes::new()).unwrap();
1020
1021 buffers.release_ranges(&[3..5, 8..10]);
1023 assert_eq!(buffers.ranges, vec![0..3, 5..8, 10..20]);
1024 assert_eq!(buffers.buffered_bytes(), 16);
1025 assert_eq!(buffers.get_bytes(5, 3).unwrap(), Bytes::from_static(b"fgh"));
1026
1027 release(&mut buffers, 5..8);
1029 assert_eq!(buffers.ranges, vec![0..3, 10..20]);
1030 assert_eq!(buffers.buffered_bytes(), 13);
1031 }
1032
1033 #[test]
1034 #[cfg(feature = "arrow")]
1035 fn fuzz_release_ranges_matches_rebuild() {
1036 use rand::{RngExt, SeedableRng, rngs::StdRng};
1037 let data: Vec<u8> = (0..=255).collect();
1038 let mut rng = StdRng::seed_from_u64(42);
1039 for _ in 0..2000 {
1040 let mut buffers = PushBuffers::new(256);
1041 for _ in 0..rng.random_range(0..6) {
1042 let start = rng.random_range(0..100u64);
1043 if rng.random_bool(0.1) {
1044 let end = start.saturating_sub(rng.random_range(1..5u64));
1046 buffers.push_range(start..end, Bytes::new()).unwrap();
1047 continue;
1048 }
1049 let end = start + rng.random_range(0..20u64);
1050 let bytes = Bytes::copy_from_slice(&data[start as usize..end as usize]);
1051 buffers.push_range(start..end, bytes).unwrap();
1052 }
1053 for _ in 0..3 {
1054 let release: Vec<_> = (0..rng.random_range(0..4))
1055 .map(|_| {
1056 let start = rng.random_range(0..110u64);
1057 start..start + rng.random_range(0..15u64)
1058 })
1059 .collect();
1060 let overlaps = buffers.ranges.iter().any(|range| {
1061 !range.is_empty()
1062 && release
1063 .iter()
1064 .any(|r| !r.is_empty() && r.start < range.end && r.end > range.start)
1065 });
1066 let before = buffered_parts(&buffers);
1067 let expected = release_by_rebuild(&buffers, &release);
1068
1069 buffers.release_ranges(&release);
1070 buffers.assert_invariants();
1071 let actual = buffered_parts(&buffers);
1072 if overlaps {
1073 let by_range = |mut parts: Vec<(Range<u64>, Bytes)>| {
1076 parts.sort_by_key(|(range, _)| (range.start, range.end));
1077 parts
1078 };
1079 assert_eq!(
1080 by_range(actual.clone()),
1081 by_range(expected),
1082 "release {release:?} of {before:?}"
1083 );
1084 } else {
1085 assert_eq!(actual, before, "release {release:?} of {before:?}");
1086 }
1087 for (range, buffer) in actual.iter().filter(|(range, _)| !range.is_empty()) {
1088 assert_eq!(
1089 &data[range.start as usize..range.end as usize],
1090 buffer.as_ref()
1091 );
1092 }
1093 let total: u64 = actual
1094 .iter()
1095 .map(|(r, _)| r.end.saturating_sub(r.start))
1096 .sum();
1097 assert_eq!(buffers.buffered_bytes(), total);
1098 }
1099 }
1100 }
1101}