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 Default for Session {
1196 fn default() -> Self {
1197 Self::new()
1198 }
1199 }
1200
1201 impl Session {
1202 pub const fn new() -> Self {
1203 Self {
1204 active: false,
1205 id: 0,
1206 subscriptions: [const { Subscription::empty() }; MAX_SUBS_PER_CLIENT],
1207 sub_count: 0,
1208 }
1209 }
1210
1211 pub fn subscribe(&mut self, id: u32, pattern: &str) -> bool {
1213 if self.sub_count as usize >= MAX_SUBS_PER_CLIENT {
1214 return false;
1215 }
1216 if pattern.len() > MAX_PATTERN_LEN {
1217 return false;
1218 }
1219
1220 for sub in &mut self.subscriptions {
1222 if !sub.active {
1223 sub.active = true;
1224 sub.id = id;
1225 sub.pattern[..pattern.len()].copy_from_slice(pattern.as_bytes());
1226 sub.pattern_len = pattern.len();
1227 self.sub_count += 1;
1228 return true;
1229 }
1230 }
1231 false
1232 }
1233
1234 pub fn unsubscribe(&mut self, id: u32) -> bool {
1236 for sub in &mut self.subscriptions {
1237 if sub.active && sub.id == id {
1238 sub.active = false;
1239 sub.pattern_len = 0;
1240 self.sub_count = self.sub_count.saturating_sub(1);
1241 return true;
1242 }
1243 }
1244 false
1245 }
1246
1247 pub fn has_match(&self, address: &str) -> bool {
1249 self.subscriptions.iter().any(|s| s.matches(address))
1250 }
1251 }
1252
1253 pub struct BroadcastList {
1255 pub clients: [bool; MAX_CLIENTS],
1256 pub count: u8,
1257 }
1258
1259 impl BroadcastList {
1260 pub const fn empty() -> Self {
1261 Self {
1262 clients: [false; MAX_CLIENTS],
1263 count: 0,
1264 }
1265 }
1266 }
1267
1268 pub struct MiniRouter {
1272 pub state: StateCache,
1273 sessions: [Session; MAX_CLIENTS],
1274 session_count: u8,
1275 tx_buf: [u8; TX_BUF_SIZE],
1276 }
1277
1278 impl MiniRouter {
1279 pub const fn new() -> Self {
1280 Self {
1281 state: StateCache::new(),
1282 sessions: [const { Session::new() }; MAX_CLIENTS],
1283 session_count: 0,
1284 tx_buf: [0; TX_BUF_SIZE],
1285 }
1286 }
1287
1288 pub fn process(&mut self, client_id: u8, data: &[u8]) -> Option<&[u8]> {
1292 let (_, payload_len) = decode_header(data)?;
1293 let payload = &data[HEADER_SIZE..HEADER_SIZE + payload_len];
1294 let msg = decode_message(payload)?;
1295
1296 match msg {
1297 Message::Hello { name: _, .. } => {
1298 self.create_session(client_id);
1299 Some(self.prepare_welcome(client_id))
1300 }
1301 Message::Subscribe { id, pattern } => {
1302 self.handle_subscribe(client_id, id, pattern);
1303 None }
1305 Message::Unsubscribe { id } => {
1306 self.handle_unsubscribe(client_id, id);
1307 None
1308 }
1309 Message::Set { address, value } => {
1310 self.state.set(address, value);
1311 None }
1313 Message::Ping => Some(self.prepare_pong()),
1314 _ => None,
1315 }
1316 }
1317
1318 pub fn get_broadcast_targets(&self, address: &str, sender_id: u8) -> BroadcastList {
1322 let mut result = BroadcastList::empty();
1323
1324 for (i, session) in self.sessions.iter().enumerate() {
1325 if session.active && i as u8 != sender_id && session.has_match(address) {
1327 result.clients[i] = true;
1328 result.count += 1;
1329 }
1330 }
1331
1332 result
1333 }
1334
1335 pub fn prepare_broadcast(&mut self, address: &str, value: Value) -> &[u8] {
1339 let n = encode_set_frame(&mut self.tx_buf, address, &value);
1340 &self.tx_buf[..n]
1341 }
1342
1343 fn handle_subscribe(&mut self, client_id: u8, id: u32, pattern: &str) {
1344 if let Some(session) = self.sessions.get_mut(client_id as usize) {
1345 if session.active {
1346 session.subscribe(id, pattern);
1347 }
1348 }
1349 }
1350
1351 fn handle_unsubscribe(&mut self, client_id: u8, id: u32) {
1352 if let Some(session) = self.sessions.get_mut(client_id as usize) {
1353 if session.active {
1354 session.unsubscribe(id);
1355 }
1356 }
1357 }
1358
1359 fn create_session(&mut self, client_id: u8) {
1360 if (client_id as usize) < MAX_CLIENTS {
1361 self.sessions[client_id as usize] = Session {
1362 active: true,
1363 id: client_id,
1364 subscriptions: [const { Subscription::empty() }; MAX_SUBS_PER_CLIENT],
1365 sub_count: 0,
1366 };
1367 self.session_count += 1;
1368 }
1369 }
1370
1371 pub fn disconnect(&mut self, client_id: u8) {
1373 if let Some(session) = self.sessions.get_mut(client_id as usize) {
1374 if session.active {
1375 session.active = false;
1376 session.sub_count = 0;
1377 self.session_count = self.session_count.saturating_sub(1);
1378 }
1379 }
1380 }
1381
1382 fn prepare_welcome(&mut self, _client_id: u8) -> &[u8] {
1383 let payload_start = HEADER_SIZE;
1384 let mut offset = payload_start;
1385
1386 self.tx_buf[offset] = msg::WELCOME;
1387 offset += 1;
1388
1389 self.tx_buf[offset] = VERSION;
1390 offset += 1;
1391
1392 self.tx_buf[offset] = 0xF8; offset += 1;
1394
1395 self.tx_buf[offset..offset + 8].copy_from_slice(&0u64.to_be_bytes());
1396 offset += 8;
1397
1398 offset += encode_string(&mut self.tx_buf[offset..], "embedded");
1399 offset += encode_string(&mut self.tx_buf[offset..], "MiniRouter");
1400
1401 let payload_len = offset - payload_start;
1402 encode_header(&mut self.tx_buf, 0, payload_len);
1403
1404 &self.tx_buf[..offset]
1405 }
1406
1407 fn prepare_pong(&mut self) -> &[u8] {
1408 let n = encode_pong_frame(&mut self.tx_buf);
1409 &self.tx_buf[..n]
1410 }
1411
1412 pub fn get(&self, address: &str) -> Option<Value> {
1413 self.state.get(address)
1414 }
1415
1416 pub fn set(&mut self, address: &str, value: Value) {
1417 self.state.set(address, value);
1418 }
1419
1420 pub fn session_count(&self) -> u8 {
1422 self.session_count
1423 }
1424
1425 pub fn session_mut(&mut self, client_id: u8) -> Option<&mut Session> {
1427 self.sessions.get_mut(client_id as usize)
1428 }
1429 }
1430
1431 impl Default for MiniRouter {
1432 fn default() -> Self {
1433 Self::new()
1434 }
1435 }
1436}
1437
1438#[cfg(test)]
1443mod tests {
1444 use super::*;
1445
1446 #[test]
1447 fn test_encode_decode_value() {
1448 let mut buf = [0u8; 16];
1449
1450 let n = encode_value(&mut buf, &Value::Float(1.25));
1452 assert_eq!(n, 9);
1453 let (v, consumed) = decode_value(&buf).unwrap();
1454 assert_eq!(consumed, 9);
1455 assert!((v.as_float().unwrap() - 1.25).abs() < 0.001);
1456
1457 let _n = encode_value(&mut buf, &Value::Int(-42));
1459 let (v, _) = decode_value(&buf).unwrap();
1460 assert_eq!(v.as_int(), Some(-42));
1461 }
1462
1463 #[test]
1464 fn test_encode_decode_set() {
1465 let mut buf = [0u8; 64];
1466 let n = encode_set_frame(&mut buf, "/test/value", &Value::Float(1.5));
1467 assert!(n > HEADER_SIZE);
1468
1469 let (_, payload_len) = decode_header(&buf).unwrap();
1470 let payload = &buf[HEADER_SIZE..HEADER_SIZE + payload_len];
1471 let msg = decode_message(payload).unwrap();
1472
1473 match msg {
1474 Message::Set { address, value } => {
1475 assert_eq!(address, "/test/value");
1476 assert!((value.as_float().unwrap() - 1.5).abs() < 0.001);
1477 }
1478 _ => panic!("Expected Set message"),
1479 }
1480 }
1481
1482 #[test]
1483 fn test_client_flow() {
1484 let mut client = Client::new();
1485 assert_eq!(client.state, ClientState::Disconnected);
1486
1487 let hello = client.prepare_hello("ESP32");
1489 assert!(hello.len() > HEADER_SIZE);
1490
1491 let mut welcome_buf = [0u8; 64];
1493 let payload_start = HEADER_SIZE;
1494 let mut offset = payload_start;
1495
1496 welcome_buf[offset] = msg::WELCOME;
1498 offset += 1;
1499
1500 welcome_buf[offset] = VERSION;
1502 offset += 1;
1503
1504 welcome_buf[offset] = 0xF8;
1506 offset += 1;
1507
1508 welcome_buf[offset..offset + 8].copy_from_slice(&0u64.to_be_bytes());
1510 offset += 8;
1511
1512 offset += encode_string(&mut welcome_buf[offset..], "session123");
1514
1515 offset += encode_string(&mut welcome_buf[offset..], "TestRouter");
1517
1518 encode_header(&mut welcome_buf, 0, offset - payload_start);
1519
1520 client.process(&welcome_buf[..offset]);
1521 assert_eq!(client.state, ClientState::Connected);
1522 }
1523
1524 #[test]
1525 fn test_state_cache() {
1526 let mut cache = StateCache::new();
1527
1528 cache.set("/sensor/temp", Value::Float(25.5));
1529 cache.set("/sensor/humidity", Value::Float(60.0));
1530
1531 assert_eq!(cache.get("/sensor/temp").unwrap().as_float(), Some(25.5));
1532 assert_eq!(
1533 cache.get("/sensor/humidity").unwrap().as_float(),
1534 Some(60.0)
1535 );
1536 assert!(cache.get("/unknown").is_none());
1537 }
1538
1539 #[test]
1540 fn test_memory_size() {
1541 let client_size = core::mem::size_of::<Client>();
1542 let _cache_size = core::mem::size_of::<StateCache>();
1543
1544 assert!(
1546 client_size < 4096,
1547 "Client too large: {} bytes",
1548 client_size
1549 );
1550
1551 let total = client_size + 1024; assert!(total < 8192, "Total too large: {} bytes", total);
1554 }
1555
1556 #[cfg(feature = "server")]
1557 #[test]
1558 fn test_mini_router() {
1559 use server::MiniRouter;
1560
1561 let mut router = MiniRouter::new();
1562 router.set("/light/brightness", Value::Float(0.8));
1563
1564 assert_eq!(
1565 router.get("/light/brightness").unwrap().as_float(),
1566 Some(0.8)
1567 );
1568 }
1569
1570 #[cfg(feature = "server")]
1571 #[test]
1572 fn test_mini_router_subscriptions() {
1573 use server::{Session, Subscription};
1574
1575 let mut sub = Subscription::empty();
1577 sub.active = true;
1578 sub.pattern_len = "/light/*".len();
1579 sub.pattern[..sub.pattern_len].copy_from_slice(b"/light/*");
1580
1581 assert!(sub.matches("/light/brightness"));
1582 assert!(sub.matches("/light/color"));
1583 assert!(!sub.matches("/audio/volume"));
1584 assert!(!sub.matches("/light/zone/1"));
1585
1586 sub.pattern_len = "/light/**".len();
1588 sub.pattern[..sub.pattern_len].copy_from_slice(b"/light/**");
1589 assert!(sub.matches("/light/brightness"));
1590 assert!(sub.matches("/light/zone/1/brightness"));
1591 assert!(!sub.matches("/audio/volume"));
1592
1593 let mut session = Session::new();
1595 session.active = true;
1596 assert!(session.subscribe(1, "/light/*"));
1597 assert!(session.subscribe(2, "/audio/**"));
1598 assert_eq!(session.sub_count, 2);
1599
1600 assert!(session.has_match("/light/brightness"));
1601 assert!(session.has_match("/audio/master/volume"));
1602 assert!(!session.has_match("/midi/cc/1"));
1603
1604 assert!(session.unsubscribe(1));
1606 assert_eq!(session.sub_count, 1);
1607 assert!(!session.has_match("/light/brightness"));
1608 assert!(session.has_match("/audio/master/volume"));
1609 }
1610
1611 #[cfg(feature = "server")]
1612 #[test]
1613 fn test_mini_router_broadcast() {
1614 use server::MiniRouter;
1615
1616 let mut router = MiniRouter::new();
1617
1618 {
1624 let session = router.session_mut(0).unwrap();
1625 session.active = true;
1626 session.id = 0;
1627 session.subscribe(1, "/light/**");
1628 }
1629
1630 {
1631 let session = router.session_mut(1).unwrap();
1632 session.active = true;
1633 session.id = 1;
1634 session.subscribe(1, "/audio/**");
1635 }
1636
1637 router.set("/light/brightness", Value::Float(0.75));
1639
1640 let targets = router.get_broadcast_targets("/light/brightness", 2);
1642 assert_eq!(targets.count, 1);
1643 assert!(targets.clients[0]); assert!(!targets.clients[1]); let targets = router.get_broadcast_targets("/audio/volume", 2);
1648 assert_eq!(targets.count, 1);
1649 assert!(!targets.clients[0]); assert!(targets.clients[1]); let targets = router.get_broadcast_targets("/light/brightness", 0);
1654 assert_eq!(targets.count, 0); }
1656
1657 #[test]
1658 fn test_new_message_types() {
1659 let announce_payload = [msg::ANNOUNCE, 0x00, 0x03]; let msg = decode_message(&announce_payload).unwrap();
1662 match msg {
1663 Message::Announce { signal_count } => assert_eq!(signal_count, 3),
1664 _ => panic!("Expected Announce message"),
1665 }
1666
1667 let sync_payload = [
1669 msg::SYNC,
1670 0x00,
1671 0x00,
1672 0x01,
1673 0x90,
1674 0x9F,
1675 0x8D,
1676 0x80,
1677 0x00, ];
1679 let msg = decode_message(&sync_payload).unwrap();
1680 match msg {
1681 Message::Sync { timestamp } => assert!(timestamp > 0),
1682 _ => panic!("Expected Sync message"),
1683 }
1684
1685 let bundle_payload = [msg::BUNDLE, 0x00, 0x00, 0x05]; let msg = decode_message(&bundle_payload).unwrap();
1688 match msg {
1689 Message::Bundle { message_count } => assert_eq!(message_count, 5),
1690 _ => panic!("Expected Bundle message"),
1691 }
1692
1693 let mut query_payload = [0u8; 32];
1695 query_payload[0] = msg::QUERY;
1696 let pattern = "/test/**";
1697 let len = (pattern.len() as u16).to_be_bytes();
1698 query_payload[1] = len[0];
1699 query_payload[2] = len[1];
1700 query_payload[3..3 + pattern.len()].copy_from_slice(pattern.as_bytes());
1701
1702 let msg = decode_message(&query_payload[..3 + pattern.len()]).unwrap();
1703 match msg {
1704 Message::Query { pattern: p } => assert_eq!(p, "/test/**"),
1705 _ => panic!("Expected Query message"),
1706 }
1707
1708 let result_payload = [msg::RESULT, 0x00, 0x0A]; let msg = decode_message(&result_payload).unwrap();
1711 match msg {
1712 Message::Result { signal_count } => assert_eq!(signal_count, 10),
1713 _ => panic!("Expected Result message"),
1714 }
1715 }
1716
1717 #[cfg(feature = "alloc")]
1718 #[test]
1719 fn test_value_ext_string() {
1720 let mut buf = [0u8; 64];
1721
1722 let value = ValueExt::String("hello world".into());
1724 let n = encode_value_ext(&mut buf, &value);
1725 assert!(n > 0);
1726
1727 let (decoded, consumed) = decode_value_ext(&buf).unwrap();
1729 assert_eq!(consumed, n);
1730 assert_eq!(decoded.as_str(), Some("hello world"));
1731 }
1732
1733 #[cfg(feature = "alloc")]
1734 #[test]
1735 fn test_value_ext_bytes() {
1736 use alloc::vec;
1737
1738 let mut buf = [0u8; 64];
1739
1740 let data = vec![0x01, 0x02, 0x03, 0x04];
1742 let value = ValueExt::Bytes(data.clone());
1743 let n = encode_value_ext(&mut buf, &value);
1744 assert!(n > 0);
1745
1746 let (decoded, consumed) = decode_value_ext(&buf).unwrap();
1748 assert_eq!(consumed, n);
1749 assert_eq!(decoded.as_bytes(), Some(data.as_slice()));
1750 }
1751
1752 #[cfg(feature = "alloc")]
1753 #[test]
1754 fn test_value_ext_conversion() {
1755 let v = Value::Float(1.25);
1757 let ve = ValueExt::from_value(v);
1758 assert!((ve.as_float().unwrap() - 1.25).abs() < 0.001);
1759
1760 let ve = ValueExt::Int(42);
1762 let v = ve.to_value().unwrap();
1763 assert_eq!(v.as_int(), Some(42));
1764
1765 let ve = ValueExt::String("test".into());
1767 assert!(ve.to_value().is_none());
1768 }
1769}