1use crate::error::Error;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct TimeTag {
9 pub seconds: u64,
11 pub fractional: u64,
13}
14
15impl TimeTag {
16 pub fn immediate() -> Self {
18 TimeTag {
19 seconds: 0,
20 fractional: 0,
21 }
22 }
23}
24
25#[derive(Debug, Clone, PartialEq)]
27pub enum OscType {
28 Int(i32),
30 Float(f32),
32 String(String),
34 Blob(Vec<u8>),
36 Timetag(TimeTag),
38}
39
40#[derive(Debug, Clone, PartialEq)]
42pub struct OscMessage {
43 pub addr: String,
45 pub args: Vec<OscType>,
47}
48
49#[derive(Debug, Clone, PartialEq)]
51pub struct OscBundle {
52 pub timetag: TimeTag,
54 pub packets: Vec<OscPacket>,
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub enum OscPacket {
61 Message(OscMessage),
63 Bundle(OscBundle),
65}
66
67pub fn decode(buf: &[u8]) -> Result<OscPacket, Error> {
72 if buf.starts_with(b"/") {
73 decode_message(buf).map(OscPacket::Message)
74 } else if buf.starts_with(b"#bundle\0") {
75 decode_bundle(buf).map(OscPacket::Bundle)
76 } else {
77 Err(Error::InvalidPacket)
78 }
79}
80
81fn decode_message(buf: &[u8]) -> Result<OscMessage, Error> {
82 let (addr, rest) = read_string(buf)?;
83
84 if rest.is_empty() {
85 return Ok(OscMessage {
86 addr,
87 args: Vec::new(),
88 });
89 }
90
91 let (type_tags, rest) = read_string(rest)?;
92
93 if !type_tags.starts_with(',') {
94 return Err(Error::Parse("missing type tag prefix ','".into()));
95 }
96
97 let type_chars: Vec<char> = type_tags[1..].chars().collect();
98 let mut args = Vec::with_capacity(type_chars.len());
99 let mut ptr = rest;
100
101 for ch in type_chars {
102 let (arg, remaining) = match ch {
103 'i' => {
104 let (v, r) = read_i32(ptr)?;
105 (OscType::Int(v), r)
106 }
107 'f' => {
108 let (v, r) = read_f32(ptr)?;
109 (OscType::Float(v), r)
110 }
111 's' => {
112 let (v, r) = read_string(ptr)?;
113 (OscType::String(v), r)
114 }
115 'b' => {
116 let (v, r) = read_blob(ptr)?;
117 (OscType::Blob(v), r)
118 }
119 't' => {
120 let (v, r) = read_timetag(ptr)?;
121 (OscType::Timetag(v), r)
122 }
123 'T' => (OscType::Int(1), ptr),
124 'F' => (OscType::Int(0), ptr),
125 _ => return Err(Error::Parse(format!("unknown type tag: {}", ch))),
126 };
127 args.push(arg);
128 ptr = remaining;
129 }
130
131 Ok(OscMessage { addr, args })
132}
133
134fn decode_bundle(buf: &[u8]) -> Result<OscBundle, Error> {
135 let buf = &buf[8..];
136
137 let (timetag, mut rest) = read_timetag(buf)?;
138
139 let mut packets = Vec::new();
140
141 while !rest.is_empty() {
142 let (size, rest_after_size) = read_i32(rest)?;
143 let size = size as usize;
144
145 if size > rest_after_size.len() {
146 return Err(Error::Parse(
147 "bundle element size exceeds remaining data length".into(),
148 ));
149 }
150
151 let packet_data = &rest_after_size[..size];
152 let packet = decode(packet_data)?;
153 packets.push(packet);
154
155 rest = &rest_after_size[size..];
156 }
157
158 Ok(OscBundle { timetag, packets })
159}
160
161pub fn encode(packet: &OscPacket) -> Result<Vec<u8>, Error> {
163 match packet {
164 OscPacket::Message(msg) => encode_message(msg),
165 OscPacket::Bundle(bundle) => encode_bundle(bundle),
166 }
167}
168
169fn encode_message(msg: &OscMessage) -> Result<Vec<u8>, Error> {
170 let mut buf = Vec::new();
171
172 write_string(&mut buf, &msg.addr);
173
174 let mut type_str = String::from(",");
175 for arg in &msg.args {
176 match arg {
177 OscType::Int(_) => type_str.push('i'),
178 OscType::Float(_) => type_str.push('f'),
179 OscType::String(_) => type_str.push('s'),
180 OscType::Blob(_) => type_str.push('b'),
181 OscType::Timetag(_) => type_str.push('t'),
182 }
183 }
184
185 write_string(&mut buf, &type_str);
186
187 for arg in &msg.args {
188 match arg {
189 OscType::Int(v) => buf.extend_from_slice(&v.to_be_bytes()),
190 OscType::Float(v) => buf.extend_from_slice(&v.to_be_bytes()),
191 OscType::String(v) => write_string(&mut buf, v),
192 OscType::Blob(v) => write_blob(&mut buf, v),
193 OscType::Timetag(v) => write_timetag(&mut buf, *v),
194 }
195 }
196
197 Ok(buf)
198}
199
200fn encode_bundle(bundle: &OscBundle) -> Result<Vec<u8>, Error> {
201 let mut buf = Vec::new();
202
203 buf.extend_from_slice(b"#bundle\0");
204 write_timetag(&mut buf, bundle.timetag);
205
206 for packet in &bundle.packets {
207 let packet_bytes = encode(packet)?;
208 let size = packet_bytes.len() as i32;
209 buf.extend_from_slice(&size.to_be_bytes());
210 buf.extend_from_slice(&packet_bytes);
211 }
212
213 Ok(buf)
214}
215
216fn read_i32(buf: &[u8]) -> Result<(i32, &[u8]), Error> {
217 if buf.len() < 4 {
218 return Err(Error::Parse("buffer too short for int32".into()));
219 }
220 let bytes: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]];
221 Ok((i32::from_be_bytes(bytes), &buf[4..]))
222}
223
224fn read_f32(buf: &[u8]) -> Result<(f32, &[u8]), Error> {
225 if buf.len() < 4 {
226 return Err(Error::Parse("buffer too short for float32".into()));
227 }
228 let bytes: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]];
229 Ok((f32::from_be_bytes(bytes), &buf[4..]))
230}
231
232fn read_string(buf: &[u8]) -> Result<(String, &[u8]), Error> {
233 let null_pos = buf
234 .iter()
235 .position(|&b| b == 0)
236 .ok_or_else(|| Error::Parse("string without null terminator".into()))?;
237
238 let s = std::str::from_utf8(&buf[..null_pos])
239 .map_err(|e| Error::Parse(format!("invalid UTF-8: {}", e)))?
240 .to_string();
241
242 let padded_len = ((null_pos + 4) / 4) * 4;
243
244 if padded_len > buf.len() {
245 return Err(Error::Parse("buffer too short for padded string".into()));
246 }
247
248 Ok((s, &buf[padded_len..]))
249}
250
251fn read_blob(buf: &[u8]) -> Result<(Vec<u8>, &[u8]), Error> {
252 let (size, rest) = read_i32(buf)?;
253 let size = size as usize;
254
255 if size > rest.len() {
256 return Err(Error::Parse("blob size exceeds data length".into()));
257 }
258
259 let data = rest[..size].to_vec();
260
261 let padded_len = size.div_ceil(4) * 4;
262
263 if padded_len > rest.len() {
264 return Err(Error::Parse("buffer too short for padded blob".into()));
265 }
266
267 Ok((data, &rest[padded_len..]))
268}
269
270fn read_timetag(buf: &[u8]) -> Result<(TimeTag, &[u8]), Error> {
271 if buf.len() < 8 {
272 return Err(Error::Parse("buffer too short for timetag".into()));
273 }
274
275 let sec_bytes: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]];
276 let frac_bytes: [u8; 4] = [buf[4], buf[5], buf[6], buf[7]];
277
278 let seconds = u32::from_be_bytes(sec_bytes) as u64;
279 let fractional = u32::from_be_bytes(frac_bytes) as u64;
280
281 Ok((
282 TimeTag {
283 seconds,
284 fractional,
285 },
286 &buf[8..],
287 ))
288}
289
290fn write_string(buf: &mut Vec<u8>, s: &str) {
291 let bytes = s.as_bytes();
292 buf.extend_from_slice(bytes);
293 buf.push(0);
294
295 let padded_len = (bytes.len() + 1).div_ceil(4) * 4;
296 let pad_len = padded_len - (bytes.len() + 1);
297 buf.extend(std::iter::repeat_n(0u8, pad_len));
298}
299
300fn write_blob(buf: &mut Vec<u8>, data: &[u8]) {
301 let size = data.len() as i32;
302 buf.extend_from_slice(&size.to_be_bytes());
303 buf.extend_from_slice(data);
304
305 let padded_len = data.len().div_ceil(4) * 4;
306 let pad_len = padded_len - data.len();
307 buf.extend(std::iter::repeat_n(0u8, pad_len));
308}
309
310fn write_timetag(buf: &mut Vec<u8>, tag: TimeTag) {
311 buf.extend_from_slice(&(tag.seconds as u32).to_be_bytes());
312 buf.extend_from_slice(&(tag.fractional as u32).to_be_bytes());
313}
314
315pub fn pattern_match(pattern: &str, addr: &str) -> bool {
319 if pattern == "*" {
320 return true;
321 }
322
323 let pat_parts: Vec<&str> = pattern.split('/').collect();
324 let addr_parts: Vec<&str> = addr.split('/').collect();
325
326 if pat_parts.len() != addr_parts.len() {
327 return false;
328 }
329
330 for (p, a) in pat_parts.iter().zip(addr_parts.iter()) {
331 if *p == "*" {
332 continue;
333 }
334 if p != a {
335 return false;
336 }
337 }
338
339 true
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_message_int_float() {
348 let msg = OscMessage {
349 addr: "/test/foo".into(),
350 args: vec![OscType::Int(42), OscType::Float(1.5)],
351 };
352 let packet = OscPacket::Message(msg.clone());
353 let bytes = encode(&packet).unwrap();
354 let decoded = decode(&bytes).unwrap();
355 assert_eq!(packet, decoded);
356 }
357
358 #[test]
359 fn test_message_string() {
360 let msg = OscMessage {
361 addr: "/hello".into(),
362 args: vec![OscType::String("world".into())],
363 };
364 let packet = OscPacket::Message(msg);
365 let bytes = encode(&packet).unwrap();
366 let decoded = decode(&bytes).unwrap();
367 assert_eq!(packet, decoded);
368 }
369
370 #[test]
371 fn test_message_blob() {
372 let msg = OscMessage {
373 addr: "/data".into(),
374 args: vec![OscType::Blob(vec![0x00, 0x01, 0x02, 0x03, 0x04])],
375 };
376 let packet = OscPacket::Message(msg);
377 let bytes = encode(&packet).unwrap();
378 let decoded = decode(&bytes).unwrap();
379 assert_eq!(packet, decoded);
380 }
381
382 #[test]
383 fn test_message_timetag() {
384 let msg = OscMessage {
385 addr: "/sync".into(),
386 args: vec![OscType::Timetag(TimeTag {
387 seconds: 100,
388 fractional: 500,
389 })],
390 };
391 let packet = OscPacket::Message(msg);
392 let bytes = encode(&packet).unwrap();
393 let decoded = decode(&bytes).unwrap();
394 assert_eq!(packet, decoded);
395 }
396
397 #[test]
398 fn test_message_mixed_args() {
399 let msg = OscMessage {
400 addr: "/mix".into(),
401 args: vec![
402 OscType::Int(-1),
403 OscType::Float(2.5),
404 OscType::String("test".into()),
405 OscType::Blob(vec![0xFF]),
406 ],
407 };
408 let packet = OscPacket::Message(msg);
409 let bytes = encode(&packet).unwrap();
410 let decoded = decode(&bytes).unwrap();
411 assert_eq!(packet, decoded);
412 }
413
414 #[test]
415 fn test_empty_message() {
416 let msg = OscMessage {
417 addr: "/test".into(),
418 args: vec![],
419 };
420 let bytes = encode(&OscPacket::Message(msg.clone())).unwrap();
421 let decoded = decode(&bytes).unwrap();
422 assert_eq!(OscPacket::Message(msg), decoded);
423 }
424
425 #[test]
426 fn test_bundle() {
427 let msg1 = OscMessage {
428 addr: "/a".into(),
429 args: vec![OscType::Int(1)],
430 };
431 let msg2 = OscMessage {
432 addr: "/b".into(),
433 args: vec![OscType::Float(2.0)],
434 };
435 let bundle = OscBundle {
436 timetag: TimeTag::immediate(),
437 packets: vec![OscPacket::Message(msg1), OscPacket::Message(msg2)],
438 };
439 let packet = OscPacket::Bundle(bundle);
440 let bytes = encode(&packet).unwrap();
441 let decoded = decode(&bytes).unwrap();
442 assert_eq!(packet, decoded);
443 }
444
445 #[test]
446 fn test_nested_bundle() {
447 let inner = OscBundle {
448 timetag: TimeTag::immediate(),
449 packets: vec![OscPacket::Message(OscMessage {
450 addr: "/inner".into(),
451 args: vec![OscType::Int(99)],
452 })],
453 };
454 let outer = OscBundle {
455 timetag: TimeTag {
456 seconds: 1,
457 fractional: 0,
458 },
459 packets: vec![OscPacket::Bundle(inner)],
460 };
461 let packet = OscPacket::Bundle(outer);
462 let bytes = encode(&packet).unwrap();
463 let decoded = decode(&bytes).unwrap();
464 assert_eq!(packet, decoded);
465 }
466
467 #[test]
468 fn test_decode_invalid() {
469 assert!(decode(b"garbage").is_err());
470 assert!(decode(b"").is_err());
471 }
472
473 #[test]
474 fn test_pattern_match_exact() {
475 assert!(pattern_match("/audio/volume", "/audio/volume"));
476 assert!(!pattern_match("/audio/volume", "/audio/pan"));
477 }
478
479 #[test]
480 fn test_pattern_match_wildcard() {
481 assert!(pattern_match("/audio/*", "/audio/volume"));
482 assert!(pattern_match("/audio/*", "/audio/pan"));
483 assert!(!pattern_match("/audio/*", "/mixer/volume"));
484 assert!(pattern_match("*", "/anything/works"));
485 }
486
487 #[test]
488 fn test_pattern_match_multi_segment() {
489 assert!(pattern_match("/*/volume", "/audio/volume"));
490 assert!(!pattern_match("/*/volume", "/audio/pan"));
491 }
492
493 #[test]
494 fn test_time_tag_immediate() {
495 let t = TimeTag::immediate();
496 assert_eq!(t.seconds, 0);
497 assert_eq!(t.fractional, 0);
498 }
499
500 #[test]
501 fn test_encode_empty_string() {
502 let msg = OscMessage {
503 addr: "/".into(),
504 args: vec![OscType::String(String::new())],
505 };
506 let packet = OscPacket::Message(msg);
507 let bytes = encode(&packet).unwrap();
508 let decoded = decode(&bytes).unwrap();
509 assert_eq!(packet, decoded);
510 }
511
512 #[test]
513 fn test_known_osc_packet() {
514 let bytes = b"/test\0\0\0,i\0\0\0\0\0\x2a";
515 let decoded = decode(bytes).unwrap();
516 match decoded {
517 OscPacket::Message(msg) => {
518 assert_eq!(msg.addr, "/test");
519 assert_eq!(msg.args.len(), 1);
520 assert_eq!(msg.args[0], OscType::Int(42));
521 }
522 _ => panic!("expected message"),
523 }
524 }
525
526 #[test]
527 fn test_empty_addr_no_args() {
528 let bytes = b"/\0\0\0";
529 let decoded = decode(bytes).unwrap();
530 match decoded {
531 OscPacket::Message(msg) => {
532 assert_eq!(msg.addr, "/");
533 assert!(msg.args.is_empty());
534 }
535 _ => panic!("expected message"),
536 }
537 }
538}