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