1#![forbid(unsafe_code)]
2
3use std::collections::VecDeque;
6use std::fmt;
7use std::io::{self, Read, Write};
8use std::time::Duration;
9
10pub mod editor;
11pub mod layout;
12pub mod processing;
13pub use editor::{
14 EditorAction, EditorImage, EditorPlatform, EditorReply, EditorStatus, ParentWindow,
15};
16pub use processing::{BusConfiguration, Capabilities, ProcessingSetup, SampleFormat};
17pub mod state;
18#[derive(Clone, Debug, thiserror::Error)]
19#[error("{0}")]
20pub struct PluginError(pub String);
21
22pub const MAGIC: [u8; 4] = *b"SSRP";
23pub const PROTOCOL_MAJOR: u16 = 4;
24pub const PROTOCOL_MINOR: u16 = 0;
25pub const MAX_MESSAGE_BYTES: usize = 64 * 1024;
26pub const MAX_STATE_BYTES: usize = 64 * 1024 * 1024;
27pub const MAX_STRING_BYTES: usize = 4096;
30const FRAME_META_BYTES: usize = 8;
31
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33#[repr(u16)]
34pub enum PluginFormat {
35 Vst2 = 2,
36 Vst3 = 3,
37}
38
39#[derive(Clone, Debug, Eq, PartialEq)]
40pub struct Bus {
41 pub channels: u16,
42 pub arrangement: u64,
43}
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum BusKind {
48 Main,
49 Aux,
51}
52
53#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct BusInfo {
56 pub layout: Bus,
57 pub kind: BusKind,
58 pub name: String,
60 pub default_active: bool,
62}
63
64impl BusInfo {
65 pub fn configuration(&self, active: bool) -> BusConfiguration {
66 BusConfiguration {
67 layout: self.layout.clone(),
68 active,
69 }
70 }
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
74pub struct ClassInfo {
75 pub id: [u8; 16],
76 pub name: String,
77 pub vendor: String,
78 pub category: String,
79}
80
81#[derive(Clone, Debug, PartialEq)]
82pub struct Parameter {
83 pub id: u32,
84 pub name: String,
85 pub unit: String,
86 pub value: f64,
87 pub default: Option<f64>,
88 pub steps: Option<u32>,
89 pub automatable: bool,
90 pub read_only: bool,
91 pub hidden: bool,
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum EditPhase {
96 Begin,
97 Perform,
98 End,
99}
100
101#[derive(Clone, Debug, PartialEq)]
102pub enum Message {
103 Sandboxed {
105 mechanism: String,
106 },
107 Hello {
108 magic: [u8; 4],
109 major: u16,
110 minor: u16,
111 },
112 Load {
113 path: String,
114 format: PluginFormat,
115 class_id: Option<[u8; 16]>,
116 },
117 Configure {
118 rate: u32,
119 max_frames: u32,
120 in_channels: u16,
121 out_channels: u16,
122 },
123 Editor(EditorAction),
124 EditorStatus(EditorStatus),
125 EditorImage(EditorImage),
126 Prepare(ProcessingSetup),
127 Describe,
128 Features(Capabilities),
129 Activate,
130 SetState {
131 blob: Vec<u8>,
132 },
133 GetState,
134 OpenEditor,
135 OpenEditorIn {
136 parent: ParentWindow,
137 },
138 CloseEditor,
139 ListParameters {
140 start: u32,
141 count: u16,
142 },
143 Parameters {
144 total: u32,
145 entries: Vec<Parameter>,
146 },
147 SetParameter {
148 id: u32,
149 value: f64,
150 },
151 RequestError {
152 code: u32,
153 text: String,
154 },
155 Shutdown,
156 Scan {
157 path: String,
158 },
159 Loaded {
160 name: String,
161 vendor: String,
162 latency_frames: u32,
163 inputs: Vec<BusInfo>,
164 outputs: Vec<BusInfo>,
165 },
166 Classes {
167 entries: Vec<ClassInfo>,
168 },
169 LatencyChanged {
170 frames: u32,
171 },
172 State {
173 blob: Vec<u8>,
174 },
175 ParamsChanged,
176 Fault {
177 code: u32,
178 text: String,
179 },
180 Ack,
183 EditorClosed,
185 EditorResized {
186 width: u32,
187 height: u32,
188 },
189 ParameterEdit {
190 id: u32,
191 phase: EditPhase,
192 value: f64,
193 },
194}
195
196impl Message {
197 pub fn hello() -> Self {
198 Self::Hello {
199 magic: MAGIC,
200 major: PROTOCOL_MAJOR,
201 minor: PROTOCOL_MINOR,
202 }
203 }
204
205 pub fn kind(&self) -> u16 {
207 match self {
208 Self::Sandboxed { .. } => 29,
209 Self::Hello { .. } => 1,
210 Self::Load { .. } => 2,
211 Self::Configure { .. } => 3,
212 Self::Editor(_) => 26,
213 Self::EditorStatus(_) => 27,
214 Self::EditorImage(_) => 28,
215 Self::Prepare(_) => 23,
216 Self::Describe => 24,
217 Self::Features(_) => 25,
218 Self::Activate => 4,
219 Self::SetState { .. } => 5,
220 Self::GetState => 6,
221 Self::OpenEditor => 7,
222 Self::OpenEditorIn { .. } => 30,
223 Self::CloseEditor => 18,
224 Self::ListParameters { .. } => 19,
225 Self::Parameters { .. } => 20,
226 Self::SetParameter { .. } => 21,
227 Self::RequestError { .. } => 22,
228 Self::Shutdown => 8,
229 Self::Scan { .. } => 9,
230 Self::Loaded { .. } => 10,
231 Self::Classes { .. } => 11,
232 Self::LatencyChanged { .. } => 12,
233 Self::State { .. } => 13,
234 Self::ParamsChanged => 14,
235 Self::Fault { .. } => 15,
236 Self::Ack => 16,
237 Self::EditorClosed => 17,
238 Self::EditorResized { .. } => 31,
239 Self::ParameterEdit { .. } => 32,
240 }
241 }
242}
243
244#[derive(Clone, Debug, PartialEq)]
245pub struct Frame {
246 pub flags: u16,
247 pub request: u32,
248 pub message: Message,
249}
250
251#[derive(Clone, Copy, Debug, Eq, PartialEq)]
252pub struct UnknownFrame {
253 pub kind: u16,
254 pub flags: u16,
255 pub request: u32,
256 pub body_bytes: usize,
257}
258
259#[derive(Clone, Debug, PartialEq)]
260pub enum DecodedFrame {
261 Known(Frame),
262 Unknown(UnknownFrame),
263}
264
265#[derive(Debug)]
266pub enum CodecError {
267 Io(io::Error),
268 Truncated,
269 InvalidLength,
270 TooLarge { length: usize, limit: usize },
271 StringTooLong,
272 InvalidUtf8,
273 InvalidValue,
274 WrongMagic,
275 WrongMajor { got: u16, expected: u16 },
276 TrailingBytes,
277}
278
279impl fmt::Display for CodecError {
280 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
281 write!(formatter, "{self:?}")
282 }
283}
284
285impl std::error::Error for CodecError {}
286
287impl From<io::Error> for CodecError {
288 fn from(error: io::Error) -> Self {
289 Self::Io(error)
290 }
291}
292
293pub fn write_frame(writer: &mut impl Write, frame: &Frame) -> Result<(), CodecError> {
294 let bytes = encode_frame(frame)?;
295 writer.write_all(&bytes)?;
296 Ok(())
297}
298
299pub fn encode_frame(frame: &Frame) -> Result<Vec<u8>, CodecError> {
300 let mut body = Vec::new();
301 encode_message(&frame.message, &mut body)?;
302 let length = FRAME_META_BYTES
303 .checked_add(body.len())
304 .ok_or(CodecError::InvalidLength)?;
305 let limit = length_limit(frame.message.kind());
306 if length > limit {
307 return Err(CodecError::TooLarge { length, limit });
308 }
309 let mut output = Vec::with_capacity(4 + length);
310 output.extend_from_slice(&(length as u32).to_le_bytes());
311 output.extend_from_slice(&frame.message.kind().to_le_bytes());
312 output.extend_from_slice(&frame.flags.to_le_bytes());
313 output.extend_from_slice(&frame.request.to_le_bytes());
314 output.extend_from_slice(&body);
315 Ok(output)
316}
317
318pub fn decode_frame(bytes: &[u8]) -> Result<DecodedFrame, CodecError> {
319 if bytes.len() < 12 {
320 return Err(CodecError::Truncated);
321 }
322 let length = u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes")) as usize;
323 if length < FRAME_META_BYTES {
324 return Err(CodecError::InvalidLength);
325 }
326 if bytes.len() < 4 + length {
327 return Err(CodecError::Truncated);
328 }
329 if bytes.len() != 4 + length {
330 return Err(CodecError::TrailingBytes);
331 }
332 decode_parts(&bytes[4..12], &bytes[12..])
333}
334
335pub fn read_frame(reader: &mut impl Read) -> Result<Option<DecodedFrame>, CodecError> {
339 let mut length_bytes = [0_u8; 4];
340 let mut first = [0_u8; 1];
341 match reader.read(&mut first) {
342 Ok(0) => return Ok(None),
343 Ok(_) => length_bytes[0] = first[0],
344 Err(error) => return Err(CodecError::Io(error)),
345 }
346 reader.read_exact(&mut length_bytes[1..]).map_err(map_eof)?;
347 let length = u32::from_le_bytes(length_bytes) as usize;
348 if length < FRAME_META_BYTES {
349 return Err(CodecError::InvalidLength);
350 }
351 let mut meta = [0_u8; FRAME_META_BYTES];
352 reader.read_exact(&mut meta).map_err(map_eof)?;
353 let kind = u16::from_le_bytes(meta[0..2].try_into().expect("two bytes"));
354 let limit = length_limit(kind);
355 if length > limit {
356 return Err(CodecError::TooLarge { length, limit });
357 }
358 let mut body = vec![0; length - FRAME_META_BYTES];
359 reader.read_exact(&mut body).map_err(map_eof)?;
360 decode_parts(&meta, &body).map(Some)
361}
362
363fn map_eof(error: io::Error) -> CodecError {
364 if error.kind() == io::ErrorKind::UnexpectedEof {
365 CodecError::Truncated
366 } else {
367 CodecError::Io(error)
368 }
369}
370
371fn length_limit(kind: u16) -> usize {
372 if kind == 28 {
373 return 16 * 1024 * 1024 + 16;
374 }
375 if matches!(kind, 5 | 13) {
376 MAX_STATE_BYTES
377 } else {
378 MAX_MESSAGE_BYTES
379 }
380}
381
382fn decode_parts(meta: &[u8], body: &[u8]) -> Result<DecodedFrame, CodecError> {
383 let kind = u16::from_le_bytes(meta[0..2].try_into().expect("two bytes"));
384 let flags = u16::from_le_bytes(meta[2..4].try_into().expect("two bytes"));
385 let request = u32::from_le_bytes(meta[4..8].try_into().expect("four bytes"));
386 let length = FRAME_META_BYTES + body.len();
387 let limit = length_limit(kind);
388 if length > limit {
389 return Err(CodecError::TooLarge { length, limit });
390 }
391 let Some(message) = decode_message(kind, body)? else {
392 return Ok(DecodedFrame::Unknown(UnknownFrame {
393 kind,
394 flags,
395 request,
396 body_bytes: body.len(),
397 }));
398 };
399 Ok(DecodedFrame::Known(Frame {
400 flags,
401 request,
402 message,
403 }))
404}
405
406fn encode_message(message: &Message, out: &mut Vec<u8>) -> Result<(), CodecError> {
407 match message {
408 Message::Sandboxed { mechanism } => put_string(out, mechanism)?,
409 Message::Hello {
410 magic,
411 major,
412 minor,
413 } => {
414 out.extend_from_slice(magic);
415 put_u16(out, *major);
416 put_u16(out, *minor);
417 }
418 Message::Load {
419 path,
420 format,
421 class_id,
422 } => {
423 put_string(out, path)?;
424 put_u16(out, *format as u16);
425 out.push(u8::from(class_id.is_some()));
426 if let Some(id) = class_id {
427 out.extend_from_slice(id);
428 }
429 }
430 Message::Configure {
431 rate,
432 max_frames,
433 in_channels,
434 out_channels,
435 } => {
436 put_u32(out, *rate);
437 put_u32(out, *max_frames);
438 put_u16(out, *in_channels);
439 put_u16(out, *out_channels);
440 }
441 Message::Activate
442 | Message::Describe
443 | Message::GetState
444 | Message::CloseEditor
445 | Message::OpenEditor
446 | Message::Shutdown
447 | Message::ParamsChanged
448 | Message::Ack
449 | Message::EditorClosed => {}
450 Message::SetState { blob } | Message::State { blob } => out.extend_from_slice(blob),
451 Message::Scan { path } => put_string(out, path)?,
452 Message::Loaded {
453 name,
454 vendor,
455 latency_frames,
456 inputs,
457 outputs,
458 } => {
459 put_string(out, name)?;
460 put_string(out, vendor)?;
461 put_u32(out, *latency_frames);
462 put_bus_infos(out, inputs)?;
463 put_bus_infos(out, outputs)?;
464 }
465 Message::Classes { entries } => {
466 put_u16(
467 out,
468 u16::try_from(entries.len()).map_err(|_| CodecError::InvalidValue)?,
469 );
470 for entry in entries {
471 out.extend_from_slice(&entry.id);
472 put_string(out, &entry.name)?;
473 put_string(out, &entry.vendor)?;
474 put_string(out, &entry.category)?;
475 }
476 }
477 Message::LatencyChanged { frames } => put_u32(out, *frames),
478 Message::EditorResized { width, height } => {
479 if !(1..=8192).contains(width) || !(1..=8192).contains(height) {
480 return Err(CodecError::InvalidValue);
481 }
482 put_u32(out, *width);
483 put_u32(out, *height);
484 }
485 Message::Editor(action) => match action {
486 EditorAction::Show => out.push(0),
487 EditorAction::Hide => out.push(1),
488 EditorAction::Focus => out.push(2),
489 EditorAction::Resize { width, height } => {
490 out.push(3);
491 put_u32(out, *width);
492 put_u32(out, *height);
493 }
494 EditorAction::Scale(scale) => {
495 out.push(4);
496 put_u32(out, scale.to_bits());
497 }
498 EditorAction::Inspect => out.push(5),
499 EditorAction::Capture => out.push(6),
500 },
501 Message::OpenEditorIn { parent } => {
502 let (tag, handle) = match parent {
503 ParentWindow::Win32 { hwnd } => (0, hwnd),
504 ParentWindow::X11 { window } => (1, window),
505 };
506 out.push(tag);
507 put_u64(out, *handle);
508 }
509 Message::EditorStatus(status) => {
510 out.push(status.platform as u8);
511 out.push(u8::from(status.visible));
512 out.push(u8::from(status.embedded));
513 put_u32(out, status.width);
514 put_u32(out, status.height);
515 put_u32(out, status.scale.to_bits());
516 out.push(u8::from(status.resize_supported));
517 out.push(u8::from(status.scale_supported));
518 }
519 Message::EditorImage(image) => {
520 if !image.valid() {
521 return Err(CodecError::InvalidValue);
522 }
523 put_u32(out, image.width);
524 put_u32(out, image.height);
525 out.extend_from_slice(&image.rgba);
526 }
527 Message::Prepare(setup) => {
528 if !setup.valid() {
529 return Err(CodecError::InvalidValue);
530 }
531 put_u32(out, setup.sample_rate);
532 put_u32(out, setup.quantum);
533 out.push(setup.sample_format as u8);
534 out.push(u8::from(setup.offline));
535 for buses in [&setup.inputs, &setup.outputs] {
536 out.push(buses.len() as u8);
537 for bus in buses {
538 put_u16(out, bus.layout.channels);
539 put_u64(out, bus.layout.arrangement);
540 out.push(u8::from(bus.active));
541 }
542 }
543 }
544 Message::Features(caps) => {
545 out.push(u8::from(caps.f64));
546 put_u16(out, caps.input_event_buses);
547 put_u16(out, caps.output_event_buses);
548 out.push(u8::from(caps.editor));
549 out.push(u8::from(caps.sysex));
550 out.push(u8::from(caps.sample_accurate_parameters));
551 }
552 Message::ListParameters { start, count } => {
553 if *count == 0 || *count > 32 {
554 return Err(CodecError::InvalidValue);
555 }
556 put_u32(out, *start);
557 put_u16(out, *count);
558 }
559 Message::SetParameter { id, value } => {
560 if !value.is_finite() || !(0.0..=1.0).contains(value) {
561 return Err(CodecError::InvalidValue);
562 }
563 put_u32(out, *id);
564 put_u64(out, value.to_bits());
565 }
566 Message::ParameterEdit { id, phase, value } => {
567 if !value.is_finite() || !(0.0..=1.0).contains(value) {
568 return Err(CodecError::InvalidValue);
569 }
570 put_u32(out, *id);
571 out.push(match phase {
572 EditPhase::Begin => 0,
573 EditPhase::Perform => 1,
574 EditPhase::End => 2,
575 });
576 put_u64(out, value.to_bits());
577 }
578 Message::Parameters { total, entries } => {
579 if entries.len() > 32 || *total > 16384 {
580 return Err(CodecError::InvalidValue);
581 }
582 put_u32(out, *total);
583 put_u16(out, entries.len() as u16);
584 for entry in entries {
585 put_u32(out, entry.id);
586 out.push(
587 u8::from(entry.automatable)
588 | (u8::from(entry.read_only) << 1)
589 | (u8::from(entry.hidden) << 2),
590 );
591 put_string(out, &entry.name)?;
592 put_string(out, &entry.unit)?;
593 put_u64(out, entry.value.to_bits());
594 out.push(u8::from(entry.default.is_some()));
595 if let Some(value) = entry.default {
596 put_u64(out, value.to_bits());
597 }
598 out.push(u8::from(entry.steps.is_some()));
599 if let Some(steps) = entry.steps {
600 put_u32(out, steps);
601 }
602 }
603 }
604 Message::RequestError { code, text } | Message::Fault { code, text } => {
605 put_u32(out, *code);
606 put_string(out, text)?;
607 }
608 }
609 Ok(())
610}
611
612fn decode_message(kind: u16, body: &[u8]) -> Result<Option<Message>, CodecError> {
613 let mut cursor = Cursor::new(body);
614 let message = match kind {
615 1 => {
616 let magic = cursor.array::<4>()?;
617 let major = cursor.u16()?;
618 let minor = cursor.u16()?;
619 if magic != MAGIC {
620 return Err(CodecError::WrongMagic);
621 }
622 if major != PROTOCOL_MAJOR {
623 return Err(CodecError::WrongMajor {
624 got: major,
625 expected: PROTOCOL_MAJOR,
626 });
627 }
628 Message::Hello {
629 magic,
630 major,
631 minor,
632 }
633 }
634 29 => Message::Sandboxed {
635 mechanism: cursor.string()?,
636 },
637 2 => {
638 let path = cursor.string()?;
639 let format = match cursor.u16()? {
640 2 => PluginFormat::Vst2,
641 3 => PluginFormat::Vst3,
642 _ => return Err(CodecError::InvalidValue),
643 };
644 let class_id = match cursor.u8()? {
645 0 => None,
646 1 => Some(cursor.array::<16>()?),
647 _ => return Err(CodecError::InvalidValue),
648 };
649 Message::Load {
650 path,
651 format,
652 class_id,
653 }
654 }
655 3 => Message::Configure {
656 rate: cursor.u32()?,
657 max_frames: cursor.u32()?,
658 in_channels: cursor.u16()?,
659 out_channels: cursor.u16()?,
660 },
661 4 => Message::Activate,
662 5 => Message::SetState {
663 blob: body.to_vec(),
664 },
665 6 => Message::GetState,
666 7 => Message::OpenEditor,
667 8 => Message::Shutdown,
668 9 => Message::Scan {
669 path: cursor.string()?,
670 },
671 10 => Message::Loaded {
672 name: cursor.string()?,
673 vendor: cursor.string()?,
674 latency_frames: cursor.u32()?,
675 inputs: cursor.bus_infos()?,
676 outputs: cursor.bus_infos()?,
677 },
678 11 => {
679 let count = cursor.u16()? as usize;
680 let mut entries = Vec::with_capacity(count);
681 for _ in 0..count {
682 entries.push(ClassInfo {
683 id: cursor.array::<16>()?,
684 name: cursor.string()?,
685 vendor: cursor.string()?,
686 category: cursor.string()?,
687 });
688 }
689 Message::Classes { entries }
690 }
691 12 => Message::LatencyChanged {
692 frames: cursor.u32()?,
693 },
694 13 => Message::State {
695 blob: body.to_vec(),
696 },
697 14 => Message::ParamsChanged,
698 15 => Message::Fault {
699 code: cursor.u32()?,
700 text: cursor.string()?,
701 },
702 16 => Message::Ack,
703 17 => Message::EditorClosed,
704 31 => {
705 let width = cursor.u32()?;
706 let height = cursor.u32()?;
707 if !(1..=8192).contains(&width) || !(1..=8192).contains(&height) {
708 return Err(CodecError::InvalidValue);
709 }
710 Message::EditorResized { width, height }
711 }
712 18 => Message::CloseEditor,
713 26 => Message::Editor(match cursor.u8()? {
714 0 => EditorAction::Show,
715 1 => EditorAction::Hide,
716 2 => EditorAction::Focus,
717 3 => EditorAction::Resize {
718 width: cursor.u32()?,
719 height: cursor.u32()?,
720 },
721 4 => EditorAction::Scale(f32::from_bits(cursor.u32()?)),
722 5 => EditorAction::Inspect,
723 6 => EditorAction::Capture,
724 _ => return Err(CodecError::InvalidValue),
725 }),
726 30 => Message::OpenEditorIn {
727 parent: match cursor.u8()? {
728 0 => ParentWindow::Win32 {
729 hwnd: cursor.u64()?,
730 },
731 1 => ParentWindow::X11 {
732 window: cursor.u64()?,
733 },
734 _ => return Err(CodecError::InvalidValue),
735 },
736 },
737 27 => Message::EditorStatus(EditorStatus {
738 platform: match cursor.u8()? {
739 0 => EditorPlatform::Win32,
740 1 => EditorPlatform::Cocoa,
741 2 => EditorPlatform::X11,
742 3 => EditorPlatform::Wayland,
743 _ => return Err(CodecError::InvalidValue),
744 },
745 visible: cursor.boolean()?,
746 embedded: cursor.boolean()?,
747 width: cursor.u32()?,
748 height: cursor.u32()?,
749 scale: f32::from_bits(cursor.u32()?),
750 resize_supported: cursor.boolean()?,
751 scale_supported: cursor.boolean()?,
752 }),
753 28 => {
754 let width = cursor.u32()?;
755 let height = cursor.u32()?;
756 if width == 0
757 || height == 0
758 || width > 2048
759 || height > 2048
760 || cursor.remaining() != width as usize * height as usize * 4
761 {
762 return Err(CodecError::InvalidValue);
763 }
764 let rgba = cursor.take(cursor.remaining())?.to_vec();
765 Message::EditorImage(EditorImage {
766 width,
767 height,
768 rgba,
769 })
770 }
771 23 => {
772 let sample_rate = cursor.u32()?;
773 let quantum = cursor.u32()?;
774 let sample_format = match cursor.u8()? {
775 0 => SampleFormat::F32,
776 1 => SampleFormat::F64,
777 _ => return Err(CodecError::InvalidValue),
778 };
779 let offline = cursor.boolean()?;
780 let mut lists = Vec::new();
781 for _ in 0..2 {
782 let count = cursor.u8()?;
783 if count > 32 {
784 return Err(CodecError::InvalidValue);
785 }
786 let mut buses = Vec::with_capacity(count as usize);
787 for _ in 0..count {
788 buses.push(BusConfiguration {
789 layout: Bus {
790 channels: cursor.u16()?,
791 arrangement: cursor.u64()?,
792 },
793 active: cursor.boolean()?,
794 });
795 }
796 lists.push(buses);
797 }
798 let outputs = lists.pop().expect("two lists");
799 let inputs = lists.pop().expect("two lists");
800 let setup = ProcessingSetup {
801 sample_rate,
802 quantum,
803 sample_format,
804 offline,
805 inputs,
806 outputs,
807 };
808 if !setup.valid() {
809 return Err(CodecError::InvalidValue);
810 }
811 Message::Prepare(setup)
812 }
813 24 => Message::Describe,
814 25 => Message::Features(Capabilities {
815 f64: cursor.boolean()?,
816 input_event_buses: cursor.u16()?,
817 output_event_buses: cursor.u16()?,
818 editor: cursor.boolean()?,
819 sysex: cursor.boolean()?,
820 sample_accurate_parameters: cursor.boolean()?,
821 }),
822 19 => {
823 let start = cursor.u32()?;
824 let count = cursor.u16()?;
825 if count == 0 || count > 32 {
826 return Err(CodecError::InvalidValue);
827 }
828 Message::ListParameters { start, count }
829 }
830 20 => {
831 let total = cursor.u32()?;
832 let count = cursor.u16()?;
833 if total > 16384 || count > 32 {
834 return Err(CodecError::InvalidValue);
835 }
836 let mut entries = Vec::with_capacity(count as usize);
837 for _ in 0..count {
838 let id = cursor.u32()?;
839 let flags = cursor.u8()?;
840 if flags & !7 != 0 {
841 return Err(CodecError::InvalidValue);
842 }
843 let name = cursor.string()?;
844 let unit = cursor.string()?;
845 let value = cursor.normalized()?;
846 let default = match cursor.u8()? {
847 0 => None,
848 1 => Some(cursor.normalized()?),
849 _ => return Err(CodecError::InvalidValue),
850 };
851 let steps = match cursor.u8()? {
852 0 => None,
853 1 => Some(cursor.u32()?),
854 _ => return Err(CodecError::InvalidValue),
855 };
856 entries.push(Parameter {
857 automatable: flags & 1 != 0,
858 read_only: flags & 2 != 0,
859 hidden: flags & 4 != 0,
860 id,
861 name,
862 unit,
863 value,
864 default,
865 steps,
866 });
867 }
868 Message::Parameters { total, entries }
869 }
870 21 => Message::SetParameter {
871 id: cursor.u32()?,
872 value: cursor.normalized()?,
873 },
874 22 => Message::RequestError {
875 code: cursor.u32()?,
876 text: cursor.string()?,
877 },
878 32 => Message::ParameterEdit {
879 id: cursor.u32()?,
880 phase: match cursor.u8()? {
881 0 => EditPhase::Begin,
882 1 => EditPhase::Perform,
883 2 => EditPhase::End,
884 _ => return Err(CodecError::InvalidValue),
885 },
886 value: cursor.normalized()?,
887 },
888 _ => return Ok(None),
889 };
890 if !matches!(kind, 5 | 13) && cursor.remaining() != 0 {
891 return Err(CodecError::TrailingBytes);
892 }
893 Ok(Some(message))
894}
895
896fn put_u16(out: &mut Vec<u8>, value: u16) {
897 out.extend_from_slice(&value.to_le_bytes());
898}
899fn put_u32(out: &mut Vec<u8>, value: u32) {
900 out.extend_from_slice(&value.to_le_bytes());
901}
902fn put_u64(out: &mut Vec<u8>, value: u64) {
903 out.extend_from_slice(&value.to_le_bytes());
904}
905
906fn put_string(out: &mut Vec<u8>, value: &str) -> Result<(), CodecError> {
907 if value.len() > MAX_STRING_BYTES {
908 return Err(CodecError::StringTooLong);
909 }
910 put_u16(out, value.len() as u16);
911 out.extend_from_slice(value.as_bytes());
912 Ok(())
913}
914
915fn put_bus_infos(out: &mut Vec<u8>, buses: &[BusInfo]) -> Result<(), CodecError> {
916 put_u16(
917 out,
918 u16::try_from(buses.len()).map_err(|_| CodecError::InvalidValue)?,
919 );
920 for bus in buses {
921 put_u16(out, bus.layout.channels);
922 put_u64(out, bus.layout.arrangement);
923 out.push(match bus.kind {
924 BusKind::Main => 0,
925 BusKind::Aux => 1,
926 });
927 out.push(u8::from(bus.default_active));
928 put_string(out, &bus.name)?;
929 }
930 Ok(())
931}
932
933struct Cursor<'a> {
934 bytes: &'a [u8],
935 position: usize,
936}
937
938impl<'a> Cursor<'a> {
939 fn new(bytes: &'a [u8]) -> Self {
940 Self { bytes, position: 0 }
941 }
942 fn remaining(&self) -> usize {
943 self.bytes.len() - self.position
944 }
945 fn take(&mut self, count: usize) -> Result<&'a [u8], CodecError> {
946 let end = self
947 .position
948 .checked_add(count)
949 .ok_or(CodecError::Truncated)?;
950 let value = self
951 .bytes
952 .get(self.position..end)
953 .ok_or(CodecError::Truncated)?;
954 self.position = end;
955 Ok(value)
956 }
957 fn array<const N: usize>(&mut self) -> Result<[u8; N], CodecError> {
958 Ok(self.take(N)?.try_into().expect("exact length"))
959 }
960 fn boolean(&mut self) -> Result<bool, CodecError> {
961 match self.u8()? {
962 0 => Ok(false),
963 1 => Ok(true),
964 _ => Err(CodecError::InvalidValue),
965 }
966 }
967 fn u8(&mut self) -> Result<u8, CodecError> {
968 Ok(self.take(1)?[0])
969 }
970 fn u16(&mut self) -> Result<u16, CodecError> {
971 Ok(u16::from_le_bytes(self.array()?))
972 }
973 fn u32(&mut self) -> Result<u32, CodecError> {
974 Ok(u32::from_le_bytes(self.array()?))
975 }
976 fn u64(&mut self) -> Result<u64, CodecError> {
977 Ok(u64::from_le_bytes(self.array()?))
978 }
979 fn normalized(&mut self) -> Result<f64, CodecError> {
980 let value = f64::from_bits(self.u64()?);
981 if value.is_finite() && (0.0..=1.0).contains(&value) {
982 Ok(value)
983 } else {
984 Err(CodecError::InvalidValue)
985 }
986 }
987 fn string(&mut self) -> Result<String, CodecError> {
988 let length = self.u16()? as usize;
989 if length > MAX_STRING_BYTES {
990 return Err(CodecError::StringTooLong);
991 }
992 String::from_utf8(self.take(length)?.to_vec()).map_err(|_| CodecError::InvalidUtf8)
993 }
994 fn bus_infos(&mut self) -> Result<Vec<BusInfo>, CodecError> {
995 let count = self.u16()? as usize;
996 let mut buses = Vec::with_capacity(count);
997 for _ in 0..count {
998 let layout = Bus {
999 channels: self.u16()?,
1000 arrangement: self.u64()?,
1001 };
1002 let kind = match self.u8()? {
1003 0 => BusKind::Main,
1004 1 => BusKind::Aux,
1005 _ => return Err(CodecError::InvalidValue),
1006 };
1007 let flags = self.u8()?;
1008 if flags & !1 != 0 {
1009 return Err(CodecError::InvalidValue);
1010 }
1011 buses.push(BusInfo {
1012 layout,
1013 kind,
1014 name: self.string()?,
1015 default_active: flags & 1 != 0,
1016 });
1017 }
1018 Ok(buses)
1019 }
1020}
1021
1022#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1023pub enum WatchState {
1024 Starting,
1025 Loading,
1026 Configuring,
1027 Running,
1028 Stopping,
1029 Stopped,
1031 Backoff,
1032 Disabled,
1033}
1034
1035#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1036pub enum KillReason {
1037 HelloTimeout,
1038 LoadTimeout,
1039 ConfigureTimeout,
1040 HeartbeatTimeout,
1041 RequestTimeout,
1042 StopTimeout,
1043}
1044
1045#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1046pub enum DisableReason {
1047 ThreeFailuresInTenMinutes,
1048}
1049
1050#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1055pub enum Action {
1056 Kill { reason: KillReason },
1057 Spawn,
1058 Disable { reason: DisableReason },
1059 EnterFallback,
1060 LeaveFallback,
1061 SaveStateNow,
1062 None,
1063}
1064
1065#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1066pub enum Received {
1067 Hello,
1068 Loaded,
1069 Configured,
1070 Response { request: u32 },
1071 ParamsChanged,
1072 EditorClosed,
1073}
1074
1075#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1076pub enum RequestKind {
1077 StateSave,
1078 Editor,
1079}
1080
1081const STARTING_MS: u64 = 5_000;
1082const LOADING_MS: u64 = 60_000;
1083const CONFIGURING_MS: u64 = 10_000;
1084const HEARTBEAT_MS: u64 = 500;
1085const OBSERVER_GAP_MS: u64 = HEARTBEAT_MS / 2;
1088const REQUEST_MS: u64 = 10_000;
1089const STOPPING_MS: u64 = 2_000;
1090const PARAM_QUIET_MS: u64 = 2_000;
1091const FAILURE_WINDOW_MS: u64 = 10 * 60 * 1_000;
1092const BACKOFF_MS: [u64; 3] = [500, 2_000, 8_000];
1093
1094#[derive(Debug)]
1095pub struct HostWatch {
1096 state: WatchState,
1097 entered_at: u64,
1098 heartbeat_value: u64,
1099 heartbeat_at: u64,
1100 observed_at: u64,
1101 pending_requests: Vec<(u32, u64)>,
1102 params_changed_at: Option<u64>,
1103 missed_blocks: u8,
1104 fallback: bool,
1105 failures: VecDeque<u64>,
1106 backoff_attempt: usize,
1107 spawn_at: Option<u64>,
1108}
1109
1110impl HostWatch {
1111 pub fn new(now_ms: u64) -> Self {
1112 Self {
1113 state: WatchState::Starting,
1114 entered_at: now_ms,
1115 heartbeat_value: 0,
1116 heartbeat_at: now_ms,
1117 observed_at: now_ms,
1118 pending_requests: Vec::new(),
1119 params_changed_at: None,
1120 missed_blocks: 0,
1121 fallback: false,
1122 failures: VecDeque::with_capacity(3),
1123 backoff_attempt: 0,
1124 spawn_at: None,
1125 }
1126 }
1127
1128 pub fn state(&self) -> WatchState {
1129 self.state
1130 }
1131
1132 pub fn received(&mut self, now_ms: u64, message: Received) -> Action {
1133 match message {
1134 Received::Hello if self.state == WatchState::Starting => {
1135 self.enter(WatchState::Loading, now_ms)
1136 }
1137 Received::Loaded if self.state == WatchState::Loading => {
1138 self.enter(WatchState::Configuring, now_ms)
1139 }
1140 Received::Configured if self.state == WatchState::Configuring => {
1141 self.enter(WatchState::Running, now_ms);
1142 self.heartbeat_at = now_ms;
1143 self.observed_at = now_ms;
1144 return Action::SaveStateNow;
1145 }
1146 Received::Response { request } => {
1147 self.pending_requests.retain(|(id, _)| *id != request);
1148 }
1149 Received::ParamsChanged => self.params_changed_at = Some(now_ms),
1150 Received::EditorClosed => return Action::SaveStateNow,
1151 _ => {}
1152 }
1153 Action::None
1154 }
1155
1156 pub fn heartbeat(&mut self, now_ms: u64, value: u64) -> Action {
1160 let gap = now_ms.saturating_sub(self.observed_at);
1161 if gap > OBSERVER_GAP_MS {
1162 self.heartbeat_at = self.heartbeat_at.saturating_add(gap).min(now_ms);
1163 }
1164 self.observed_at = now_ms;
1165 if value != self.heartbeat_value {
1166 self.heartbeat_value = value;
1167 self.heartbeat_at = now_ms;
1168 }
1169 Action::None
1170 }
1171
1172 pub fn request_sent(&mut self, now_ms: u64, request: u32, _kind: RequestKind) -> Action {
1173 self.pending_requests.retain(|(id, _)| *id != request);
1174 self.pending_requests.push((request, now_ms));
1175 Action::None
1176 }
1177
1178 pub fn block_delivered(&mut self) -> Action {
1179 self.missed_blocks = 0;
1180 if self.fallback {
1181 self.fallback = false;
1182 Action::LeaveFallback
1183 } else {
1184 Action::None
1185 }
1186 }
1187
1188 pub fn block_missed(&mut self) -> Action {
1189 self.missed_blocks = self.missed_blocks.saturating_add(1);
1190 if self.missed_blocks == 3 && !self.fallback {
1191 self.fallback = true;
1192 Action::EnterFallback
1193 } else {
1194 Action::None
1195 }
1196 }
1197
1198 pub fn shutdown(&mut self, now_ms: u64) -> Action {
1199 self.enter(WatchState::Stopping, now_ms);
1200 Action::SaveStateNow
1201 }
1202
1203 pub fn process_exited(&mut self, now_ms: u64, _exit_code: i32) -> Action {
1204 match self.state {
1205 WatchState::Backoff | WatchState::Disabled | WatchState::Stopped => Action::None,
1206 WatchState::Stopping => {
1207 self.enter(WatchState::Stopped, now_ms);
1208 Action::None
1209 }
1210 _ => self.failure(now_ms, None),
1211 }
1212 }
1213
1214 pub fn tick(&mut self, now_ms: u64) -> Action {
1215 if self.state == WatchState::Backoff {
1216 if self.spawn_at.is_some_and(|deadline| now_ms >= deadline) {
1217 self.enter(WatchState::Starting, now_ms);
1218 self.spawn_at = None;
1219 return Action::Spawn;
1220 }
1221 return Action::None;
1222 }
1223 if matches!(self.state, WatchState::Disabled | WatchState::Stopped) {
1224 return Action::None;
1225 }
1226 if self.state == WatchState::Running {
1227 if now_ms.saturating_sub(self.heartbeat_at) >= HEARTBEAT_MS {
1228 return self.failure(now_ms, Some(KillReason::HeartbeatTimeout));
1229 }
1230 if self
1231 .pending_requests
1232 .iter()
1233 .any(|(_, sent)| now_ms.saturating_sub(*sent) >= REQUEST_MS)
1234 {
1235 return self.failure(now_ms, Some(KillReason::RequestTimeout));
1236 }
1237 if self
1238 .params_changed_at
1239 .is_some_and(|changed| now_ms.saturating_sub(changed) >= PARAM_QUIET_MS)
1240 {
1241 self.params_changed_at = None;
1242 return Action::SaveStateNow;
1243 }
1244 return Action::None;
1245 }
1246 let (deadline, reason) = match self.state {
1247 WatchState::Starting => (STARTING_MS, KillReason::HelloTimeout),
1248 WatchState::Loading => (LOADING_MS, KillReason::LoadTimeout),
1249 WatchState::Configuring => (CONFIGURING_MS, KillReason::ConfigureTimeout),
1250 WatchState::Stopping => {
1251 if now_ms.saturating_sub(self.entered_at) >= STOPPING_MS {
1254 self.enter(WatchState::Stopped, now_ms);
1255 return Action::Kill {
1256 reason: KillReason::StopTimeout,
1257 };
1258 }
1259 return Action::None;
1260 }
1261 _ => return Action::None,
1262 };
1263 if now_ms.saturating_sub(self.entered_at) >= deadline {
1264 self.failure(now_ms, Some(reason))
1265 } else {
1266 Action::None
1267 }
1268 }
1269
1270 fn enter(&mut self, state: WatchState, now_ms: u64) {
1271 self.state = state;
1272 self.entered_at = now_ms;
1273 }
1274
1275 fn failure(&mut self, now_ms: u64, kill: Option<KillReason>) -> Action {
1276 while self
1277 .failures
1278 .front()
1279 .is_some_and(|time| now_ms.saturating_sub(*time) > FAILURE_WINDOW_MS)
1280 {
1281 self.failures.pop_front();
1282 }
1283 self.failures.push_back(now_ms);
1284 if self.failures.len() >= 3 {
1285 self.state = WatchState::Disabled;
1286 self.pending_requests.clear();
1287 self.fallback = true;
1288 return Action::Disable {
1289 reason: DisableReason::ThreeFailuresInTenMinutes,
1290 };
1291 }
1292 if self.failures.len() == 1 {
1295 self.backoff_attempt = 0;
1296 }
1297 let delay = BACKOFF_MS[self.backoff_attempt.min(BACKOFF_MS.len() - 1)];
1298 self.backoff_attempt = (self.backoff_attempt + 1).min(BACKOFF_MS.len() - 1);
1299 self.state = WatchState::Backoff;
1300 self.entered_at = now_ms;
1301 self.spawn_at = Some(now_ms.saturating_add(delay));
1302 self.pending_requests.clear();
1303 self.params_changed_at = None;
1304 self.missed_blocks = 0;
1305 self.fallback = true;
1306 if let Some(reason) = kill {
1307 Action::Kill { reason }
1308 } else {
1309 Action::EnterFallback
1310 }
1311 }
1312}
1313
1314#[derive(Clone, Debug, Default, Eq, PartialEq)]
1317pub struct StateHistory {
1318 current: Option<Vec<u8>>,
1319 previous: Option<Vec<u8>>,
1320 restoring_current: bool,
1321}
1322
1323impl StateHistory {
1324 pub fn new() -> Self {
1325 Self::default()
1326 }
1327 pub fn save(&mut self, blob: Vec<u8>) {
1328 if self.current.as_ref() == Some(&blob) {
1329 return;
1330 }
1331 self.previous = self.current.replace(blob);
1332 self.restoring_current = false;
1333 }
1334 pub fn begin_restore(&mut self) -> Option<&[u8]> {
1335 self.restoring_current = self.current.is_some();
1336 self.current.as_deref()
1337 }
1338 pub fn restore_succeeded(&mut self) {
1339 self.restoring_current = false;
1340 }
1341 pub fn crash_after_restore(&mut self) -> Option<&[u8]> {
1342 if self.restoring_current {
1343 self.restoring_current = false;
1344 self.previous.as_deref()
1345 } else {
1346 self.current.as_deref()
1347 }
1348 }
1349 pub fn current(&self) -> Option<&[u8]> {
1350 self.current.as_deref()
1351 }
1352 pub fn previous(&self) -> Option<&[u8]> {
1353 self.previous.as_deref()
1354 }
1355}
1356
1357pub fn duration_ms(duration: Duration) -> u64 {
1358 u64::try_from(duration.as_millis()).unwrap_or(u64::MAX)
1359}