someip_sd_wire/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![warn(missing_docs)]
3
4//! # SOME/IP-SD-wire
5//!
6//! This crate provides the means for parsing byte arrays into higher-level
7//! SOME/IP Service Discovery representations, and vice versa. It is designed to be used in embedded
8//! environments and is a `no_std` crate by default.
9//!
10//! ## Features
11//!
12//! - `no_std` compatible by default
13//! - Zero-allocation parsing and serialization
14//! - Support for all SOME/IP-SD message types
15//! - Clean enum-based API for entry and option types
16//! - Wire format using smoltcp-inspired zero-copy pattern
17//!
18//! ## Architecture
19//!
20//! Following the smoltcp/someip-wire pattern:
21//! - `packet` - Zero-copy wrapper around raw packet buffers
22//! - `repr` - High-level representation for parsing/emitting
23//! - `entries` - Zero-copy wrappers for service/eventgroup entries
24//! - `options` - Zero-copy wrappers for various option types
25//! - `config` - DNS-SD TXT record configuration options
26//! - `field` - Field offset definitions
27
28/// DNS-SD TXT record style configuration options for SOME/IP-SD.
29pub mod config;
30
31/// Service and EventGroup entry types with zero-copy wrappers.
32pub mod entries;
33
34/// Error type for parsing and validation failures.
35pub mod error;
36
37/// Field offset definitions for all wire format structures.
38pub mod field;
39
40/// SOME/IP-SD option types (IPv4/IPv6 Endpoint, LoadBalancing, etc.).
41pub mod options;
42
43/// Zero-copy packet wrapper for SOME/IP-SD messages.
44pub mod packet;
45
46/// High-level representation for parse/emit operations.
47pub mod repr;
48
49/// Prelude module for convenient imports.
50pub mod prelude;
51
52#[cfg(test)]
53mod zero_cost_tests {
54    use super::*;
55    
56    /// Verify that Packet and Repr are zero-sized wrappers (zero-cost abstraction)
57    /// The Packet struct should only contain a reference/slice to the buffer, no additional overhead
58    #[test]
59    fn test_zero_cost_packet_wrapper() {
60        use core::mem::size_of;
61        
62        // Packet<&[u8]> should be same size as a slice reference (2 * usize: ptr + len)
63        assert_eq!(size_of::<packet::Packet<&[u8]>>(), size_of::<&[u8]>());
64        
65        // Packet<&mut [u8]> should be same size as a mutable slice reference
66        assert_eq!(size_of::<packet::Packet<&mut [u8]>>(), size_of::<&mut [u8]>());
67    }
68    
69    /// Verify that Repr doesn't add overhead beyond its slice references
70    #[test]
71    fn test_zero_cost_repr() {
72        use core::mem::size_of;
73        
74        // Repr should be: u8 + u32 + 2 slices = 1 + 4 + 2*(ptr+len) + padding
75        // On 32-bit: ~20-24 bytes, on 64-bit: ~40-48 bytes
76        // The important part is it's just the fields, no heap pointers
77        let repr_size = size_of::<repr::Repr>();
78        let expected_min = size_of::<u8>() + size_of::<u32>() + 2 * size_of::<&[u8]>();
79        
80        assert!(repr_size >= expected_min);
81        assert!(repr_size <= expected_min + 16); // Allow for alignment padding
82    }
83    
84    /// Verify operations are const/inline-friendly (compile-time test)
85    /// This tests that field range calculations can be used in const contexts
86    #[test]
87    fn test_const_field_calculations() {
88        const _ENTRIES_LEN_END: usize = field::entries::LENGTH.end;
89        const _MIN_HEADER: usize = field::entries::MIN_HEADER_LEN;
90        
91        // If this compiles, the calculations are const-evaluable (zero-cost)
92        assert_eq!(_MIN_HEADER, 8);
93        assert_eq!(_ENTRIES_LEN_END, 8);
94    }
95    
96    /// Verify that parse/emit operations work on stack-allocated buffers
97    /// This demonstrates the intended usage pattern: all data lives on the stack or in user-provided buffers
98    #[test]
99    fn test_stack_only_operations() {
100        let mut buffer = [0u8; 64];
101        
102        // Setup a minimal valid packet on the stack
103        buffer[0] = 0x80; // flags
104        // reserved: 0, 0, 0
105        // entries_length: 0, 0, 0, 0
106        // options_length: 0, 0, 0, 0
107        
108        // All operations borrow from user-provided buffers (zero-copy)
109        let packet = packet::Packet::new_checked(&buffer[..]).unwrap();
110        let repr = repr::Repr::parse(&packet).unwrap();
111        
112        // Emit to another user-provided buffer (zero-copy)
113        let mut out_buffer = [0u8; 64];
114        let mut out_packet = packet::Packet::new_unchecked(&mut out_buffer[..]);
115        repr.emit(&mut out_packet);
116        
117        assert_eq!(out_packet.flags(), 0x80);
118    }
119}
120
121// Compile-time assertion that we don't link against an allocator in no_std mode
122// This will fail to compile if somehow an allocator is required
123#[cfg(not(test))]
124unsafe extern "C" {
125    // This symbol should NOT exist - if it's required, compilation will fail with "undefined reference"
126    // Remove this if you ever need to add allocation support
127    #[link_name = "\n\nERROR: This crate must not require an allocator\n\n"]
128    fn __rust_alloc_trigger_compile_error() -> !;
129}