harmos_storage/
file_batch.rs1use std::borrow::Borrow;
20use std::num::NonZeroUsize;
21use std::ops::Range;
22use std::sync::Arc;
23
24use crate::{Error, FileBatch, MemorySize, ReadBatch, Result};
28use harmos_domain::model::storage::files::FileEvent;
29
30#[derive(Clone)]
39pub struct FileReadBatch {
40 owner: ReadBatch<FileBatch>,
41}
42
43#[derive(Clone)]
44pub struct FileReadSlice {
45 owner: FileReadBatch,
46 range: Range<usize>,
47}
48
49pub type FileReadEvent = ReadBatch<FileEvent>;
50
51impl FileReadEvent {
58 pub fn event(&self) -> &FileEvent {
59 self.as_ref()
60 }
61
62 pub fn data(&self) -> Option<&[u8]> {
63 match self.event() {
64 FileEvent::Data(batch) => Some(&batch.bytes),
65 _ => None,
66 }
67 }
68
69 pub fn offset(&self) -> Option<u64> {
70 match self.event() {
71 FileEvent::Data(batch) => Some(batch.offset),
72 _ => None,
73 }
74 }
75}
76
77impl FileReadBatch {
78 pub(crate) fn new(batch: FileBatch) -> Self {
79 Self { owner: ReadBatch::new(batch) }
80 }
81
82 #[tracing::instrument(name = "storage.file_batch.try_map", level = "trace", skip_all, fields(outcome = "cancelled"))]
85 pub fn try_map<U, E>(self, map: impl FnOnce(FileBatch) -> std::result::Result<U, E>) -> std::result::Result<ReadBatch<U>, E>
86 where
87 E: From<Error>,
88 {
89 let result: std::result::Result<ReadBatch<U>, E> = self.owner.try_map(map);
90 tracing::Span::current().record("outcome", if result.is_ok() { "completed" } else { "failed" });
91 result
92 }
93
94 pub fn offset(&self) -> u64 {
95 self.owner.offset
96 }
97
98 pub fn bytes(&self) -> &[u8] {
99 &self.owner.bytes
100 }
101
102 pub fn owner(&self) -> Arc<impl Borrow<FileBatch> + Send + Sync + 'static> {
103 self.owner.owner()
104 }
105
106 pub fn slice(&self, range: Range<usize>) -> FileReadSlice {
107 assert!(range.start <= range.end && range.end <= self.bytes().len(), "file slice is outside its batch");
108 FileReadSlice { owner: self.clone(), range }
109 }
110}
111
112impl Borrow<FileBatch> for FileReadBatch {
113 fn borrow(&self) -> &FileBatch {
114 self.owner.as_ref()
115 }
116}
117
118impl FileReadSlice {
119 pub fn offset(&self) -> u64 {
120 self.owner.offset() + self.range.start as u64
121 }
122
123 pub fn bytes(&self) -> &[u8] {
124 &self.owner.bytes()[self.range.clone()]
125 }
126}
127
128impl MemorySize for FileReadBatch {
129 fn memory_size(&self) -> usize {
130 self.owner.memory_size().saturating_add(std::mem::size_of::<Self>())
131 }
132}
133
134impl std::fmt::Debug for FileReadBatch {
135 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 self.owner.fmt(formatter)
137 }
138}
139
140pub(crate) fn batch_limit(ceiling: usize, offset: u64, remaining: u64, event: bool) -> Result<NonZeroUsize> {
143 let mut count = ceiling.min(usize::try_from(remaining).unwrap_or(usize::MAX));
144 loop {
145 let batch = 2usize.saturating_add(varint(offset)).saturating_add(varint(count as u64)).saturating_add(count);
146 let framed = if event { batch.saturating_add(1 + varint(batch as u64)) } else { batch };
147 if framed <= ceiling {
148 return NonZeroUsize::new(count).ok_or(Error::ResourceExhausted { resource: "file response batch bytes" });
149 }
150 count = count
151 .checked_sub(framed - ceiling)
152 .filter(|count| *count != 0)
153 .ok_or(Error::ResourceExhausted { resource: "file response batch bytes" })?;
154 }
155}
156
157fn varint(mut value: u64) -> usize {
160 let mut bytes = 1;
161 while value >= 128 {
162 value >>= 7;
163 bytes += 1;
164 }
165 bytes
166}
167
168