1#![no_std]
28#![allow(dead_code)]
29
30#[cfg(feature = "alloc")]
31extern crate alloc;
32
33#[cfg(feature = "alloc")]
34use alloc::{string::String, vec::Vec};
35
36pub const MAGIC: u8 = 0x53; pub const VERSION: u8 = 1;
45
46pub mod msg {
48 pub const HELLO: u8 = 0x01;
49 pub const WELCOME: u8 = 0x02;
50 pub const ANNOUNCE: u8 = 0x03;
51 pub const SUBSCRIBE: u8 = 0x10;
52 pub const UNSUBSCRIBE: u8 = 0x11;
53 pub const PUBLISH: u8 = 0x20;
54 pub const SET: u8 = 0x21;
55 pub const GET: u8 = 0x22;
56 pub const SNAPSHOT: u8 = 0x23;
57 pub const BUNDLE: u8 = 0x30;
58 pub const SYNC: u8 = 0x40;
59 pub const PING: u8 = 0x41;
60 pub const PONG: u8 = 0x42;
61 pub const ACK: u8 = 0x50;
62 pub const ERROR: u8 = 0x51;
63 pub const QUERY: u8 = 0x60;
64 pub const RESULT: u8 = 0x61;
65}
66
67pub mod val {
69 pub const NULL: u8 = 0x00;
70 pub const BOOL: u8 = 0x01;
71 pub const I32: u8 = 0x04;
72 pub const I64: u8 = 0x05;
73 pub const F32: u8 = 0x06;
74 pub const F64: u8 = 0x07;
75 pub const STRING: u8 = 0x08;
76 pub const BYTES: u8 = 0x09;
77 pub const ARRAY: u8 = 0x0A;
78 pub const MAP: u8 = 0x0B;
79}
80
81pub const HEADER_SIZE: usize = 4;
87
88pub const MAX_PAYLOAD: usize = 1024;
90
91pub fn decode_header(buf: &[u8]) -> Option<(u8, usize)> {
93 if buf.len() < HEADER_SIZE || buf[0] != MAGIC {
94 return None;
95 }
96 let flags = buf[1];
97 let len = u16::from_be_bytes([buf[2], buf[3]]) as usize;
98 Some((flags, len))
99}
100
101pub const FLAGS_BINARY: u8 = 0x01; pub fn encode_header(buf: &mut [u8], _flags: u8, payload_len: usize) -> usize {
107 if buf.len() < HEADER_SIZE {
108 return 0;
109 }
110 buf[0] = MAGIC;
111 buf[1] = FLAGS_BINARY; let len = (payload_len as u16).to_be_bytes();
113 buf[2] = len[0];
114 buf[3] = len[1];
115 HEADER_SIZE
116}
117
118#[derive(Clone, Copy, Debug, PartialEq)]
124pub enum Value {
125 Null,
126 Bool(bool),
127 Int(i64),
128 Float(f64),
129}
130
131#[cfg(feature = "alloc")]
133#[derive(Clone, Debug, PartialEq)]
134pub enum ValueExt {
135 Null,
136 Bool(bool),
137 Int(i64),
138 Float(f64),
139 String(String),
140 Bytes(Vec<u8>),
141}
142
143#[cfg(feature = "alloc")]
144impl ValueExt {
145 pub fn as_int(&self) -> Option<i64> {
146 match self {
147 ValueExt::Int(i) => Some(*i),
148 ValueExt::Float(f) => Some(*f as i64),
149 _ => None,
150 }
151 }
152
153 pub fn as_float(&self) -> Option<f64> {
154 match self {
155 ValueExt::Float(f) => Some(*f),
156 ValueExt::Int(i) => Some(*i as f64),
157 _ => None,
158 }
159 }
160
161 pub fn as_bool(&self) -> Option<bool> {
162 match self {
163 ValueExt::Bool(b) => Some(*b),
164 _ => None,
165 }
166 }
167
168 pub fn as_str(&self) -> Option<&str> {
169 match self {
170 ValueExt::String(s) => Some(s.as_str()),
171 _ => None,
172 }
173 }
174
175 pub fn as_bytes(&self) -> Option<&[u8]> {
176 match self {
177 ValueExt::Bytes(b) => Some(b.as_slice()),
178 _ => None,
179 }
180 }
181
182 pub fn from_value(v: Value) -> Self {
184 match v {
185 Value::Null => ValueExt::Null,
186 Value::Bool(b) => ValueExt::Bool(b),
187 Value::Int(i) => ValueExt::Int(i),
188 Value::Float(f) => ValueExt::Float(f),
189 }
190 }
191
192 pub fn to_value(&self) -> Option<Value> {
194 match self {
195 ValueExt::Null => Some(Value::Null),
196 ValueExt::Bool(b) => Some(Value::Bool(*b)),
197 ValueExt::Int(i) => Some(Value::Int(*i)),
198 ValueExt::Float(f) => Some(Value::Float(*f)),
199 ValueExt::String(_) | ValueExt::Bytes(_) => None,
200 }
201 }
202}
203
204impl Value {
205 pub fn as_int(&self) -> Option<i64> {
206 match self {
207 Value::Int(i) => Some(*i),
208 Value::Float(f) => Some(*f as i64),
209 _ => None,
210 }
211 }
212
213 pub fn as_float(&self) -> Option<f64> {
214 match self {
215 Value::Float(f) => Some(*f),
216 Value::Int(i) => Some(*i as f64),
217 _ => None,
218 }
219 }
220
221 pub fn as_bool(&self) -> Option<bool> {
222 match self {
223 Value::Bool(b) => Some(*b),
224 _ => None,
225 }
226 }
227}
228
229pub fn encode_value(buf: &mut [u8], value: &Value) -> usize {
231 match value {
232 Value::Null => {
233 if buf.is_empty() {
234 return 0;
235 }
236 buf[0] = val::NULL;
237 1
238 }
239 Value::Bool(b) => {
240 if buf.len() < 2 {
241 return 0;
242 }
243 buf[0] = val::BOOL;
244 buf[1] = if *b { 1 } else { 0 };
245 2
246 }
247 Value::Int(i) => {
248 if buf.len() < 9 {
249 return 0;
250 }
251 buf[0] = val::I64;
252 buf[1..9].copy_from_slice(&i.to_be_bytes());
253 9
254 }
255 Value::Float(f) => {
256 if buf.len() < 9 {
257 return 0;
258 }
259 buf[0] = val::F64;
260 buf[1..9].copy_from_slice(&f.to_be_bytes());
261 9
262 }
263 }
264}
265
266pub fn decode_value(buf: &[u8]) -> Option<(Value, usize)> {
269 if buf.is_empty() {
270 return None;
271 }
272 match buf[0] {
273 val::NULL => Some((Value::Null, 1)),
274 val::BOOL => {
275 if buf.len() < 2 {
276 return None;
277 }
278 Some((Value::Bool(buf[1] != 0), 2))
279 }
280 val::I32 => {
281 if buf.len() < 5 {
282 return None;
283 }
284 let i = i32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
285 Some((Value::Int(i as i64), 5))
286 }
287 val::I64 => {
288 if buf.len() < 9 {
289 return None;
290 }
291 let i = i64::from_be_bytes([
292 buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
293 ]);
294 Some((Value::Int(i), 9))
295 }
296 val::F32 => {
297 if buf.len() < 5 {
298 return None;
299 }
300 let f = f32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
301 Some((Value::Float(f as f64), 5))
302 }
303 val::F64 => {
304 if buf.len() < 9 {
305 return None;
306 }
307 let f = f64::from_be_bytes([
308 buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
309 ]);
310 Some((Value::Float(f), 9))
311 }
312 val::STRING | val::BYTES => {
314 if buf.len() < 3 {
316 return None;
317 }
318 let len = u16::from_be_bytes([buf[1], buf[2]]) as usize;
319 if buf.len() < 3 + len {
320 return None;
321 }
322 None }
324 val::ARRAY | val::MAP => None,
326 _ => None,
327 }
328}
329
330#[cfg(feature = "alloc")]
332pub fn decode_value_ext(buf: &[u8]) -> Option<(ValueExt, usize)> {
333 if buf.is_empty() {
334 return None;
335 }
336 match buf[0] {
337 val::NULL => Some((ValueExt::Null, 1)),
338 val::BOOL => {
339 if buf.len() < 2 {
340 return None;
341 }
342 Some((ValueExt::Bool(buf[1] != 0), 2))
343 }
344 val::I32 => {
345 if buf.len() < 5 {
346 return None;
347 }
348 let i = i32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
349 Some((ValueExt::Int(i as i64), 5))
350 }
351 val::I64 => {
352 if buf.len() < 9 {
353 return None;
354 }
355 let i = i64::from_be_bytes([
356 buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
357 ]);
358 Some((ValueExt::Int(i), 9))
359 }
360 val::F32 => {
361 if buf.len() < 5 {
362 return None;
363 }
364 let f = f32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
365 Some((ValueExt::Float(f as f64), 5))
366 }
367 val::F64 => {
368 if buf.len() < 9 {
369 return None;
370 }
371 let f = f64::from_be_bytes([
372 buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
373 ]);
374 Some((ValueExt::Float(f), 9))
375 }
376 val::STRING => {
377 if buf.len() < 3 {
378 return None;
379 }
380 let len = u16::from_be_bytes([buf[1], buf[2]]) as usize;
381 if buf.len() < 3 + len {
382 return None;
383 }
384 let s = core::str::from_utf8(&buf[3..3 + len]).ok()?;
385 Some((ValueExt::String(String::from(s)), 3 + len))
386 }
387 val::BYTES => {
388 if buf.len() < 3 {
389 return None;
390 }
391 let len = u16::from_be_bytes([buf[1], buf[2]]) as usize;
392 if buf.len() < 3 + len {
393 return None;
394 }
395 let bytes = buf[3..3 + len].to_vec();
396 Some((ValueExt::Bytes(bytes), 3 + len))
397 }
398 _ => None,
399 }
400}
401
402#[cfg(feature = "alloc")]
404pub fn encode_value_ext(buf: &mut [u8], value: &ValueExt) -> usize {
405 match value {
406 ValueExt::Null => {
407 if buf.is_empty() {
408 return 0;
409 }
410 buf[0] = val::NULL;
411 1
412 }
413 ValueExt::Bool(b) => {
414 if buf.len() < 2 {
415 return 0;
416 }
417 buf[0] = val::BOOL;
418 buf[1] = if *b { 1 } else { 0 };
419 2
420 }
421 ValueExt::Int(i) => {
422 if buf.len() < 9 {
423 return 0;
424 }
425 buf[0] = val::I64;
426 buf[1..9].copy_from_slice(&i.to_be_bytes());
427 9
428 }
429 ValueExt::Float(f) => {
430 if buf.len() < 9 {
431 return 0;
432 }
433 buf[0] = val::F64;
434 buf[1..9].copy_from_slice(&f.to_be_bytes());
435 9
436 }
437 ValueExt::String(s) => {
438 let bytes = s.as_bytes();
439 if buf.len() < 3 + bytes.len() {
440 return 0;
441 }
442 buf[0] = val::STRING;
443 let len = (bytes.len() as u16).to_be_bytes();
444 buf[1] = len[0];
445 buf[2] = len[1];
446 buf[3..3 + bytes.len()].copy_from_slice(bytes);
447 3 + bytes.len()
448 }
449 ValueExt::Bytes(b) => {
450 if buf.len() < 3 + b.len() {
451 return 0;
452 }
453 buf[0] = val::BYTES;
454 let len = (b.len() as u16).to_be_bytes();
455 buf[1] = len[0];
456 buf[2] = len[1];
457 buf[3..3 + b.len()].copy_from_slice(b);
458 3 + b.len()
459 }
460 }
461}
462
463pub fn encode_string(buf: &mut [u8], s: &str) -> usize {
469 let bytes = s.as_bytes();
470 if buf.len() < 2 + bytes.len() {
471 return 0;
472 }
473 let len = (bytes.len() as u16).to_be_bytes();
474 buf[0] = len[0];
475 buf[1] = len[1];
476 buf[2..2 + bytes.len()].copy_from_slice(bytes);
477 2 + bytes.len()
478}
479
480pub fn decode_string(buf: &[u8]) -> Option<(&str, usize)> {
482 if buf.len() < 2 {
483 return None;
484 }
485 let len = u16::from_be_bytes([buf[0], buf[1]]) as usize;
486 if buf.len() < 2 + len {
487 return None;
488 }
489 let s = core::str::from_utf8(&buf[2..2 + len]).ok()?;
490 Some((s, 2 + len))
491}
492
493fn value_type_code(value: &Value) -> u8 {
499 match value {
500 Value::Null => val::NULL,
501 Value::Bool(_) => val::BOOL,
502 Value::Int(_) => val::I64,
503 Value::Float(_) => val::F64,
504 }
505}
506
507fn encode_value_data(buf: &mut [u8], value: &Value) -> usize {
509 match value {
510 Value::Null => 0,
511 Value::Bool(b) => {
512 if buf.is_empty() {
513 return 0;
514 }
515 buf[0] = if *b { 1 } else { 0 };
516 1
517 }
518 Value::Int(i) => {
519 if buf.len() < 8 {
520 return 0;
521 }
522 buf[..8].copy_from_slice(&i.to_be_bytes());
523 8
524 }
525 Value::Float(f) => {
526 if buf.len() < 8 {
527 return 0;
528 }
529 buf[..8].copy_from_slice(&f.to_be_bytes());
530 8
531 }
532 }
533}
534
535pub fn encode_set(buf: &mut [u8], address: &str, value: &Value) -> usize {
539 if buf.len() < 2 {
540 return 0;
541 }
542
543 buf[0] = msg::SET;
545
546 let vtype = value_type_code(value);
548 buf[1] = vtype & 0x0F;
549
550 let mut offset = 2;
551
552 offset += encode_string(&mut buf[offset..], address);
554
555 offset += encode_value_data(&mut buf[offset..], value);
557
558 offset
559}
560
561pub fn encode_set_frame(buf: &mut [u8], address: &str, value: &Value) -> usize {
563 let header_size = HEADER_SIZE;
564 let payload_start = header_size;
565
566 let payload_len = encode_set(&mut buf[payload_start..], address, value);
567 if payload_len == 0 {
568 return 0;
569 }
570
571 encode_header(buf, 0, payload_len);
572 header_size + payload_len
573}
574
575pub fn encode_subscribe(buf: &mut [u8], pattern: &str) -> usize {
578 if buf.is_empty() {
579 return 0;
580 }
581 buf[0] = msg::SUBSCRIBE;
582 let mut offset = 1;
583
584 if buf.len() < offset + 4 {
586 return 0;
587 }
588 buf[offset..offset + 4].copy_from_slice(&0u32.to_be_bytes());
589 offset += 4;
590
591 offset += encode_string(&mut buf[offset..], pattern);
593
594 if buf.len() > offset {
596 buf[offset] = 0xFF;
597 offset += 1;
598 }
599
600 if buf.len() > offset {
602 buf[offset] = 0;
603 offset += 1;
604 }
605
606 offset
607}
608
609pub fn encode_subscribe_frame(buf: &mut [u8], pattern: &str) -> usize {
611 let header_size = HEADER_SIZE;
612 let payload_len = encode_subscribe(&mut buf[header_size..], pattern);
613 if payload_len == 0 {
614 return 0;
615 }
616 encode_header(buf, 0, payload_len);
617 header_size + payload_len
618}
619
620pub fn encode_hello(buf: &mut [u8], name: &str) -> usize {
623 if buf.len() < 6 {
624 return 0;
625 }
626
627 buf[0] = msg::HELLO;
629
630 buf[1] = VERSION;
632
633 buf[2] = 0xF8; let mut offset = 3;
637
638 offset += encode_string(&mut buf[offset..], name);
640
641 if buf.len() >= offset + 2 {
643 buf[offset] = 0;
644 buf[offset + 1] = 0;
645 offset += 2;
646 }
647
648 offset
649}
650
651pub fn encode_hello_frame(buf: &mut [u8], name: &str) -> usize {
653 let header_size = HEADER_SIZE;
654 let payload_len = encode_hello(&mut buf[header_size..], name);
655 if payload_len == 0 {
656 return 0;
657 }
658 encode_header(buf, 0, payload_len);
659 header_size + payload_len
660}
661
662pub fn encode_ping_frame(buf: &mut [u8]) -> usize {
664 if buf.len() < HEADER_SIZE + 1 {
665 return 0;
666 }
667 encode_header(buf, 0, 1);
668 buf[HEADER_SIZE] = msg::PING;
669 HEADER_SIZE + 1
670}
671
672pub fn encode_pong_frame(buf: &mut [u8]) -> usize {
674 if buf.len() < HEADER_SIZE + 1 {
675 return 0;
676 }
677 encode_header(buf, 0, 1);
678 buf[HEADER_SIZE] = msg::PONG;
679 HEADER_SIZE + 1
680}
681
682#[derive(Debug)]
688pub enum Message<'a> {
689 Hello { name: &'a str, version: u8 },
690 Welcome { session: &'a str },
691 Announce { signal_count: u16 },
692 Set { address: &'a str, value: Value },
693 Subscribe { id: u32, pattern: &'a str },
694 Unsubscribe { id: u32 },
695 Publish { address: &'a str },
696 Bundle { message_count: u16 },
697 Sync { timestamp: u64 },
698 Ping,
699 Pong,
700 Query { pattern: &'a str },
701 Result { signal_count: u16 },
702 Error { code: u16, message: &'a str },
703 Unknown(u8),
704}
705
706pub fn decode_message(payload: &[u8]) -> Option<Message<'_>> {
708 if payload.is_empty() {
709 return None;
710 }
711
712 let msg_type = payload[0];
713 let data = &payload[1..];
714
715 match msg_type {
716 msg::HELLO => {
717 if data.len() < 2 {
719 return None;
720 }
721 let version = data[0];
722 let _features = data[1];
723 let (name, _) = decode_string(&data[2..])?;
724 Some(Message::Hello { name, version })
725 }
726 msg::WELCOME => {
727 if data.len() < 10 {
729 return None;
730 }
731 let _version = data[0];
732 let _features = data[1];
733 let _time = u64::from_be_bytes([
734 data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9],
735 ]);
736 let (session, _) = decode_string(&data[10..])?;
737 Some(Message::Welcome { session })
738 }
739 msg::SET => {
740 if data.is_empty() {
743 return None;
744 }
745 let flags = data[0];
746 let vtype = flags & 0x0F;
747 let _has_rev = (flags & 0x80) != 0;
748
749 let (address, offset) = decode_string(&data[1..])?;
750 let value_data = &data[1 + offset..];
751
752 let value = match vtype {
753 val::NULL => Value::Null,
754 val::BOOL => {
755 if value_data.is_empty() {
756 return None;
757 }
758 Value::Bool(value_data[0] != 0)
759 }
760 val::I64 => {
761 if value_data.len() < 8 {
762 return None;
763 }
764 let i = i64::from_be_bytes([
765 value_data[0],
766 value_data[1],
767 value_data[2],
768 value_data[3],
769 value_data[4],
770 value_data[5],
771 value_data[6],
772 value_data[7],
773 ]);
774 Value::Int(i)
775 }
776 val::F64 => {
777 if value_data.len() < 8 {
778 return None;
779 }
780 let f = f64::from_be_bytes([
781 value_data[0],
782 value_data[1],
783 value_data[2],
784 value_data[3],
785 value_data[4],
786 value_data[5],
787 value_data[6],
788 value_data[7],
789 ]);
790 Value::Float(f)
791 }
792 _ => return None, };
794
795 Some(Message::Set { address, value })
796 }
797 msg::SUBSCRIBE => {
798 if data.len() < 4 {
800 return None;
801 }
802 let id = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
803 let (pattern, _) = decode_string(&data[4..])?;
804 Some(Message::Subscribe { id, pattern })
805 }
806 msg::UNSUBSCRIBE => {
807 if data.len() < 4 {
809 return None;
810 }
811 let id = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
812 Some(Message::Unsubscribe { id })
813 }
814 msg::PING => Some(Message::Ping),
815 msg::PONG => Some(Message::Pong),
816 msg::ERROR => {
817 if data.len() < 2 {
818 return None;
819 }
820 let code = u16::from_be_bytes([data[0], data[1]]);
821 let (message, _) = decode_string(&data[2..]).unwrap_or(("", 0));
822 Some(Message::Error { code, message })
823 }
824 msg::ANNOUNCE => {
825 if data.len() < 2 {
828 return None;
829 }
830 let signal_count = u16::from_be_bytes([data[0], data[1]]);
831 Some(Message::Announce { signal_count })
832 }
833 msg::PUBLISH => {
834 if data.is_empty() {
837 return None;
838 }
839 let _flags = data[0];
840 let (address, _) = decode_string(&data[1..])?;
841 Some(Message::Publish { address })
842 }
843 msg::BUNDLE => {
844 if data.len() < 3 {
846 return None;
847 }
848 let _flags = data[0];
849 let message_count = u16::from_be_bytes([data[1], data[2]]);
850 Some(Message::Bundle { message_count })
851 }
852 msg::SYNC => {
853 if data.len() < 8 {
855 return None;
856 }
857 let timestamp = u64::from_be_bytes([
858 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
859 ]);
860 Some(Message::Sync { timestamp })
861 }
862 msg::QUERY => {
863 let (pattern, _) = decode_string(data)?;
865 Some(Message::Query { pattern })
866 }
867 msg::RESULT => {
868 if data.len() < 2 {
870 return None;
871 }
872 let signal_count = u16::from_be_bytes([data[0], data[1]]);
873 Some(Message::Result { signal_count })
874 }
875 _ => Some(Message::Unknown(msg_type)),
876 }
877}
878
879pub const MAX_CACHE_ENTRIES: usize = 32;
885
886pub const MAX_ADDRESS_LEN: usize = 64;
888
889#[derive(Clone)]
891pub struct CacheEntry {
892 address: [u8; MAX_ADDRESS_LEN],
893 address_len: u8,
894 value: Value,
895 valid: bool,
896}
897
898impl Default for CacheEntry {
899 fn default() -> Self {
900 Self {
901 address: [0; MAX_ADDRESS_LEN],
902 address_len: 0,
903 value: Value::Null,
904 valid: false,
905 }
906 }
907}
908
909impl CacheEntry {
910 fn address(&self) -> &str {
911 core::str::from_utf8(&self.address[..self.address_len as usize]).unwrap_or("")
912 }
913
914 fn set_address(&mut self, addr: &str) {
915 let bytes = addr.as_bytes();
916 let len = bytes.len().min(MAX_ADDRESS_LEN);
917 self.address[..len].copy_from_slice(&bytes[..len]);
918 self.address_len = len as u8;
919 }
920}
921
922pub struct StateCache {
924 entries: [CacheEntry; MAX_CACHE_ENTRIES],
925 count: usize,
926}
927
928impl StateCache {
929 pub const fn new() -> Self {
930 Self {
931 entries: [const {
932 CacheEntry {
933 address: [0; MAX_ADDRESS_LEN],
934 address_len: 0,
935 value: Value::Null,
936 valid: false,
937 }
938 }; MAX_CACHE_ENTRIES],
939 count: 0,
940 }
941 }
942
943 pub fn get(&self, address: &str) -> Option<Value> {
945 for entry in &self.entries[..self.count] {
946 if entry.valid && entry.address() == address {
947 return Some(entry.value);
948 }
949 }
950 None
951 }
952
953 pub fn set(&mut self, address: &str, value: Value) -> bool {
955 for entry in &mut self.entries[..self.count] {
957 if entry.valid && entry.address() == address {
958 entry.value = value;
959 return true;
960 }
961 }
962
963 if self.count < MAX_CACHE_ENTRIES {
965 self.entries[self.count].set_address(address);
966 self.entries[self.count].value = value;
967 self.entries[self.count].valid = true;
968 self.count += 1;
969 return true;
970 }
971
972 false
973 }
974
975 pub fn len(&self) -> usize {
976 self.count
977 }
978
979 pub fn is_empty(&self) -> bool {
980 self.count == 0
981 }
982
983 pub fn clear(&mut self) {
984 for entry in &mut self.entries {
985 entry.valid = false;
986 }
987 self.count = 0;
988 }
989}
990
991impl Default for StateCache {
992 fn default() -> Self {
993 Self::new()
994 }
995}
996
997#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1003pub enum ClientState {
1004 Disconnected,
1005 Connecting,
1006 Connected,
1007}
1008
1009pub const TX_BUF_SIZE: usize = 256;
1011pub const RX_BUF_SIZE: usize = 512;
1012
1013pub struct Client {
1018 pub state: ClientState,
1019 pub cache: StateCache,
1020 tx_buf: [u8; TX_BUF_SIZE],
1021 rx_buf: [u8; RX_BUF_SIZE],
1022}
1023
1024impl Client {
1025 pub const fn new() -> Self {
1026 Self {
1027 state: ClientState::Disconnected,
1028 cache: StateCache::new(),
1029 tx_buf: [0; TX_BUF_SIZE],
1030 rx_buf: [0; RX_BUF_SIZE],
1031 }
1032 }
1033
1034 pub fn prepare_hello(&mut self, name: &str) -> &[u8] {
1036 let n = encode_hello_frame(&mut self.tx_buf, name);
1037 &self.tx_buf[..n]
1038 }
1039
1040 pub fn prepare_set(&mut self, address: &str, value: Value) -> &[u8] {
1042 let n = encode_set_frame(&mut self.tx_buf, address, &value);
1043 &self.tx_buf[..n]
1044 }
1045
1046 pub fn prepare_subscribe(&mut self, pattern: &str) -> &[u8] {
1048 let n = encode_subscribe_frame(&mut self.tx_buf, pattern);
1049 &self.tx_buf[..n]
1050 }
1051
1052 pub fn prepare_ping(&mut self) -> &[u8] {
1054 let n = encode_ping_frame(&mut self.tx_buf);
1055 &self.tx_buf[..n]
1056 }
1057
1058 pub fn process<'a>(&mut self, data: &'a [u8]) -> Option<Message<'a>> {
1060 let (_, payload_len) = decode_header(data)?;
1061 let payload = &data[HEADER_SIZE..HEADER_SIZE + payload_len];
1062 let msg = decode_message(payload)?;
1063
1064 match &msg {
1065 Message::Welcome { .. } => {
1066 self.state = ClientState::Connected;
1067 }
1068 Message::Set { address, value } => {
1069 self.cache.set(address, *value);
1070 }
1071 _ => {}
1072 }
1073
1074 Some(msg)
1075 }
1076
1077 pub fn is_connected(&self) -> bool {
1078 self.state == ClientState::Connected
1079 }
1080
1081 pub fn get_cached(&self, address: &str) -> Option<Value> {
1082 self.cache.get(address)
1083 }
1084}
1085
1086impl Default for Client {
1087 fn default() -> Self {
1088 Self::new()
1089 }
1090}
1091
1092#[cfg(feature = "server")]
1097pub mod server {
1098 use super::*;
1099
1100 pub const MAX_CLIENTS: usize = 4;
1102
1103 pub const MAX_SUBS_PER_CLIENT: usize = 8;
1105
1106 pub const MAX_PATTERN_LEN: usize = 64;
1108
1109 #[derive(Clone)]
1111 pub struct Subscription {
1112 pub active: bool,
1113 pub id: u32,
1114 pub pattern: [u8; MAX_PATTERN_LEN],
1115 pub pattern_len: usize,
1116 }
1117
1118 impl Subscription {
1119 pub const fn empty() -> Self {
1120 Self {
1121 active: false,
1122 id: 0,
1123 pattern: [0; MAX_PATTERN_LEN],
1124 pattern_len: 0,
1125 }
1126 }
1127
1128 pub fn matches(&self, address: &str) -> bool {
1130 if !self.active || self.pattern_len == 0 {
1131 return false;
1132 }
1133
1134 let pattern = match core::str::from_utf8(&self.pattern[..self.pattern_len]) {
1136 Ok(s) => s,
1137 Err(_) => return false,
1138 };
1139
1140 Self::match_pattern(pattern, address)
1143 }
1144
1145 fn match_pattern(pattern: &str, address: &str) -> bool {
1146 let mut pattern_iter = pattern.split('/').filter(|s| !s.is_empty());
1149 let mut address_iter = address.split('/').filter(|s| !s.is_empty());
1150
1151 loop {
1152 match (pattern_iter.next(), address_iter.next()) {
1153 (None, None) => return true,
1154 (Some("**"), _) => {
1155 if let Some(next_pattern) = pattern_iter.next() {
1158 loop {
1160 match address_iter.next() {
1161 None => return next_pattern == "**",
1162 Some(seg) if seg == next_pattern || next_pattern == "*" => {
1163 break;
1165 }
1166 Some(_) => continue,
1167 }
1168 }
1169 } else {
1170 return true;
1172 }
1173 }
1174 (Some("*"), Some(_)) => continue,
1175 (Some(p), Some(a)) if p == a => continue,
1176 (None, Some(_)) => return false,
1177 (Some(_), None) => {
1178 return pattern_iter.all(|p| p == "**");
1180 }
1181 _ => return false,
1182 }
1183 }
1184 }
1185 }
1186
1187 pub struct Session {
1189 pub active: bool,
1190 pub id: u8,
1191 pub subscriptions: [Subscription; MAX_SUBS_PER_CLIENT],
1192 pub sub_count: u8,
1193 }
1194
1195 impl Session {
1196 pub const fn new() -> Self {
1197 Self {
1198 active: false,
1199 id: 0,
1200 subscriptions: [const { Subscription::empty() }; MAX_SUBS_PER_CLIENT],
1201 sub_count: 0,
1202 }
1203 }
1204
1205 pub fn subscribe(&mut self, id: u32, pattern: &str) -> bool {
1207 if self.sub_count as usize >= MAX_SUBS_PER_CLIENT {
1208 return false;
1209 }
1210 if pattern.len() > MAX_PATTERN_LEN {
1211 return false;
1212 }
1213
1214 for sub in &mut self.subscriptions {
1216 if !sub.active {
1217 sub.active = true;
1218 sub.id = id;
1219 sub.pattern[..pattern.len()].copy_from_slice(pattern.as_bytes());
1220 sub.pattern_len = pattern.len();
1221 self.sub_count += 1;
1222 return true;
1223 }
1224 }
1225 false
1226 }
1227
1228 pub fn unsubscribe(&mut self, id: u32) -> bool {
1230 for sub in &mut self.subscriptions {
1231 if sub.active && sub.id == id {
1232 sub.active = false;
1233 sub.pattern_len = 0;
1234 self.sub_count = self.sub_count.saturating_sub(1);
1235 return true;
1236 }
1237 }
1238 false
1239 }
1240
1241 pub fn has_match(&self, address: &str) -> bool {
1243 self.subscriptions.iter().any(|s| s.matches(address))
1244 }
1245 }
1246
1247 pub struct BroadcastList {
1249 pub clients: [bool; MAX_CLIENTS],
1250 pub count: u8,
1251 }
1252
1253 impl BroadcastList {
1254 pub const fn empty() -> Self {
1255 Self {
1256 clients: [false; MAX_CLIENTS],
1257 count: 0,
1258 }
1259 }
1260 }
1261
1262 pub struct MiniRouter {
1266 pub state: StateCache,
1267 sessions: [Session; MAX_CLIENTS],
1268 session_count: u8,
1269 tx_buf: [u8; TX_BUF_SIZE],
1270 }
1271
1272 impl MiniRouter {
1273 pub const fn new() -> Self {
1274 Self {
1275 state: StateCache::new(),
1276 sessions: [const { Session::new() }; MAX_CLIENTS],
1277 session_count: 0,
1278 tx_buf: [0; TX_BUF_SIZE],
1279 }
1280 }
1281
1282 pub fn process(&mut self, client_id: u8, data: &[u8]) -> Option<&[u8]> {
1286 let (_, payload_len) = decode_header(data)?;
1287 let payload = &data[HEADER_SIZE..HEADER_SIZE + payload_len];
1288 let msg = decode_message(payload)?;
1289
1290 match msg {
1291 Message::Hello { name, .. } => {
1292 self.create_session(client_id);
1293 Some(self.prepare_welcome(client_id))
1294 }
1295 Message::Subscribe { id, pattern } => {
1296 self.handle_subscribe(client_id, id, pattern);
1297 None }
1299 Message::Unsubscribe { id } => {
1300 self.handle_unsubscribe(client_id, id);
1301 None
1302 }
1303 Message::Set { address, value } => {
1304 self.state.set(address, value);
1305 None }
1307 Message::Ping => Some(self.prepare_pong()),
1308 _ => None,
1309 }
1310 }
1311
1312 pub fn get_broadcast_targets(&self, address: &str, sender_id: u8) -> BroadcastList {
1316 let mut result = BroadcastList::empty();
1317
1318 for (i, session) in self.sessions.iter().enumerate() {
1319 if session.active && i as u8 != sender_id && session.has_match(address) {
1321 result.clients[i] = true;
1322 result.count += 1;
1323 }
1324 }
1325
1326 result
1327 }
1328
1329 pub fn prepare_broadcast(&mut self, address: &str, value: Value) -> &[u8] {
1333 let n = encode_set_frame(&mut self.tx_buf, address, &value);
1334 &self.tx_buf[..n]
1335 }
1336
1337 fn handle_subscribe(&mut self, client_id: u8, id: u32, pattern: &str) {
1338 if let Some(session) = self.sessions.get_mut(client_id as usize) {
1339 if session.active {
1340 session.subscribe(id, pattern);
1341 }
1342 }
1343 }
1344
1345 fn handle_unsubscribe(&mut self, client_id: u8, id: u32) {
1346 if let Some(session) = self.sessions.get_mut(client_id as usize) {
1347 if session.active {
1348 session.unsubscribe(id);
1349 }
1350 }
1351 }
1352
1353 fn create_session(&mut self, client_id: u8) {
1354 if (client_id as usize) < MAX_CLIENTS {
1355 self.sessions[client_id as usize] = Session {
1356 active: true,
1357 id: client_id,
1358 subscriptions: [const { Subscription::empty() }; MAX_SUBS_PER_CLIENT],
1359 sub_count: 0,
1360 };
1361 self.session_count += 1;
1362 }
1363 }
1364
1365 pub fn disconnect(&mut self, client_id: u8) {
1367 if let Some(session) = self.sessions.get_mut(client_id as usize) {
1368 if session.active {
1369 session.active = false;
1370 session.sub_count = 0;
1371 self.session_count = self.session_count.saturating_sub(1);
1372 }
1373 }
1374 }
1375
1376 fn prepare_welcome(&mut self, _client_id: u8) -> &[u8] {
1377 let payload_start = HEADER_SIZE;
1378 let mut offset = payload_start;
1379
1380 self.tx_buf[offset] = msg::WELCOME;
1381 offset += 1;
1382
1383 self.tx_buf[offset] = VERSION;
1384 offset += 1;
1385
1386 self.tx_buf[offset] = 0xF8; offset += 1;
1388
1389 self.tx_buf[offset..offset + 8].copy_from_slice(&0u64.to_be_bytes());
1390 offset += 8;
1391
1392 offset += encode_string(&mut self.tx_buf[offset..], "embedded");
1393 offset += encode_string(&mut self.tx_buf[offset..], "MiniRouter");
1394
1395 let payload_len = offset - payload_start;
1396 encode_header(&mut self.tx_buf, 0, payload_len);
1397
1398 &self.tx_buf[..offset]
1399 }
1400
1401 fn prepare_pong(&mut self) -> &[u8] {
1402 let n = encode_pong_frame(&mut self.tx_buf);
1403 &self.tx_buf[..n]
1404 }
1405
1406 pub fn get(&self, address: &str) -> Option<Value> {
1407 self.state.get(address)
1408 }
1409
1410 pub fn set(&mut self, address: &str, value: Value) {
1411 self.state.set(address, value);
1412 }
1413
1414 pub fn session_count(&self) -> u8 {
1416 self.session_count
1417 }
1418
1419 pub fn session_mut(&mut self, client_id: u8) -> Option<&mut Session> {
1421 self.sessions.get_mut(client_id as usize)
1422 }
1423 }
1424
1425 impl Default for MiniRouter {
1426 fn default() -> Self {
1427 Self::new()
1428 }
1429 }
1430}
1431
1432#[cfg(test)]
1437mod tests {
1438 use super::*;
1439
1440 #[test]
1441 fn test_encode_decode_value() {
1442 let mut buf = [0u8; 16];
1443
1444 let n = encode_value(&mut buf, &Value::Float(1.25));
1446 assert_eq!(n, 9);
1447 let (v, consumed) = decode_value(&buf).unwrap();
1448 assert_eq!(consumed, 9);
1449 assert!((v.as_float().unwrap() - 1.25).abs() < 0.001);
1450
1451 let n = encode_value(&mut buf, &Value::Int(-42));
1453 let (v, _) = decode_value(&buf).unwrap();
1454 assert_eq!(v.as_int(), Some(-42));
1455 }
1456
1457 #[test]
1458 fn test_encode_decode_set() {
1459 let mut buf = [0u8; 64];
1460 let n = encode_set_frame(&mut buf, "/test/value", &Value::Float(1.5));
1461 assert!(n > HEADER_SIZE);
1462
1463 let (_, payload_len) = decode_header(&buf).unwrap();
1464 let payload = &buf[HEADER_SIZE..HEADER_SIZE + payload_len];
1465 let msg = decode_message(payload).unwrap();
1466
1467 match msg {
1468 Message::Set { address, value } => {
1469 assert_eq!(address, "/test/value");
1470 assert!((value.as_float().unwrap() - 1.5).abs() < 0.001);
1471 }
1472 _ => panic!("Expected Set message"),
1473 }
1474 }
1475
1476 #[test]
1477 fn test_client_flow() {
1478 let mut client = Client::new();
1479 assert_eq!(client.state, ClientState::Disconnected);
1480
1481 let hello = client.prepare_hello("ESP32");
1483 assert!(hello.len() > HEADER_SIZE);
1484
1485 let mut welcome_buf = [0u8; 64];
1487 let payload_start = HEADER_SIZE;
1488 let mut offset = payload_start;
1489
1490 welcome_buf[offset] = msg::WELCOME;
1492 offset += 1;
1493
1494 welcome_buf[offset] = VERSION;
1496 offset += 1;
1497
1498 welcome_buf[offset] = 0xF8;
1500 offset += 1;
1501
1502 welcome_buf[offset..offset + 8].copy_from_slice(&0u64.to_be_bytes());
1504 offset += 8;
1505
1506 offset += encode_string(&mut welcome_buf[offset..], "session123");
1508
1509 offset += encode_string(&mut welcome_buf[offset..], "TestRouter");
1511
1512 encode_header(&mut welcome_buf, 0, offset - payload_start);
1513
1514 client.process(&welcome_buf[..offset]);
1515 assert_eq!(client.state, ClientState::Connected);
1516 }
1517
1518 #[test]
1519 fn test_state_cache() {
1520 let mut cache = StateCache::new();
1521
1522 cache.set("/sensor/temp", Value::Float(25.5));
1523 cache.set("/sensor/humidity", Value::Float(60.0));
1524
1525 assert_eq!(cache.get("/sensor/temp").unwrap().as_float(), Some(25.5));
1526 assert_eq!(
1527 cache.get("/sensor/humidity").unwrap().as_float(),
1528 Some(60.0)
1529 );
1530 assert!(cache.get("/unknown").is_none());
1531 }
1532
1533 #[test]
1534 fn test_memory_size() {
1535 let client_size = core::mem::size_of::<Client>();
1536 let cache_size = core::mem::size_of::<StateCache>();
1537
1538 assert!(
1540 client_size < 4096,
1541 "Client too large: {} bytes",
1542 client_size
1543 );
1544
1545 let total = client_size + 1024; assert!(total < 8192, "Total too large: {} bytes", total);
1548 }
1549
1550 #[cfg(feature = "server")]
1551 #[test]
1552 fn test_mini_router() {
1553 use server::MiniRouter;
1554
1555 let mut router = MiniRouter::new();
1556 router.set("/light/brightness", Value::Float(0.8));
1557
1558 assert_eq!(
1559 router.get("/light/brightness").unwrap().as_float(),
1560 Some(0.8)
1561 );
1562 }
1563
1564 #[cfg(feature = "server")]
1565 #[test]
1566 fn test_mini_router_subscriptions() {
1567 use server::{MiniRouter, Session, Subscription};
1568
1569 let mut sub = Subscription::empty();
1571 sub.active = true;
1572 sub.pattern_len = "/light/*".len();
1573 sub.pattern[..sub.pattern_len].copy_from_slice(b"/light/*");
1574
1575 assert!(sub.matches("/light/brightness"));
1576 assert!(sub.matches("/light/color"));
1577 assert!(!sub.matches("/audio/volume"));
1578 assert!(!sub.matches("/light/zone/1"));
1579
1580 sub.pattern_len = "/light/**".len();
1582 sub.pattern[..sub.pattern_len].copy_from_slice(b"/light/**");
1583 assert!(sub.matches("/light/brightness"));
1584 assert!(sub.matches("/light/zone/1/brightness"));
1585 assert!(!sub.matches("/audio/volume"));
1586
1587 let mut session = Session::new();
1589 session.active = true;
1590 assert!(session.subscribe(1, "/light/*"));
1591 assert!(session.subscribe(2, "/audio/**"));
1592 assert_eq!(session.sub_count, 2);
1593
1594 assert!(session.has_match("/light/brightness"));
1595 assert!(session.has_match("/audio/master/volume"));
1596 assert!(!session.has_match("/midi/cc/1"));
1597
1598 assert!(session.unsubscribe(1));
1600 assert_eq!(session.sub_count, 1);
1601 assert!(!session.has_match("/light/brightness"));
1602 assert!(session.has_match("/audio/master/volume"));
1603 }
1604
1605 #[cfg(feature = "server")]
1606 #[test]
1607 fn test_mini_router_broadcast() {
1608 use server::MiniRouter;
1609
1610 let mut router = MiniRouter::new();
1611
1612 {
1618 let session = router.session_mut(0).unwrap();
1619 session.active = true;
1620 session.id = 0;
1621 session.subscribe(1, "/light/**");
1622 }
1623
1624 {
1625 let session = router.session_mut(1).unwrap();
1626 session.active = true;
1627 session.id = 1;
1628 session.subscribe(1, "/audio/**");
1629 }
1630
1631 router.set("/light/brightness", Value::Float(0.75));
1633
1634 let targets = router.get_broadcast_targets("/light/brightness", 2);
1636 assert_eq!(targets.count, 1);
1637 assert!(targets.clients[0]); assert!(!targets.clients[1]); let targets = router.get_broadcast_targets("/audio/volume", 2);
1642 assert_eq!(targets.count, 1);
1643 assert!(!targets.clients[0]); assert!(targets.clients[1]); let targets = router.get_broadcast_targets("/light/brightness", 0);
1648 assert_eq!(targets.count, 0); }
1650
1651 #[test]
1652 fn test_new_message_types() {
1653 let announce_payload = [msg::ANNOUNCE, 0x00, 0x03]; let msg = decode_message(&announce_payload).unwrap();
1656 match msg {
1657 Message::Announce { signal_count } => assert_eq!(signal_count, 3),
1658 _ => panic!("Expected Announce message"),
1659 }
1660
1661 let sync_payload = [
1663 msg::SYNC,
1664 0x00,
1665 0x00,
1666 0x01,
1667 0x90,
1668 0x9F,
1669 0x8D,
1670 0x80,
1671 0x00, ];
1673 let msg = decode_message(&sync_payload).unwrap();
1674 match msg {
1675 Message::Sync { timestamp } => assert!(timestamp > 0),
1676 _ => panic!("Expected Sync message"),
1677 }
1678
1679 let bundle_payload = [msg::BUNDLE, 0x00, 0x00, 0x05]; let msg = decode_message(&bundle_payload).unwrap();
1682 match msg {
1683 Message::Bundle { message_count } => assert_eq!(message_count, 5),
1684 _ => panic!("Expected Bundle message"),
1685 }
1686
1687 let mut query_payload = [0u8; 32];
1689 query_payload[0] = msg::QUERY;
1690 let pattern = "/test/**";
1691 let len = (pattern.len() as u16).to_be_bytes();
1692 query_payload[1] = len[0];
1693 query_payload[2] = len[1];
1694 query_payload[3..3 + pattern.len()].copy_from_slice(pattern.as_bytes());
1695
1696 let msg = decode_message(&query_payload[..3 + pattern.len()]).unwrap();
1697 match msg {
1698 Message::Query { pattern: p } => assert_eq!(p, "/test/**"),
1699 _ => panic!("Expected Query message"),
1700 }
1701
1702 let result_payload = [msg::RESULT, 0x00, 0x0A]; let msg = decode_message(&result_payload).unwrap();
1705 match msg {
1706 Message::Result { signal_count } => assert_eq!(signal_count, 10),
1707 _ => panic!("Expected Result message"),
1708 }
1709 }
1710
1711 #[cfg(feature = "alloc")]
1712 #[test]
1713 fn test_value_ext_string() {
1714 let mut buf = [0u8; 64];
1715
1716 let value = ValueExt::String("hello world".into());
1718 let n = encode_value_ext(&mut buf, &value);
1719 assert!(n > 0);
1720
1721 let (decoded, consumed) = decode_value_ext(&buf).unwrap();
1723 assert_eq!(consumed, n);
1724 assert_eq!(decoded.as_str(), Some("hello world"));
1725 }
1726
1727 #[cfg(feature = "alloc")]
1728 #[test]
1729 fn test_value_ext_bytes() {
1730 use alloc::vec;
1731
1732 let mut buf = [0u8; 64];
1733
1734 let data = vec![0x01, 0x02, 0x03, 0x04];
1736 let value = ValueExt::Bytes(data.clone());
1737 let n = encode_value_ext(&mut buf, &value);
1738 assert!(n > 0);
1739
1740 let (decoded, consumed) = decode_value_ext(&buf).unwrap();
1742 assert_eq!(consumed, n);
1743 assert_eq!(decoded.as_bytes(), Some(data.as_slice()));
1744 }
1745
1746 #[cfg(feature = "alloc")]
1747 #[test]
1748 fn test_value_ext_conversion() {
1749 let v = Value::Float(1.25);
1751 let ve = ValueExt::from_value(v);
1752 assert!((ve.as_float().unwrap() - 1.25).abs() < 0.001);
1753
1754 let ve = ValueExt::Int(42);
1756 let v = ve.to_value().unwrap();
1757 assert_eq!(v.as_int(), Some(42));
1758
1759 let ve = ValueExt::String("test".into());
1761 assert!(ve.to_value().is_none());
1762 }
1763}