1use crate::chunks::{FrameDecoder, PlaintextChunks, CHUNK_SIZE};
4use crate::dismissal::WriteFrame;
5use crate::error::{require, Error, Rule};
6use crate::wire::{decode_frame_value, wire_struct};
7use crate::write::{WriteHeader, WriteRecord};
8use crate::{encode_frame, FRAME_PREFIX_LEN};
9use coven_merge::{Audience, RowId, WriteId};
10
11#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct PartHeader {
14 pub audience: Audience,
16 pub record_count: u64,
19 pub plaintext_length: u64,
21}
22wire_struct!(PartHeader, audience, record_count, plaintext_length);
23impl PartHeader {
24 pub(crate) fn validate(&self) -> Result<(), Error> {
25 require(self.record_count > 0, "write part rows", Rule::Required)?;
26 require(
27 self.record_count <= self.plaintext_length / FRAME_PREFIX_LEN as u64,
28 "write part length",
29 Rule::StreamLength,
30 )
31 }
32
33 pub fn chunk_count(&self) -> u64 {
35 crate::chunks::chunk_count(self.plaintext_length)
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct WriteHeaderFrame {
42 pub header: WriteHeader,
44 pub parts: Vec<PartHeader>,
46}
47wire_struct!(WriteHeaderFrame, header, parts);
48impl WriteHeaderFrame {
49 pub fn encode(&self) -> Result<Vec<u8>, Error> {
51 self.validate()?;
52 encode_frame(crate::kinds::WRITE_HEADER, self)
53 }
54
55 pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
57 let header: Self =
58 decode_frame_value(bytes, crate::kinds::WRITE_HEADER, "write header kind")?;
59 header.validate()?;
60 Ok(header)
61 }
62
63 fn validate(&self) -> Result<(), Error> {
64 self.header.validate_parts(self.parts.iter().map(|part| &part.audience))?;
65 for part in &self.parts {
66 part.validate()?;
67 }
68 Ok(())
69 }
70}
71
72pub struct WriteEncoder<'a> {
76 record: &'a WriteRecord,
77 encoded: Option<&'a [Vec<Vec<u8>>]>,
79 header: WriteHeaderFrame,
80 frame: Vec<u8>,
81 length: u64,
82}
83impl<'a> WriteEncoder<'a> {
84 pub fn new(record: &'a WriteRecord) -> Result<Self, Error> {
86 record.validate()?;
87 Self::measured(record, None)
88 }
89
90 pub fn with_records(
95 record: &'a WriteRecord,
96 encoded: &'a [Vec<Vec<u8>>],
97 ) -> Result<Self, Error> {
98 require(
99 encoded.len() == record.parts.len()
100 && record
101 .parts
102 .iter()
103 .zip(encoded)
104 .all(|(part, frames)| frames.len() == part.records().count()),
105 "write encoded records",
106 Rule::Required,
107 )?;
108 record.validate_encoded()?;
109 Self::measured(record, Some(encoded))
110 }
111
112 fn measured(
113 record: &'a WriteRecord,
114 encoded: Option<&'a [Vec<Vec<u8>>]>,
115 ) -> Result<Self, Error> {
116 let mut parts = Vec::new();
117 for (index, part) in record.parts.iter().enumerate() {
118 let mut length = 0u64;
119 for frame in part_frames(part, encoded.map(|encoded| &encoded[index][..])) {
120 length = add_length(length, frame?.len() as u64)?;
121 }
122 parts.push(PartHeader {
123 audience: part.audience.clone(),
124 record_count: (part.rows.len() + part.executions.len() + part.dismissals.len())
125 as u64,
126 plaintext_length: length,
127 });
128 }
129 let header = WriteHeaderFrame { header: record.header.clone(), parts };
130 let frame = header.encode()?;
131 let mut length = frame.len() as u64;
132 for part in &header.parts {
133 length = add_length(length, part.plaintext_length)?;
134 }
135 Ok(Self { record, encoded, header, frame, length })
136 }
137
138 pub fn header(&self) -> &WriteHeaderFrame {
140 &self.header
141 }
142
143 pub fn header_frame(&self) -> &[u8] {
145 &self.frame
146 }
147
148 pub fn plaintext_length(&self) -> u64 {
150 self.length
151 }
152
153 pub fn encode_plaintext(&self, output: &mut [u8]) -> Result<(), Error> {
157 require(
158 output.len() as u64 == self.length,
159 "write plaintext buffer length",
160 Rule::StreamLength,
161 )?;
162 let (header, mut remaining) = output.split_at_mut(self.frame.len());
163 header.copy_from_slice(&self.frame);
164 for index in 0..self.header.parts.len() {
165 for chunk in self.part_chunks(index)? {
166 let chunk = chunk?;
167 let (piece, rest) = remaining.split_at_mut(chunk.len());
168 piece.copy_from_slice(&chunk);
169 remaining = rest;
170 }
171 }
172 assert!(remaining.is_empty(), "encoded the measured write length");
173 Ok(())
174 }
175
176 pub fn part_chunks(
178 &self,
179 index: usize,
180 ) -> Result<PlaintextChunks<impl Iterator<Item = Result<Vec<u8>, Error>> + '_>, Error> {
181 let part = self
182 .record
183 .parts
184 .get(index)
185 .ok_or(Error::Invalid { field: "write part index", rule: Rule::Chunk })?;
186 Ok(PlaintextChunks::new(part_frames(part, self.encoded.map(|encoded| &encoded[index][..]))))
187 }
188}
189
190fn part_frames<'a>(
192 part: &'a crate::write::WritePart,
193 encoded: Option<&'a [Vec<u8>]>,
194) -> Box<dyn Iterator<Item = Result<Vec<u8>, Error>> + 'a> {
195 match encoded {
196 Some(encoded) => Box::new(part.frames_from(encoded)),
197 None => Box::new(part.frames()),
198 }
199}
200
201fn add_length(a: u64, b: u64) -> Result<u64, Error> {
202 a.checked_add(b)
203 .ok_or(Error::Invalid { field: "write plaintext length", rule: Rule::StreamLength })
204}
205
206pub struct PartDecoder {
212 header: PartHeader,
213 frames: FrameDecoder,
214 received: u64,
215 rows: u64,
216 previous: Option<(RowId, Option<(crate::dismissal::DismissalTarget, WriteId)>)>,
217}
218impl PartDecoder {
219 pub fn new(header: PartHeader) -> Result<Self, Error> {
221 header.validate()?;
222 Ok(Self {
223 frames: FrameDecoder::new(Some(header.plaintext_length)),
224 header,
225 received: 0,
226 rows: 0,
227 previous: None,
228 })
229 }
230
231 pub fn chunk(&mut self, mut bytes: &[u8]) -> Result<Vec<WriteFrame>, Error> {
234 let left = self.header.plaintext_length - self.received;
235 require(
236 left > 0 && bytes.len() as u64 == left.min(CHUNK_SIZE as u64),
237 "write part chunk length",
238 Rule::StreamLength,
239 )?;
240 self.received += bytes.len() as u64;
241 let mut rows = Vec::new();
242 while let Some(frame) = self.frames.next(&mut bytes)? {
243 require(
244 self.rows < self.header.record_count,
245 "write part row count",
246 Rule::StreamLength,
247 )?;
248 let row = WriteFrame::decode(&frame)?;
249 let key = row.key();
250 require(key.0.audience == self.header.audience, "write part audience", Rule::Audience)?;
251 require(
252 self.previous.as_ref().is_none_or(|previous| *previous < key),
253 "write part rows",
254 Rule::Order,
255 )?;
256 self.previous = Some(key);
257 self.rows += 1;
258 rows.push(row);
259 if bytes.is_empty() {
260 break;
261 }
262 }
263 Ok(rows)
264 }
265
266 pub fn finish(&self) -> Result<(), Error> {
268 self.frames.finish()?;
269 require(
270 self.received == self.header.plaintext_length && self.rows == self.header.record_count,
271 "write part row count/length",
272 Rule::StreamLength,
273 )
274 }
275}
276
277#[cfg(any(test, feature = "test-utils"))]
280#[expect(
281 clippy::map_err_ignore,
282 reason = "a part longer than usize cannot be in these bytes: they are truncated"
283)]
284pub fn decode_plaintext(bytes: &[u8]) -> Result<WriteRecord, Error> {
285 let length = crate::frame_length(bytes)?;
286 let header = WriteHeaderFrame::decode(bytes.get(..length).ok_or(Error::Truncated)?)?;
287 let mut bytes = &bytes[length..];
288 let mut parts = Vec::new();
289 for part in header.parts {
290 let length = usize::try_from(part.plaintext_length).map_err(|_| Error::Truncated)?;
291 let stream = bytes.get(..length).ok_or(Error::Truncated)?;
292 bytes = &bytes[length..];
293 let audience = part.audience.clone();
294 let mut decoder = PartDecoder::new(part)?;
295 let mut rows = Vec::new();
296 let mut executions = Vec::new();
297 let mut dismissals = Vec::new();
298 for chunk in stream.chunks(CHUNK_SIZE) {
299 for frame in decoder.chunk(chunk)? {
300 match frame {
301 WriteFrame::Change(row) => rows.push(row),
302 WriteFrame::Execution(execution) => executions.push(execution),
303 WriteFrame::Dismissal(dismissal) => dismissals.push(dismissal),
304 }
305 }
306 }
307 decoder.finish()?;
308 parts.push(crate::write::WritePart { audience, rows, executions, dismissals });
309 }
310 if !bytes.is_empty() {
311 return Err(Error::TrailingBytes);
312 }
313 let record = WriteRecord { header: header.header, parts };
314 record.validate()?;
315 Ok(record)
316}
317
318#[cfg(test)]
319#[path = "write_stream_tests.rs"]
320mod tests;