1use std::ops::Range;
18use std::sync::Arc;
19pub type FileChunk = StorageBatch<u8>;
29
30#[derive(Debug)]
33pub struct StorageBatch<T> {
34 values: Arc<Vec<T>>,
35 range: Range<usize>,
36}
37
38impl<T> StorageBatch<T> {
45 pub fn new(values: Vec<T>) -> Self {
46 let range = 0..values.len();
47 Self { values: Arc::new(values), range }
48 }
49
50 pub(crate) fn merge(batches: &mut [&mut Self]) -> Option<Self> {
51 let first = batches.first()?;
52 let mut range = first.range.clone();
53 let adjoining = batches.iter().skip(1).all(|batch| {
54 let joined = Arc::ptr_eq(&first.values, &batch.values) && range.end == batch.range.start;
55 range.end = batch.range.end;
56 joined
57 });
58 if adjoining {
59 return Some(Self { values: Arc::clone(&first.values), range });
60 }
61 if batches.iter().any(|batch| Arc::strong_count(&batch.values) != 1 || batch.range != (0..batch.values.len())) {
62 return None;
63 }
64 let count = batches.iter().map(|batch| batch.len()).sum();
65 let lengths: Vec<_> = batches.iter().map(|batch| batch.len()).collect();
66 let mut values = Vec::with_capacity(count);
67 for batch in batches.iter_mut() {
68 let source = std::mem::take(Arc::get_mut(&mut batch.values).expect("unique queued backing"));
69 values.extend(source);
70 }
71 let merged = Self {
72 values: Arc::new(values),
73 range: 0..count,
74 };
75 let mut start = 0;
76 for (batch, length) in batches.iter_mut().zip(lengths) {
77 **batch = merged.slice(start..start + length);
78 start += length;
79 }
80 Some(merged)
81 }
82
83 pub fn as_slice(&self) -> &[T] {
84 &self.values[self.range.clone()]
85 }
86
87 pub fn slice(&self, range: Range<usize>) -> Self {
88 assert!(range.start <= range.end && range.end <= self.len(), "batch slice is outside its span");
89 let range = self.range.start + range.start..self.range.start + range.end;
90 Self { values: Arc::clone(&self.values), range }
91 }
92
93 pub fn len(&self) -> usize {
94 self.range.len()
95 }
96
97 pub fn is_empty(&self) -> bool {
98 self.range.is_empty()
99 }
100
101 pub fn capacity(&self) -> usize {
102 self.values.capacity()
103 }
104
105 pub fn allocation_bytes(&self) -> usize {
106 self.values.capacity().saturating_mul(std::mem::size_of::<T>())
107 }
108}
109
110impl From<Vec<u8>> for StorageBatch<u8> {
111 fn from(values: Vec<u8>) -> Self {
112 Self::new(values)
113 }
114}
115
116impl<T> Clone for StorageBatch<T> {
117 fn clone(&self) -> Self {
118 Self {
119 values: Arc::clone(&self.values),
120 range: self.range.clone(),
121 }
122 }
123}
124
125#[cfg(test)]
132mod tests {
133 use super::{FileChunk, StorageBatch};
134
135 #[test]
136 fn chunk_allocation() {
137 let mut bytes = Vec::with_capacity(1024);
138 bytes.extend_from_slice(&[1, 2, 3, 4]);
139 let pointer = bytes.as_ptr();
140 let capacity = bytes.capacity();
141 let batch = FileChunk::from(bytes);
142 assert_eq!(batch.as_slice().as_ptr(), pointer);
143 let retained = batch.slice(1..3);
144 let empty = batch.slice(4..4);
145 assert_eq!(retained.allocation_bytes(), capacity);
146 assert!(empty.is_empty());
147 drop(batch);
148 assert_eq!(retained.as_slice(), &[2, 3]);
149 assert_eq!(retained.as_slice().as_ptr(), pointer.wrapping_add(1));
150 }
151
152 #[test]
153 fn payload_merge() {
154 let left = vec![7u8; 32];
155 let right = vec![8u8; 32];
156 let pointers = [left.as_ptr(), right.as_ptr()];
157 let mut first = StorageBatch::new(vec![left]);
158 let mut second = StorageBatch::new(vec![right]);
159 let merged = StorageBatch::merge(&mut [&mut first, &mut second]).unwrap();
160 assert_eq!(merged.as_slice()[0].as_ptr(), pointers[0]);
161 assert_eq!(merged.as_slice()[1].as_ptr(), pointers[1]);
162 let detached = merged.clone();
163 let retry = StorageBatch::merge(&mut [&mut first, &mut second]).unwrap();
164 assert_eq!(retry.len(), 2);
165 assert_eq!(retry.as_slice()[1].as_ptr(), pointers[1]);
166 drop(detached);
167 }
168
169 #[test]
170 fn shared_merge_refused() {
171 let mut first = StorageBatch::new(vec![vec![7u8; 32]]);
172 let mut second = StorageBatch::new(vec![vec![8u8; 32]]);
173 let detached = first.clone();
174 assert!(StorageBatch::merge(&mut [&mut first, &mut second]).is_none());
175 assert_eq!(first.as_slice(), detached.as_slice());
176 assert_eq!(second.as_slice()[0], vec![8; 32]);
177 }
178}