sva_engine/cache/
entry_bytes.rs1use sva_formula::filter::Shape;
4use sva_samples::{AutomationFrame, Buffer, FilterTrace, Label};
5
6use super::{Entry, Payload, Tier};
7
8const MAGIC: &[u8; 4] = b"RBC6";
10
11pub trait SampleCodec: Sync {
17 fn id(&self) -> u8;
18
19 fn encode(&self, buffer: &Buffer, out: &mut Vec<u8>);
20
21 fn decode(&self, bytes: &[u8], rate: u32, width: usize, samples: usize) -> Option<Buffer>;
23}
24
25pub struct RawF64;
26
27impl SampleCodec for RawF64 {
28 fn id(&self) -> u8 {
29 0
30 }
31
32 fn encode(&self, buffer: &Buffer, out: &mut Vec<u8>) {
33 out.reserve(buffer.len() * buffer.width * size_of::<f64>());
34 for c in 0..buffer.width {
35 for &s in buffer.plane(c) {
36 out.extend_from_slice(&s.to_le_bytes());
37 }
38 }
39 }
40
41 fn decode(&self, bytes: &[u8], rate: u32, width: usize, samples: usize) -> Option<Buffer> {
42 let plane = samples.checked_mul(size_of::<f64>())?;
43 if bytes.len() != plane.checked_mul(width)? {
44 return None;
45 }
46 let planes = (0..width)
47 .map(|c| {
48 bytes[c * plane..(c + 1) * plane]
49 .chunks_exact(8)
50 .map(|b| f64::from_le_bytes(b.try_into().expect("chunks_exact(8)")))
51 .collect()
52 })
53 .collect();
54 Some(Buffer::of_planes(rate, planes))
55 }
56}
57
58#[derive(Debug, PartialEq, Eq)]
59pub(super) enum Unread {
60 OtherCodec,
61 Damaged,
62}
63
64pub(super) struct Writer(pub Vec<u8>);
65
66impl Writer {
67 pub(super) fn u32(&mut self, v: u32) {
68 self.0.extend_from_slice(&v.to_le_bytes());
69 }
70 fn u64(&mut self, v: u64) {
71 self.0.extend_from_slice(&v.to_le_bytes());
72 }
73 pub(super) fn f64(&mut self, v: f64) {
74 self.0.extend_from_slice(&v.to_le_bytes());
75 }
76}
77
78pub(super) struct Reader<'a>(pub &'a [u8]);
79
80impl<'a> Reader<'a> {
81 pub(super) fn take(&mut self, n: usize) -> Option<&'a [u8]> {
82 let (head, rest) = self.0.split_at_checked(n)?;
83 self.0 = rest;
84 Some(head)
85 }
86 pub(super) fn u32(&mut self) -> Option<u32> {
87 Some(u32::from_le_bytes(self.take(4)?.try_into().ok()?))
88 }
89 fn u64(&mut self) -> Option<u64> {
90 Some(u64::from_le_bytes(self.take(8)?.try_into().ok()?))
91 }
92 pub(super) fn f64(&mut self) -> Option<f64> {
93 Some(f64::from_le_bytes(self.take(8)?.try_into().ok()?))
94 }
95}
96
97pub(super) fn encode(
98 buffer: &Buffer,
99 traces: &[FilterTrace],
100 label: Option<&Label>,
101 codec: &dyn SampleCodec,
102) -> Vec<u8> {
103 let mut w = Writer(Vec::with_capacity(buffer.len() * buffer.width * 8 + 64));
104 w.0.extend_from_slice(MAGIC);
105 w.0.push(codec.id());
106 w.u32(buffer.rate);
107 w.u64(buffer.len() as u64);
108 w.u32(buffer.width as u32);
109 write_traces(&mut w, traces);
110 super::label::write(&mut w, label);
111 codec.encode(buffer, &mut w.0);
112 w.0
113}
114
115fn write_traces(w: &mut Writer, traces: &[FilterTrace]) {
116 w.u32(traces.len() as u32);
117 for trace in traces {
118 w.u32(trace.site as u32);
119 w.u32(trace.channel.map_or(u32::MAX, |c| c as u32));
120 w.0.push(u8::from(trace.clamped));
121 w.u32(trace.shape.len() as u32);
122 w.0.extend_from_slice(trace.shape.as_bytes());
123 w.f64(trace.trace_secs);
124 w.u32(trace.frames.len() as u32);
125 for f in &trace.frames {
126 w.f64(f.t_secs);
127 w.f64(f.cutoff);
128 w.f64(f.q);
129 w.f64(f.gain_db);
130 }
131 }
132}
133
134pub(super) fn decode(
138 bytes: &[u8],
139 node: &str,
140 (rate, width, samples): (u32, usize, usize),
141 codec: &dyn SampleCodec,
142) -> Result<Entry, Unread> {
143 let mut r = Reader(bytes);
144 if r.take(4) != Some(MAGIC) {
145 return Err(Unread::Damaged);
146 }
147 match r.take(1) {
148 Some([id]) if *id == codec.id() => {}
149 Some(_) => return Err(Unread::OtherCodec),
150 None => return Err(Unread::Damaged),
151 }
152 decode_samples(&mut r, node, rate, width, samples, codec).ok_or(Unread::Damaged)
153}
154
155fn decode_samples(
156 r: &mut Reader,
157 node: &str,
158 rate: u32,
159 width: usize,
160 samples: usize,
161 codec: &dyn SampleCodec,
162) -> Option<Entry> {
163 if r.u32()? != rate || r.u64()? != samples as u64 || r.u32()? != width as u32 {
164 return None;
165 }
166 let traces = read_traces(r, node)?;
167 let label = super::label::read(r)?;
168 let buffer = codec.decode(r.0, rate, width, samples)?;
169 Some(Entry {
170 payload: Payload::Samples(Box::new(buffer)),
171 traces,
172 label,
173 tier: Tier::Persistent,
174 })
175}
176
177fn read_traces(r: &mut Reader, node: &str) -> Option<Vec<FilterTrace>> {
178 let mut traces = Vec::new();
179 for _ in 0..r.u32()? {
180 let site = r.u32()? as usize;
181 let channel = match r.u32()? {
182 u32::MAX => None,
183 c => Some(c as usize),
184 };
185 let clamped = r.take(1)?[0] == 1;
186 let name_len = r.u32()? as usize;
187 let shape = Shape::from_name(std::str::from_utf8(r.take(name_len)?).ok()?)?.name();
188 let trace_secs = r.f64()?;
189 let frame_count = r.u32()? as usize;
190 let mut frames = Vec::with_capacity(frame_count.min(1 << 20));
191 for _ in 0..frame_count {
192 frames.push(AutomationFrame {
193 t_secs: r.f64()?,
194 cutoff: r.f64()?,
195 q: r.f64()?,
196 gain_db: r.f64()?,
197 });
198 }
199 traces.push(FilterTrace {
200 node: node.to_string(),
201 site,
202 channel,
203 shape,
204 clamped,
205 trace_secs,
206 frames,
207 });
208 }
209 Some(traces)
210}