1use sva_formula::Codomain;
4use sva_samples::{
5 Buffer, Cost, Detail, Dropped, Extent, Grid, Label, PSYCHOACOUSTIC_V1, Rule, Source,
6};
7
8use sva_formula::Hash;
9
10use super::Stored;
11use super::stored::{Laid, Samples};
12
13pub const STORE_FORMAT: u32 = 7;
15
16fn entry_tag() -> Vec<u8> {
19 [&b"SVAh"[..], &STORE_FORMAT.to_le_bytes()].concat()
20}
21
22const STAGED_RUN: &[u8; 4] = b"SVAc";
23
24pub(crate) const CHUNK: usize = 4096;
28
29pub(crate) fn entry(stored: &Stored, runs: &[Buffer]) -> Vec<u8> {
32 let mut laid = Vec::new();
33 let mut body = Vec::new();
34 for run in runs {
35 let mut sums = Vec::new();
36 let at = body.len() as u64;
37 for from in (0..run.len()).step_by(CHUNK) {
38 let to = (from + CHUNK).min(run.len());
39 let chunk = body.len();
40 for plane in &run.planes {
41 for sample in &plane[from..to] {
42 word(&mut body, sample.to_bits());
43 }
44 }
45 sums.push(checksum(&body[chunk..]));
46 }
47 laid.push(Laid {
48 rate: run.rate,
49 start: run.start,
50 width: run.width,
51 len: run.len(),
52 at,
53 sums,
54 });
55 }
56 let head = sealed(header(stored, &laid));
57 let mut out = Vec::with_capacity(8 + head.len() + body.len());
58 word(&mut out, head.len() as u64);
59 out.extend_from_slice(&head);
60 out.extend_from_slice(&body);
61 out
62}
63
64fn header(stored: &Stored, laid: &[Laid]) -> Vec<u8> {
65 let mut out = entry_tag();
66 word(&mut out, stored.key.0);
67 word(&mut out, stored.key.1);
68 labelled(&mut out, &stored.label);
69 out.push(stored.width);
70 out.push(match stored.codomain {
71 Codomain::Real => 0,
72 Codomain::Complex => 1,
73 });
74 maybe(&mut out, stored.rate.map(u64::from));
75 word(&mut out, u64::from(stored.grid.rate));
76 out.extend_from_slice(&stored.grid.a.to_le_bytes());
77 out.extend_from_slice(&stored.grid.d.to_le_bytes());
78 word(&mut out, stored.support.start as u64);
79 word(&mut out, stored.support.end as u64);
80 out.extend_from_slice(&stored.priced.to_le_bytes());
81 word(&mut out, stored.moved.to_bits());
82 out.push(u8::from(stored.readable));
83 match stored.samples {
84 Samples::Of { key, by } => {
85 out.push(1);
86 word(&mut out, key.0);
87 word(&mut out, key.1);
88 word(&mut out, by as u64);
89 }
90 _ => out.push(0),
91 }
92 word(&mut out, laid.len() as u64);
93 for run in laid {
94 word(&mut out, u64::from(run.rate));
95 word(&mut out, run.start as u64);
96 word(&mut out, run.width as u64);
97 word(&mut out, run.len as u64);
98 for sum in &run.sums {
99 word(&mut out, *sum);
100 }
101 }
102 out
103}
104
105pub(crate) fn head_len(first: &[u8]) -> Option<usize> {
107 let len = u64::from_le_bytes(first.get(..8)?.try_into().ok()?);
108 8usize.checked_add(usize::try_from(len).ok()?)
109}
110
111pub(crate) fn read_head(bytes: &[u8], file: Hash) -> Option<(Stored, u64)> {
114 let span = head_len(bytes)?;
115 let mut r = Reader(opened(bytes.get(8..span)?, &entry_tag())?);
116 let key = Hash(r.word()?, r.word()?);
117 let label = r.label()?;
118 let width = r.byte()?;
119 let codomain = match r.byte()? {
120 0 => Codomain::Real,
121 1 => Codomain::Complex,
122 _ => return None,
123 };
124 let rate = match r.maybe()? {
125 Some(rate) => Some(u32::try_from(rate).ok()?),
126 None => None,
127 };
128 let grid = Grid {
129 rate: u32::try_from(r.word()?).ok()?,
130 a: r.wide()? as i128,
131 d: r.wide()? as i128,
132 };
133 let (start, end) = (r.word()? as i64, r.word()? as i64);
134 let support = (start <= end).then(|| Extent::new(start, end))?;
135 let priced = r.wide()?;
136 let moved = f64::from_bits(r.word()?);
137 let readable = match r.byte()? {
138 0 => false,
139 1 => true,
140 _ => return None,
141 };
142 let of = match r.byte()? {
143 0 => None,
144 1 => Some(Samples::Of {
145 key: Hash(r.word()?, r.word()?),
146 by: r.word()? as i64,
147 }),
148 _ => return None,
149 };
150 let count = r.word()? as usize;
151 let mut runs = Vec::new();
152 let mut at = span as u64;
153 for _ in 0..count.min(r.0.len()) {
154 let rate = u32::try_from(r.word()?).ok()?;
155 let start = r.word()? as i64;
156 let (width, len) = (r.word()? as usize, r.word()? as usize);
157 let sums = (0..len.div_ceil(CHUNK).min(r.0.len()))
158 .map(|_| r.word())
159 .collect::<Option<Vec<u64>>>()?;
160 if sums.len() != len.div_ceil(CHUNK) {
161 return None;
162 }
163 runs.push(Laid {
164 rate,
165 start,
166 width,
167 len,
168 at,
169 sums,
170 });
171 at = at.checked_add((width.checked_mul(len)?.checked_mul(8)?) as u64)?;
172 }
173 let samples = match (of, runs.is_empty()) {
174 (Some(of), true) => of,
175 (Some(_), false) => return None,
176 (None, true) => Samples::None,
177 (None, false) => Samples::Entry {
178 file,
179 runs,
180 shift: 0,
181 },
182 };
183 let stored = Stored {
184 key,
185 label,
186 width,
187 codomain,
188 rate,
189 grid,
190 support,
191 priced,
192 moved,
193 readable,
194 samples,
195 };
196 (runs_whole(count, &stored.samples) && r.0.is_empty()).then_some((stored, at))
197}
198
199fn runs_whole(count: usize, samples: &Samples) -> bool {
200 match samples {
201 Samples::Entry { runs, .. } => runs.len() == count,
202 _ => count == 0,
203 }
204}
205
206pub(crate) fn span_of(run: &Laid, from: usize, to: usize) -> (u64, u64) {
208 let bytes = |chunks: usize| ((chunks * CHUNK).min(run.len) * run.width * 8) as u64;
209 (run.at + bytes(from), bytes(to) - bytes(from))
210}
211
212pub(crate) fn read_chunks(bytes: &[u8], run: &Laid, from: usize, to: usize) -> Option<Buffer> {
214 let first = from * CHUNK;
215 let len = (to * CHUNK).min(run.len).checked_sub(first)?;
216 if bytes.len() != len * run.width * 8 {
217 return None;
218 }
219 let mut planes = vec![Vec::with_capacity(len); run.width];
220 let mut at = 0;
221 for (k, sum) in run.sums[from..to].iter().enumerate() {
222 let n = (CHUNK).min(run.len - first - k * CHUNK);
223 let chunk = &bytes[at..at + n * run.width * 8];
224 if checksum(chunk) != *sum {
225 return None;
226 }
227 for (c, plane) in planes.iter_mut().enumerate() {
228 let own = &chunk[c * n * 8..(c + 1) * n * 8];
229 plane.extend(
230 own.chunks_exact(8).map(|b| {
231 f64::from_bits(u64::from_le_bytes(b.try_into().expect("eight bytes")))
232 }),
233 );
234 }
235 at += chunk.len();
236 }
237 let mut out = Buffer::of_planes(run.rate, planes);
238 out.start = run.start + first as i64;
239 Some(out)
240}
241
242pub(crate) fn chunk(samples: &Buffer) -> Vec<u8> {
243 let mut out = STAGED_RUN.to_vec();
244 segments(&mut out, std::slice::from_ref(samples));
245 sealed(out)
246}
247
248pub(crate) fn read_chunk(bytes: &[u8]) -> Option<Buffer> {
249 let mut r = Reader(opened(bytes, STAGED_RUN)?);
250 let mut parts = r.segments()?;
251 (r.0.is_empty() && parts.len() == 1).then(|| parts.remove(0))
252}
253
254fn sealed(mut out: Vec<u8>) -> Vec<u8> {
255 let sum = checksum(&out);
256 word(&mut out, sum);
257 out
258}
259
260fn opened<'b>(bytes: &'b [u8], magic: &[u8]) -> Option<&'b [u8]> {
262 let body = bytes.len().checked_sub(8)?;
263 let (body, sum) = bytes.split_at(body);
264 (checksum(body).to_le_bytes() == sum && body.starts_with(magic)).then(|| &body[magic.len()..])
265}
266
267fn segments(out: &mut Vec<u8>, parts: &[Buffer]) {
268 word(out, parts.len() as u64);
269 for part in parts {
270 word(out, u64::from(part.rate));
271 word(out, part.start as u64);
272 word(out, part.width as u64);
273 word(out, part.len() as u64);
274 for plane in &part.planes {
275 for sample in plane {
276 word(out, sample.to_bits());
277 }
278 }
279 }
280}
281
282fn checksum(bytes: &[u8]) -> u64 {
283 bytes.iter().fold(0xcbf2_9ce4_8422_2325, |h, b| {
284 (h ^ u64::from(*b)).wrapping_mul(0x0100_0000_01b3)
285 })
286}
287
288fn word(out: &mut Vec<u8>, w: u64) {
289 out.extend_from_slice(&w.to_le_bytes());
290}
291
292fn float(out: &mut Vec<u8>, v: Option<f64>) {
293 maybe(out, v.map(f64::to_bits));
294}
295
296fn maybe(out: &mut Vec<u8>, w: Option<u64>) {
297 match w {
298 None => out.push(0),
299 Some(w) => {
300 out.push(1);
301 word(out, w);
302 }
303 }
304}
305
306fn text(out: &mut Vec<u8>, s: &str) {
307 word(out, s.len() as u64);
308 out.extend_from_slice(s.as_bytes());
309}
310
311fn labelled(out: &mut Vec<u8>, label: &Label) {
312 out.push(match label.source {
313 Source::Exact => 0,
314 Source::Measured => 1,
315 });
316 text(out, label.profile);
317 word(out, u64::from(label.rate));
318 detailed(out, &label.detail);
319 match label.cost {
320 None => out.push(0),
321 Some(Cost { flops, budget }) => {
322 out.push(1);
323 out.extend_from_slice(&flops.to_le_bytes());
324 out.extend_from_slice(&budget.to_le_bytes());
325 }
326 }
327 float(out, label.moved);
328 match &label.pruned {
329 None => out.push(0),
330 Some(pruned) => {
331 out.push(1);
332 word(out, pruned.db.to_bits());
333 word(out, pruned.cuts.len() as u64);
334 for (node, at) in &pruned.cuts {
335 text(out, node);
336 word(out, *at as u64);
337 }
338 }
339 }
340}
341
342fn detailed(out: &mut Vec<u8>, detail: &Detail) {
343 let rule = |out: &mut Vec<u8>, rule: &Rule| text(out, rule.as_str());
344 match detail {
345 Detail::Lines {
346 rule: r,
347 placed,
348 summed,
349 dropped,
350 dropped_more,
351 terms,
352 tail_db,
353 } => {
354 out.push(0);
355 rule(out, r);
356 word(out, *placed as u64);
357 word(out, *summed as u64);
358 word(out, dropped.len() as u64);
359 for d in dropped {
360 word(out, d.hz.to_bits());
361 word(out, d.db.to_bits());
362 }
363 word(out, *dropped_more as u64);
364 maybe(out, terms.map(|t| t as u64));
365 float(out, *tail_db);
366 }
367 Detail::Continuous { rule: r } => {
368 out.push(1);
369 rule(out, r);
370 }
371 Detail::Cropped { rule: r, tail_db } => {
372 out.push(2);
373 rule(out, r);
374 float(out, *tail_db);
375 }
376 Detail::Point { rule: r, alias_db } => {
377 out.push(3);
378 rule(out, r);
379 float(out, *alias_db);
380 }
381 Detail::Spectrum { rule: r, wrap_db } => {
382 out.push(4);
383 rule(out, r);
384 word(out, wrap_db.to_bits());
385 }
386 Detail::Roundtrip { rule: r, edited } => {
387 out.push(5);
388 rule(out, r);
389 out.push(u8::from(*edited));
390 }
391 Detail::Reading { rule: r } => {
392 out.push(6);
393 rule(out, r);
394 }
395 Detail::Added { parts } => {
396 out.push(7);
397 word(out, parts.len() as u64);
398 for part in parts {
399 detailed(out, part);
400 }
401 }
402 }
403}
404
405struct Reader<'b>(&'b [u8]);
406
407impl Reader<'_> {
408 fn take(&mut self, n: usize) -> Option<&[u8]> {
409 if self.0.len() < n {
410 return None;
411 }
412 let (head, rest) = self.0.split_at(n);
413 self.0 = rest;
414 Some(head)
415 }
416
417 fn byte(&mut self) -> Option<u8> {
418 self.take(1).map(|b| b[0])
419 }
420
421 fn word(&mut self) -> Option<u64> {
422 Some(u64::from_le_bytes(self.take(8)?.try_into().ok()?))
423 }
424
425 fn segments(&mut self) -> Option<Vec<Buffer>> {
426 let count = self.word()? as usize;
427 let mut parts = Vec::new();
428 for _ in 0..count.min(self.0.len()) {
429 let rate = u32::try_from(self.word()?).ok()?;
430 let start = self.word()? as i64;
431 let (width, len) = (self.word()? as usize, self.word()? as usize);
432 if width.saturating_mul(len).saturating_mul(8) > self.0.len() {
433 return None;
434 }
435 let planes = (0..width)
436 .map(|_| (0..len).map(|_| self.word().map(f64::from_bits)).collect())
437 .collect::<Option<Vec<Vec<f64>>>>()?;
438 let mut part = Buffer::of_planes(rate, planes);
439 part.start = start;
440 parts.push(part);
441 }
442 (parts.len() == count).then_some(parts)
443 }
444
445 fn wide(&mut self) -> Option<u128> {
446 Some(u128::from_le_bytes(self.take(16)?.try_into().ok()?))
447 }
448
449 fn maybe(&mut self) -> Option<Option<u64>> {
450 match self.byte()? {
451 0 => Some(None),
452 1 => Some(Some(self.word()?)),
453 _ => None,
454 }
455 }
456
457 fn float(&mut self) -> Option<Option<f64>> {
458 Some(self.maybe()?.map(f64::from_bits))
459 }
460
461 fn text(&mut self) -> Option<&str> {
462 let len = self.word()? as usize;
463 std::str::from_utf8(self.take(len)?).ok()
464 }
465
466 fn rule(&mut self) -> Option<Rule> {
467 Rule::named(self.text()?)
468 }
469
470 fn label(&mut self) -> Option<Label> {
471 let source = match self.byte()? {
472 0 => Source::Exact,
473 1 => Source::Measured,
474 _ => return None,
475 };
476 let profile = match self.text()? {
477 name if name == PSYCHOACOUSTIC_V1.name => PSYCHOACOUSTIC_V1.name,
478 _ => return None,
479 };
480 let rate = u32::try_from(self.word()?).ok()?;
481 let detail = self.detail()?;
482 let cost = match self.byte()? {
483 0 => None,
484 1 => Some(Cost {
485 flops: self.wide()?,
486 budget: self.wide()?,
487 }),
488 _ => return None,
489 };
490 let moved = self.float()?;
491 let pruned = match self.byte()? {
492 0 => None,
493 1 => {
494 let db = f64::from_bits(self.word()?);
495 let count = usize::try_from(self.word()?).ok()?;
496 let cuts = (0..count)
497 .map(|_| Some((self.text()?.to_string(), self.word()? as i64)))
498 .collect::<Option<Vec<_>>>()?;
499 Some(sva_samples::Pruned { db, cuts })
500 }
501 _ => return None,
502 };
503 Some(Label {
504 source,
505 profile,
506 rate,
507 detail,
508 cost,
509 moved,
510 pruned,
511 })
512 }
513
514 fn detail(&mut self) -> Option<Detail> {
515 Some(match self.byte()? {
516 0 => {
517 let rule = self.rule()?;
518 let (placed, summed) = (self.word()? as usize, self.word()? as usize);
519 let count = self.word()? as usize;
520 let dropped = (0..count.min(self.0.len()))
521 .map(|_| {
522 Some(Dropped {
523 hz: f64::from_bits(self.word()?),
524 db: f64::from_bits(self.word()?),
525 })
526 })
527 .collect::<Option<Vec<_>>>()?;
528 if dropped.len() != count {
529 return None;
530 }
531 Detail::Lines {
532 rule,
533 placed,
534 summed,
535 dropped,
536 dropped_more: self.word()? as usize,
537 terms: self.maybe()?.map(|t| t as usize),
538 tail_db: self.float()?,
539 }
540 }
541 1 => Detail::Continuous { rule: self.rule()? },
542 2 => Detail::Cropped {
543 rule: self.rule()?,
544 tail_db: self.float()?,
545 },
546 3 => Detail::Point {
547 rule: self.rule()?,
548 alias_db: self.float()?,
549 },
550 4 => Detail::Spectrum {
551 rule: self.rule()?,
552 wrap_db: f64::from_bits(self.word()?),
553 },
554 5 => Detail::Roundtrip {
555 rule: self.rule()?,
556 edited: match self.byte()? {
557 0 => false,
558 1 => true,
559 _ => return None,
560 },
561 },
562 6 => Detail::Reading { rule: self.rule()? },
563 7 => {
564 let count = self.word()? as usize;
565 let parts = (0..count.min(self.0.len()))
566 .map(|_| self.detail())
567 .collect::<Option<Vec<_>>>()?;
568 if parts.len() != count {
569 return None;
570 }
571 Detail::Added { parts }
572 }
573 _ => return None,
574 })
575 }
576}