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