sim_lib_midi_smf/error.rs
1#![forbid(unsafe_code)]
2
3use thiserror::Error;
4
5/// Errors raised while reading or writing a Standard MIDI File.
6#[derive(Debug, Error, Clone, PartialEq, Eq)]
7pub enum SmfError {
8 /// The `MThd`/`MTrk` chunk header was malformed.
9 #[error("invalid header at byte {offset}")]
10 InvalidHeader {
11 /// Byte offset of the bad header.
12 offset: usize,
13 },
14 /// The byte stream ended before the structure was complete.
15 #[error("unexpected end of file at byte {offset}")]
16 UnexpectedEof {
17 /// Byte offset where more input was expected.
18 offset: usize,
19 },
20 /// A variable-length quantity was not terminated within four bytes.
21 #[error("invalid VLQ at byte {offset}")]
22 InvalidVlq {
23 /// Byte offset where the VLQ began.
24 offset: usize,
25 },
26 /// The header carried an invalid metrical or SMPTE division.
27 #[error("invalid SMF division 0x{raw:04x} at byte {offset}")]
28 InvalidDivision {
29 /// Byte offset of the division field.
30 offset: usize,
31 /// The raw division value.
32 raw: u16,
33 },
34 /// A configured defensive read limit was exceeded.
35 #[error("SMF {kind} limit exceeded at byte {offset}: requested {actual}, maximum {maximum}")]
36 LimitExceeded {
37 /// Byte offset of the value or event that crossed the limit.
38 offset: usize,
39 /// Resource being bounded.
40 kind: SmfLimitKind,
41 /// Requested or observed amount.
42 actual: usize,
43 /// Configured maximum.
44 maximum: usize,
45 },
46 /// A bounded allocation failed even though its requested size was within
47 /// the configured limits.
48 #[error("SMF allocation of {requested} items failed at byte {offset}")]
49 AllocationFailed {
50 /// Byte offset of the structure that required the allocation.
51 offset: usize,
52 /// Number of items requested.
53 requested: usize,
54 },
55 /// A data byte appeared with no running status in effect.
56 #[error("malformed running status at byte {offset}")]
57 MalformedRunningStatus {
58 /// Byte offset of the offending data byte.
59 offset: usize,
60 },
61 /// A status byte that the reader does not handle was encountered.
62 #[error("unsupported MIDI status 0x{status:02x} at byte {offset}")]
63 UnsupportedStatus {
64 /// Byte offset of the status byte.
65 offset: usize,
66 /// The unsupported status byte.
67 status: u8,
68 },
69 /// A channel message carried an out-of-range data byte.
70 #[error("invalid channel payload at byte {offset}")]
71 InvalidChannelData {
72 /// Byte offset of the bad data.
73 offset: usize,
74 },
75 /// A recognised meta event used a payload length forbidden by SMF.
76 #[error(
77 "invalid length {actual} for meta event 0x{type_byte:02x} at byte {offset}; expected {expected}"
78 )]
79 InvalidMetaLength {
80 /// Byte offset of the meta type byte.
81 offset: usize,
82 /// Meta event type.
83 type_byte: u8,
84 /// Required payload length.
85 expected: usize,
86 /// Encoded payload length.
87 actual: usize,
88 },
89 /// A track chunk ended without its required end-of-track meta event.
90 #[error("missing end-of-track event at byte {offset}")]
91 MissingEndOfTrack {
92 /// Byte offset immediately after the track chunk body.
93 offset: usize,
94 },
95 /// A system message used an invalid status, length, or data byte.
96 #[error("invalid system event 0x{status:02x} at byte {offset}")]
97 InvalidSystemEvent {
98 /// Byte offset of the status or first invalid data byte.
99 offset: usize,
100 /// System status byte.
101 status: u8,
102 },
103 /// The header format and the track count are inconsistent (for example,
104 /// format 0 with more than one track).
105 #[error("SMF format/track count mismatch")]
106 FormatTrackMismatch,
107 /// Format 2 contains independent patterns and cannot be flattened onto a
108 /// shared timeline without selecting a track.
109 #[error("SMF format 2 patterns require explicit track selection")]
110 IndependentPatternsCannotMerge,
111 /// The track count cannot be represented in the SMF header.
112 #[error("SMF track count {0} is outside 0..=65535")]
113 TrackCountOutOfRange(usize),
114 /// The ticks-per-quarter value cannot be represented as metrical SMF TPQ.
115 #[error("SMF ticks-per-quarter {0} cannot be written as metrical TPQ")]
116 TpqOutOfRange(u32),
117 /// An event time could not be represented exactly at the file resolution.
118 #[error("event time cannot be represented exactly at target TPQ")]
119 InexactEventTime,
120 /// Track events were not monotonic in absolute time, yielding a negative
121 /// delta.
122 #[error("track events are not monotonic in absolute time")]
123 NegativeDelta,
124 /// A track delta cannot be represented as an SMF four-byte VLQ.
125 #[error("SMF delta {0} exceeds the four-byte VLQ limit")]
126 DeltaOutOfRange(i64),
127 /// A track chunk body cannot be represented in the SMF chunk length field.
128 #[error("SMF chunk length {0} exceeds u32::MAX")]
129 ChunkTooLarge(usize),
130 /// A meta or SysEx payload length cannot be represented as an SMF four-byte
131 /// VLQ.
132 #[error("SMF payload length {0} exceeds the four-byte VLQ limit")]
133 PayloadTooLarge(usize),
134 /// Absolute tick accumulation overflowed the in-memory event time.
135 #[error("SMF absolute tick time overflow at byte {offset}")]
136 TimeOverflow {
137 /// Byte offset of the delta that overflowed.
138 offset: usize,
139 },
140}
141
142/// Defensive parser resources that can be bounded with [`SmfReadLimits`].
143///
144/// [`SmfReadLimits`]: crate::SmfReadLimits
145#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
146pub enum SmfLimitKind {
147 /// Complete input bytes.
148 FileBytes,
149 /// Header chunk body bytes.
150 HeaderBytes,
151 /// Declared track count.
152 TrackCount,
153 /// One track chunk body.
154 TrackBytes,
155 /// Events across the complete file.
156 EventCount,
157 /// One meta or system-exclusive payload.
158 EventPayloadBytes,
159 /// Meta and system-exclusive payload bytes across the complete file.
160 TotalPayloadBytes,
161}
162
163impl std::fmt::Display for SmfLimitKind {
164 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 formatter.write_str(match self {
166 Self::FileBytes => "file-bytes",
167 Self::HeaderBytes => "header-bytes",
168 Self::TrackCount => "track-count",
169 Self::TrackBytes => "track-bytes",
170 Self::EventCount => "event-count",
171 Self::EventPayloadBytes => "event-payload-bytes",
172 Self::TotalPayloadBytes => "total-payload-bytes",
173 })
174 }
175}