oxihuman_export/
osc_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub enum OscArg {
11 Int(i32),
12 Float(f32),
13 String(String),
14 Blob(Vec<u8>),
15}
16
17#[allow(dead_code)]
19#[derive(Debug, Clone)]
20pub struct OscMessage {
21 pub address: String,
22 pub args: Vec<OscArg>,
23}
24
25#[allow(dead_code)]
27pub struct OscBundle {
28 pub time_tag: u64,
29 pub messages: Vec<OscMessage>,
30}
31
32fn pad4(n: usize) -> usize {
34 (4 - n % 4) % 4
35}
36
37fn encode_osc_string(s: &str) -> Vec<u8> {
39 let mut out = s.as_bytes().to_vec();
40 out.push(0);
41 let pad = pad4(out.len());
42 out.extend(std::iter::repeat_n(0, pad));
43 out
44}
45
46#[allow(dead_code)]
48pub fn serialize_osc_message(msg: &OscMessage) -> Vec<u8> {
49 let mut out = Vec::new();
50 out.extend_from_slice(&encode_osc_string(&msg.address));
51 let type_tag = {
52 let mut t = String::from(",");
53 for arg in &msg.args {
54 t.push(match arg {
55 OscArg::Int(_) => 'i',
56 OscArg::Float(_) => 'f',
57 OscArg::String(_) => 's',
58 OscArg::Blob(_) => 'b',
59 });
60 }
61 t
62 };
63 out.extend_from_slice(&encode_osc_string(&type_tag));
64 for arg in &msg.args {
65 match arg {
66 OscArg::Int(i) => out.extend_from_slice(&i.to_be_bytes()),
67 OscArg::Float(f) => out.extend_from_slice(&f.to_bits().to_be_bytes()),
68 OscArg::String(s) => out.extend_from_slice(&encode_osc_string(s)),
69 OscArg::Blob(b) => {
70 out.extend_from_slice(&(b.len() as u32).to_be_bytes());
71 out.extend_from_slice(b);
72 let pad = pad4(b.len());
73 out.extend(std::iter::repeat_n(0, pad));
74 }
75 }
76 }
77 out
78}
79
80#[allow(dead_code)]
82pub fn serialize_osc_bundle(bundle: &OscBundle) -> Vec<u8> {
83 let mut out = Vec::new();
84 out.extend_from_slice(&encode_osc_string("#bundle"));
85 out.extend_from_slice(&bundle.time_tag.to_be_bytes());
86 for msg in &bundle.messages {
87 let msg_bytes = serialize_osc_message(msg);
88 out.extend_from_slice(&(msg_bytes.len() as u32).to_be_bytes());
89 out.extend_from_slice(&msg_bytes);
90 }
91 out
92}
93
94#[allow(dead_code)]
96pub fn new_osc_message(address: &str) -> OscMessage {
97 OscMessage {
98 address: address.to_string(),
99 args: Vec::new(),
100 }
101}
102
103#[allow(dead_code)]
105pub fn osc_add_int(msg: &mut OscMessage, val: i32) {
106 msg.args.push(OscArg::Int(val));
107}
108
109#[allow(dead_code)]
111pub fn osc_add_float(msg: &mut OscMessage, val: f32) {
112 msg.args.push(OscArg::Float(val));
113}
114
115#[allow(dead_code)]
117pub fn osc_add_string(msg: &mut OscMessage, val: &str) {
118 msg.args.push(OscArg::String(val.to_string()));
119}
120
121#[allow(dead_code)]
123pub fn osc_arg_count(msg: &OscMessage) -> usize {
124 msg.args.len()
125}
126
127#[allow(dead_code)]
129pub fn osc_message_size(msg: &OscMessage) -> usize {
130 serialize_osc_message(msg).len()
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn serialize_simple_address_aligned() {
139 let msg = new_osc_message("/foo");
140 let bytes = serialize_osc_message(&msg);
141 assert_eq!(bytes.len() % 4, 0);
142 }
143
144 #[test]
145 fn serialize_int_arg() {
146 let mut msg = new_osc_message("/test");
147 osc_add_int(&mut msg, 42);
148 let bytes = serialize_osc_message(&msg);
149 assert!(bytes.len() >= 8);
150 assert_eq!(bytes.len() % 4, 0);
151 }
152
153 #[test]
154 fn serialize_float_arg() {
155 let mut msg = new_osc_message("/level");
156 osc_add_float(&mut msg, 0.5);
157 let bytes = serialize_osc_message(&msg);
158 assert!(bytes.len() >= 8);
159 }
160
161 #[test]
162 fn serialize_string_arg() {
163 let mut msg = new_osc_message("/name");
164 osc_add_string(&mut msg, "hello");
165 let bytes = serialize_osc_message(&msg);
166 assert_eq!(bytes.len() % 4, 0);
167 }
168
169 #[test]
170 fn osc_arg_count_correct() {
171 let mut msg = new_osc_message("/multi");
172 osc_add_int(&mut msg, 1);
173 osc_add_float(&mut msg, 2.0);
174 assert_eq!(osc_arg_count(&msg), 2);
175 }
176
177 #[test]
178 fn osc_message_size_multiple_of_4() {
179 let mut msg = new_osc_message("/data");
180 osc_add_float(&mut msg, 1.0);
181 osc_add_int(&mut msg, 2);
182 let size = osc_message_size(&msg);
183 assert_eq!(size % 4, 0);
184 }
185
186 #[test]
187 fn bundle_starts_with_hash_bundle() {
188 let bundle = OscBundle {
189 time_tag: 1,
190 messages: vec![],
191 };
192 let bytes = serialize_osc_bundle(&bundle);
193 assert_eq!(&bytes[0..7], b"#bundle");
194 }
195
196 #[test]
197 fn bundle_contains_message() {
198 let msg = new_osc_message("/ping");
199 let bundle = OscBundle {
200 time_tag: 0,
201 messages: vec![msg],
202 };
203 let bytes = serialize_osc_bundle(&bundle);
204 assert!(bytes.len() > 16);
205 }
206
207 #[test]
208 fn pad4_values() {
209 assert_eq!(pad4(0), 0);
210 assert_eq!(pad4(1), 3);
211 assert_eq!(pad4(4), 0);
212 assert_eq!(pad4(5), 3);
213 }
214
215 #[test]
216 fn encode_osc_string_padded() {
217 let s = encode_osc_string("hi");
218 assert_eq!(s.len() % 4, 0);
219 }
220}