1use std::num::NonZeroUsize;
18use super::manager::encoded_size;
22use super::{FileReference, ObjectEntry, StorageCursor, StorageSchema, StoredRecord, StoredRow};
23use crate::{Error, Result};
24
25#[derive(Clone, Copy)]
34pub struct StorageBatch<'a> {
35 parts: &'a [StoragePart<'a>],
36 first: &'a StorageCursor,
37 through: &'a StorageCursor,
38 len: usize,
39 bytes: usize,
40}
41
42#[derive(Clone, Copy)]
45pub enum StoragePart<'a> {
46 Schemas(&'a [StorageSchema]),
47 Objects(&'a [ObjectEntry]),
48 Records(&'a [StoredRecord]),
49 Stream(&'a [StoredRow]),
50 Files(&'a [FileReference]),
51}
52
53#[derive(Clone, Copy)]
54pub enum StorageEntry<'a> {
55 Schema(&'a StorageSchema),
56 Object(&'a ObjectEntry),
57 Record(&'a StoredRecord),
58 Row(&'a StoredRow),
59 File(&'a FileReference),
60}
61
62impl<'a> StorageBatch<'a> {
67 pub fn new(parts: &'a [StoragePart<'a>], max_bytes: NonZeroUsize) -> Result<Self> {
68 let mut entries = parts.iter().copied().flat_map(StoragePart::entries);
69 let first = entries.next().ok_or(invalid("batch is empty"))?;
70 let mut through = first.position();
71 if through.sequence == 0 {
72 return Err(invalid("position zero is not a data item"));
73 }
74 let mut bytes = first.encoded_bytes()?;
75 let mut len = 1usize;
76 for entry in entries {
77 through.history.check(&entry.position().history)?;
78 if through.sequence.checked_add(1) != Some(entry.position().sequence) {
79 return Err(invalid("batch positions are not contiguous"));
80 }
81 bytes = bytes.checked_add(entry.encoded_bytes()?).ok_or(invalid("batch byte count overflow"))?;
82 if bytes > max_bytes.get() {
83 return Err(invalid("batch exceeds the encoded byte bound"));
84 }
85 len = len.checked_add(1).ok_or(invalid("batch entry count overflow"))?;
86 through = entry.position();
87 }
88 if bytes > max_bytes.get() {
89 return Err(invalid("batch exceeds the encoded byte bound"));
90 }
91 Ok(Self { parts, first: first.position(), through, len, bytes })
92 }
93
94 pub fn parts(&self) -> impl Iterator<Item = StoragePart<'a>> + '_ {
95 self.parts.iter().copied().filter_map(|part| {
96 let start = part.partition(self.first.sequence, false);
97 let end = part.partition(self.through.sequence, true);
98 (end > start).then(|| part.slice(start, end))
99 })
100 }
101
102 pub fn entries(&self) -> impl Iterator<Item = StorageEntry<'a>> + '_ {
103 self.parts().flat_map(StoragePart::entries)
104 }
105
106 pub fn first(&self) -> &'a StorageCursor {
107 self.first
108 }
109
110 pub fn through(&self) -> &'a StorageCursor {
111 self.through
112 }
113
114 pub fn len(&self) -> usize {
115 self.len
116 }
117
118 pub fn is_empty(&self) -> bool {
119 false
120 }
121
122 pub fn encoded_bytes(&self) -> usize {
123 self.bytes
124 }
125}
126
127impl<'a> StoragePart<'a> {
128 fn partition(self, sequence: u64, inclusive: bool) -> usize {
131 let mut start = 0;
132 let mut end = self.len();
133 while start < end {
134 let middle = start + (end - start) / 2;
135 let position = self.slice(middle, middle + 1).entries().next().unwrap().position().sequence;
136 if position < sequence || (inclusive && position == sequence) {
137 start = middle + 1;
138 } else {
139 end = middle;
140 }
141 }
142 start
143 }
144
145 fn slice(self, start: usize, end: usize) -> Self {
146 match self {
147 Self::Schemas(values) => Self::Schemas(&values[start..end]),
148 Self::Objects(values) => Self::Objects(&values[start..end]),
149 Self::Records(values) => Self::Records(&values[start..end]),
150 Self::Stream(values) => Self::Stream(&values[start..end]),
151 Self::Files(values) => Self::Files(&values[start..end]),
152 }
153 }
154
155 pub fn len(&self) -> usize {
156 match self {
157 Self::Schemas(values) => values.len(),
158 Self::Objects(values) => values.len(),
159 Self::Records(values) => values.len(),
160 Self::Stream(values) => values.len(),
161 Self::Files(values) => values.len(),
162 }
163 }
164
165 pub fn is_empty(&self) -> bool {
166 self.len() == 0
167 }
168
169 pub fn entries(self) -> impl Iterator<Item = StorageEntry<'a>> + 'a {
170 (0..self.len()).map(move |index| match self {
171 Self::Schemas(values) => StorageEntry::Schema(&values[index]),
172 Self::Objects(values) => StorageEntry::Object(&values[index]),
173 Self::Records(values) => StorageEntry::Record(&values[index]),
174 Self::Stream(values) => StorageEntry::Row(&values[index]),
175 Self::Files(values) => StorageEntry::File(&values[index]),
176 })
177 }
178}
179
180impl<'a> StorageEntry<'a> {
181 pub fn position(self) -> &'a StorageCursor {
182 match self {
183 Self::Schema(value) => &value.position,
184 Self::Object(value) => &value.position,
185 Self::Record(value) => &value.position,
186 Self::Row(value) => &value.record.position,
187 Self::File(value) => &value.position,
188 }
189 }
190
191 pub(super) fn encoded_bytes(self) -> Result<usize> {
192 match self {
193 Self::Schema(value) => value.encoded_bytes(),
194 Self::Object(value) => {
195 value.version.check_position(&value.position)?;
196 encoded_size(value)
197 }
198 Self::Record(value) => encoded_size(value),
199 Self::Row(value) => encoded_size(value),
200 Self::File(value) => encoded_size(value),
201 }
202 }
203}
204
205fn invalid(reason: &'static str) -> Error {
210 Error::InvalidInput { field: "storage batch", reason }
211}
212
213