1use crate::error::{Result, StreamingError};
4use bytes::Bytes;
5use std::collections::VecDeque;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8use tracing::debug;
9
10#[derive(Debug, Clone)]
12pub struct ChunkDescriptor {
13 pub offset: u64,
15
16 pub length: usize,
18
19 pub index: usize,
21
22 pub total_chunks: usize,
24
25 pub is_last: bool,
27}
28
29impl ChunkDescriptor {
30 pub fn new(offset: u64, length: usize, index: usize, total_chunks: usize) -> Self {
32 Self {
33 offset,
34 length,
35 index,
36 total_chunks,
37 is_last: index + 1 == total_chunks,
38 }
39 }
40
41 pub fn end_offset(&self) -> u64 {
43 self.offset + self.length as u64
44 }
45}
46
47pub struct ChunkedBuffer {
49 inner: Arc<RwLock<ChunkedBufferInner>>,
51
52 chunk_size: usize,
54
55 max_size: usize,
57}
58
59struct ChunkedBufferInner {
60 chunks: VecDeque<BufferedChunk>,
62
63 current_size: usize,
65
66 next_read_index: usize,
68
69 next_write_index: usize,
71
72 total_chunks: Option<usize>,
74
75 write_complete: bool,
77}
78
79struct BufferedChunk {
80 descriptor: ChunkDescriptor,
81 data: Bytes,
82}
83
84impl ChunkedBuffer {
85 pub fn new(chunk_size: usize, max_size: usize) -> Self {
87 Self {
88 inner: Arc::new(RwLock::new(ChunkedBufferInner {
89 chunks: VecDeque::new(),
90 current_size: 0,
91 next_read_index: 0,
92 next_write_index: 0,
93 total_chunks: None,
94 write_complete: false,
95 })),
96 chunk_size,
97 max_size,
98 }
99 }
100
101 pub fn with_defaults() -> Self {
103 Self::new(1024 * 1024, 100 * 1024 * 1024) }
105
106 pub fn calculate_chunks(&self, total_size: u64) -> usize {
108 total_size.div_ceil(self.chunk_size as u64) as usize
109 }
110
111 pub fn descriptor_for_index(&self, index: usize, total_size: u64) -> ChunkDescriptor {
113 let total_chunks = self.calculate_chunks(total_size);
114 let offset = (index as u64) * (self.chunk_size as u64);
115 let remaining = total_size.saturating_sub(offset);
116 let length = remaining.min(self.chunk_size as u64) as usize;
117
118 ChunkDescriptor::new(offset, length, index, total_chunks)
119 }
120
121 pub async fn push(&self, descriptor: ChunkDescriptor, data: Bytes) -> Result<()> {
123 let mut inner = self.inner.write().await;
124
125 if inner.current_size + data.len() > self.max_size {
127 return Err(StreamingError::BufferFull);
128 }
129
130 if descriptor.index != inner.next_write_index {
132 return Err(StreamingError::InvalidOperation(format!(
133 "Expected chunk {}, got {}",
134 inner.next_write_index, descriptor.index
135 )));
136 }
137
138 inner.chunks.push_back(BufferedChunk {
139 descriptor: descriptor.clone(),
140 data,
141 });
142
143 inner.current_size += descriptor.length;
144 inner.next_write_index += 1;
145
146 if let Some(total) = inner.total_chunks {
147 if descriptor.index + 1 == total {
148 inner.write_complete = true;
149 }
150 } else if descriptor.is_last {
151 inner.total_chunks = Some(descriptor.total_chunks);
152 inner.write_complete = true;
153 }
154
155 debug!(
156 "Pushed chunk {} ({} bytes), buffer size: {}",
157 descriptor.index, descriptor.length, inner.current_size
158 );
159
160 Ok(())
161 }
162
163 pub async fn pop(&self) -> Result<Option<(ChunkDescriptor, Bytes)>> {
165 let mut inner = self.inner.write().await;
166
167 if inner.chunks.is_empty() {
168 if inner.write_complete {
169 return Ok(None);
170 } else {
171 return Err(StreamingError::Other("No chunks available".to_string()));
172 }
173 }
174
175 let chunk = inner
176 .chunks
177 .pop_front()
178 .ok_or_else(|| StreamingError::Other("Failed to pop chunk".to_string()))?;
179
180 inner.current_size = inner.current_size.saturating_sub(chunk.descriptor.length);
181 inner.next_read_index += 1;
182
183 debug!(
184 "Popped chunk {} ({} bytes), buffer size: {}",
185 chunk.descriptor.index, chunk.descriptor.length, inner.current_size
186 );
187
188 Ok(Some((chunk.descriptor, chunk.data)))
189 }
190
191 pub async fn peek(&self) -> Result<Option<ChunkDescriptor>> {
193 let inner = self.inner.read().await;
194 Ok(inner.chunks.front().map(|c| c.descriptor.clone()))
195 }
196
197 pub async fn len(&self) -> usize {
199 let inner = self.inner.read().await;
200 inner.chunks.len()
201 }
202
203 pub async fn is_empty(&self) -> bool {
205 let inner = self.inner.read().await;
206 inner.chunks.is_empty()
207 }
208
209 pub async fn rebase_if_empty(&self, index: usize) -> bool {
223 let mut inner = self.inner.write().await;
224 if !inner.chunks.is_empty() {
225 return false;
226 }
227 inner.next_write_index = index;
228 inner.next_read_index = index;
229 debug!("Buffer write cursor re-based to chunk {index}");
230 true
231 }
232
233 pub async fn size_bytes(&self) -> usize {
235 let inner = self.inner.read().await;
236 inner.current_size
237 }
238
239 pub async fn is_complete(&self) -> bool {
241 let inner = self.inner.read().await;
242 inner.write_complete
243 }
244
245 pub async fn clear(&self) {
247 let mut inner = self.inner.write().await;
248 inner.chunks.clear();
249 inner.current_size = 0;
250 debug!("Buffer cleared");
251 }
252
253 pub async fn stats(&self) -> BufferStats {
255 let inner = self.inner.read().await;
256 BufferStats {
257 chunks_buffered: inner.chunks.len(),
258 bytes_buffered: inner.current_size,
259 max_bytes: self.max_size,
260 utilization: (inner.current_size as f64) / (self.max_size as f64),
261 chunks_read: inner.next_read_index,
262 chunks_written: inner.next_write_index,
263 total_chunks: inner.total_chunks,
264 complete: inner.write_complete,
265 }
266 }
267}
268
269#[derive(Debug, Clone)]
271pub struct BufferStats {
272 pub chunks_buffered: usize,
274
275 pub bytes_buffered: usize,
277
278 pub max_bytes: usize,
280
281 pub utilization: f64,
283
284 pub chunks_read: usize,
286
287 pub chunks_written: usize,
289
290 pub total_chunks: Option<usize>,
292
293 pub complete: bool,
295}
296
297impl BufferStats {
298 pub fn progress(&self) -> Option<f64> {
300 self.total_chunks.map(|total| {
301 if total == 0 {
302 100.0
303 } else {
304 (self.chunks_read as f64 / total as f64) * 100.0
305 }
306 })
307 }
308}
309
310pub struct CircularChunkBuffer {
312 buffer: Vec<u8>,
314
315 read_pos: usize,
317
318 write_pos: usize,
320
321 available: usize,
323
324 capacity: usize,
326}
327
328impl CircularChunkBuffer {
329 pub fn new(capacity: usize) -> Self {
331 Self {
332 buffer: vec![0; capacity],
333 read_pos: 0,
334 write_pos: 0,
335 available: 0,
336 capacity,
337 }
338 }
339
340 pub fn write(&mut self, data: &[u8]) -> Result<usize> {
342 let space_available = self.capacity - self.available;
343 let to_write = data.len().min(space_available);
344
345 if to_write == 0 {
346 return Ok(0);
347 }
348
349 let end_pos = self.write_pos + to_write;
350 if end_pos <= self.capacity {
351 self.buffer[self.write_pos..end_pos].copy_from_slice(&data[..to_write]);
353 self.write_pos = end_pos % self.capacity;
354 } else {
355 let first_part = self.capacity - self.write_pos;
357 self.buffer[self.write_pos..].copy_from_slice(&data[..first_part]);
358 self.buffer[..to_write - first_part].copy_from_slice(&data[first_part..to_write]);
359 self.write_pos = to_write - first_part;
360 }
361
362 self.available += to_write;
363 Ok(to_write)
364 }
365
366 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
368 let to_read = buf.len().min(self.available);
369
370 if to_read == 0 {
371 return Ok(0);
372 }
373
374 let end_pos = self.read_pos + to_read;
375 if end_pos <= self.capacity {
376 buf[..to_read].copy_from_slice(&self.buffer[self.read_pos..end_pos]);
378 self.read_pos = end_pos % self.capacity;
379 } else {
380 let first_part = self.capacity - self.read_pos;
382 buf[..first_part].copy_from_slice(&self.buffer[self.read_pos..]);
383 buf[first_part..to_read].copy_from_slice(&self.buffer[..to_read - first_part]);
384 self.read_pos = to_read - first_part;
385 }
386
387 self.available -= to_read;
388 Ok(to_read)
389 }
390
391 pub fn available(&self) -> usize {
393 self.available
394 }
395
396 pub fn space_available(&self) -> usize {
398 self.capacity - self.available
399 }
400
401 pub fn is_empty(&self) -> bool {
403 self.available == 0
404 }
405
406 pub fn is_full(&self) -> bool {
408 self.available == self.capacity
409 }
410
411 pub fn clear(&mut self) {
413 self.read_pos = 0;
414 self.write_pos = 0;
415 self.available = 0;
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[tokio::test]
424 async fn test_chunked_buffer() {
425 let buffer = ChunkedBuffer::new(1024, 10240);
426
427 let desc = ChunkDescriptor::new(0, 1024, 0, 10);
428 let data = Bytes::from(vec![0u8; 1024]);
429
430 buffer.push(desc.clone(), data.clone()).await.ok();
431
432 assert_eq!(buffer.len().await, 1);
433 assert_eq!(buffer.size_bytes().await, 1024);
434
435 let popped = buffer.pop().await.ok().flatten();
436 assert!(popped.is_some());
437
438 assert_eq!(buffer.len().await, 0);
439 }
440
441 #[test]
442 fn test_circular_buffer() {
443 let mut buffer = CircularChunkBuffer::new(10);
444
445 let written = buffer.write(&[1, 2, 3, 4, 5]).ok();
447 assert_eq!(written, Some(5));
448 assert_eq!(buffer.available(), 5);
449
450 let mut read_buf = [0u8; 3];
452 let read = buffer.read(&mut read_buf).ok();
453 assert_eq!(read, Some(3));
454 assert_eq!(read_buf, [1, 2, 3]);
455 assert_eq!(buffer.available(), 2);
456
457 let written = buffer.write(&[6, 7, 8, 9, 10]).ok();
459 assert_eq!(written, Some(5));
460 assert_eq!(buffer.available(), 7);
461 }
462
463 #[test]
464 fn test_chunk_descriptor() {
465 let desc = ChunkDescriptor::new(0, 1024, 0, 10);
466 assert_eq!(desc.offset, 0);
467 assert_eq!(desc.length, 1024);
468 assert_eq!(desc.end_offset(), 1024);
469 assert!(!desc.is_last);
470
471 let last_desc = ChunkDescriptor::new(9216, 1024, 9, 10);
472 assert!(last_desc.is_last);
473 }
474}