someip_sd_wire/
entries.rs

1/// Entry types for SOME/IP-SD messages.
2///
3/// This module provides zero-copy wrappers around service and eventgroup entries,
4/// as well as helper types for packed bitfields used within entries.
5
6use crate::error::Error;
7use crate::field;
8use byteorder::{ByteOrder, NetworkEndian};
9
10/// Result type for entry parsing operations.
11pub type Result<T> = core::result::Result<T, Error>;
12
13/// Entry type codes for SOME/IP-SD entries.
14///
15/// Each SOME/IP-SD entry starts with a type field that identifies whether
16/// it's a service-related entry or an eventgroup-related entry.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u8)]
19pub enum EntryType {
20    /// FindService entry (0x00) - Used to discover available services.
21    FindService = 0x00,
22    
23    /// OfferService entry (0x01) - Used to announce service availability.
24    /// 
25    /// Note: StopOfferService uses OfferService (0x01) with TTL=0.
26    OfferService = 0x01,
27    
28    /// Subscribe entry (0x06) - Used to subscribe to eventgroups.
29    /// 
30    /// Note: StopSubscribe uses Subscribe (0x06) with TTL=0.
31    Subscribe = 0x06,
32    
33    /// SubscribeAck entry (0x07) - Acknowledgment for Subscribe requests.
34    SubscribeAck = 0x07,
35}
36
37impl EntryType {
38    /// Creates an EntryType from a raw byte value.
39    ///
40    /// # Parameters
41    ///
42    /// * `value` - Raw byte value from wire format
43    ///
44    /// # Returns
45    ///
46    /// * `Some(EntryType)` if the value is valid
47    /// * `None` if the value doesn't match any known entry type
48    pub fn from_u8(value: u8) -> Option<Self> {
49        match value {
50            0x00 => Some(EntryType::FindService),
51            0x01 => Some(EntryType::OfferService),
52            0x06 => Some(EntryType::Subscribe),
53            0x07 => Some(EntryType::SubscribeAck),
54            _ => None,
55        }
56    }
57
58    /// Converts the EntryType to its raw byte value.
59    ///
60    /// # Returns
61    ///
62    /// Raw byte value for wire format
63    pub fn as_u8(&self) -> u8 {
64        *self as u8
65    }
66
67    /// Returns true if this is a service entry type (not eventgroup).
68    ///
69    /// Service entry types are FindService and OfferService.
70    pub fn is_service_entry(&self) -> bool {
71        matches!(self, EntryType::FindService | EntryType::OfferService)
72    }
73
74    /// Returns true if this is an eventgroup entry type (not service).
75    ///
76    /// Eventgroup entry types are Subscribe and SubscribeAck.
77    pub fn is_eventgroup_entry(&self) -> bool {
78        matches!(self, EntryType::Subscribe | EntryType::SubscribeAck)
79    }
80}
81
82/// Two 4-bit fields packed into a single byte.
83///
84/// Used for the NumberOfOptions field in entries, which contains the number of
85/// options in the first and second option runs (each 4 bits, values 0-15).
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub struct NumberOfOptions(u8);
88
89impl NumberOfOptions {
90    /// Creates a new NumberOfOptions with both fields set to 0.
91    pub fn new() -> Self {
92        NumberOfOptions(0)
93    }
94
95    /// Creates NumberOfOptions from two 4-bit values.
96    ///
97    /// # Parameters
98    ///
99    /// * `options1` - Number of options in first run (0-15, high nibble)
100    /// * `options2` - Number of options in second run (0-15, low nibble)
101    ///
102    /// # Returns
103    ///
104    /// Packed NumberOfOptions value
105    pub fn from_options(options1: u8, options2: u8) -> Self {
106        let opt1 = options1 & 0x0F;
107        let opt2 = options2 & 0x0F;
108        NumberOfOptions((opt1 << 4) | opt2)
109    }
110
111    /// Creates from raw u8 value.
112    ///
113    /// # Parameters
114    ///
115    /// * `value` - Raw byte value from wire format
116    pub fn from_u8(value: u8) -> Self {
117        NumberOfOptions(value)
118    }
119
120    /// Gets the number of options for the first option run (high nibble).
121    ///
122    /// # Returns
123    ///
124    /// Number of options (0-15)
125    pub fn options1(&self) -> u8 {
126        (self.0 >> 4) & 0x0F
127    }
128
129    /// Gets the number of options for the second option run (low nibble).
130    ///
131    /// # Returns
132    ///
133    /// Number of options (0-15)
134    pub fn options2(&self) -> u8 {
135        self.0 & 0x0F
136    }
137
138    /// Sets the number of options for the first option run.
139    ///
140    /// # Parameters
141    ///
142    /// * `value` - Number of options (0-15, will be masked)
143    pub fn set_options1(&mut self, value: u8) {
144        let masked = value & 0x0F;
145        self.0 = (self.0 & 0x0F) | (masked << 4);
146    }
147
148    /// Sets the number of options for the second option run.
149    ///
150    /// # Parameters
151    ///
152    /// * `value` - Number of options (0-15, will be masked)
153    pub fn set_options2(&mut self, value: u8) {
154        let masked = value & 0x0F;
155        self.0 = (self.0 & 0xF0) | masked;
156    }
157
158    /// Converts to raw u8 value for wire format.
159    pub fn as_u8(&self) -> u8 {
160        self.0
161    }
162}
163
164/// 12-bit reserved field + 4-bit counter packed into a u16.
165///
166/// Used in EventGroup entries. The reserved field must be 0x000 per specification.
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub struct ReservedAndCounter(u16);
169
170impl ReservedAndCounter {
171    /// Creates a new ReservedAndCounter with reserved=0x000 and counter=0x0.
172    pub fn new() -> Self {
173        ReservedAndCounter(0)
174    }
175
176    /// Creates ReservedAndCounter from reserved (12-bit) and counter (4-bit) values.
177    ///
178    /// # Parameters
179    ///
180    /// * `reserved` - Reserved field (12 bits, should be 0x000)
181    /// * `counter` - Counter field (4 bits, 0-15)
182    pub fn from_fields(reserved: u16, counter: u8) -> Self {
183        let res = reserved & 0x0FFF;
184        let cnt = (counter & 0x0F) as u16;
185        ReservedAndCounter((res << 4) | cnt)
186    }
187
188    /// Creates from counter only (reserved will be 0x000 as per spec).
189    ///
190    /// # Parameters
191    ///
192    /// * `counter` - Counter value (4 bits, 0-15)
193    pub fn from_counter(counter: u8) -> Self {
194        Self::from_fields(0, counter)
195    }
196
197    /// Gets the reserved field (should always be 0x000 per spec).
198    ///
199    /// # Returns
200    ///
201    /// 12-bit reserved value
202    pub fn reserved(&self) -> u16 {
203        (self.0 >> 4) & 0x0FFF
204    }
205
206    /// Gets the counter field (low 4 bits).
207    ///
208    /// # Returns
209    ///
210    /// Counter value (0-15)
211    pub fn counter(&self) -> u8 {
212        (self.0 & 0x0F) as u8
213    }
214
215    /// Sets the counter field (reserved remains 0x000).
216    ///
217    /// # Parameters
218    ///
219    /// * `value` - Counter value (0-15, will be masked)
220    pub fn set_counter(&mut self, value: u8) {
221        let masked = (value & 0x0F) as u16;
222        self.0 = (self.0 & 0xFFF0) | masked;
223    }
224
225    /// Converts to raw u16 value.
226    pub fn as_u16(&self) -> u16 {
227        self.0
228    }
229
230    /// Creates from raw u16 value.
231    ///
232    /// # Parameters
233    ///
234    /// * `value` - Raw 16-bit value from wire format
235    pub fn from_u16(value: u16) -> Self {
236        ReservedAndCounter(value)
237    }
238
239    /// Converts to big-endian bytes for network transmission.
240    pub fn to_be_bytes(&self) -> [u8; 2] {
241        self.0.to_be_bytes()
242    }
243
244    /// Creates from big-endian bytes (for parsing from network).
245    ///
246    /// # Parameters
247    ///
248    /// * `bytes` - 2-byte big-endian array
249    pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
250        ReservedAndCounter(u16::from_be_bytes(bytes))
251    }
252}
253
254/// Zero-copy wrapper around a Service Entry (16 bytes).
255///
256/// Service entries are used for FindService and OfferService messages in SOME/IP-SD.
257/// They contain information about service availability and discovery.
258///
259/// # Wire Format
260///
261/// ```text
262/// Byte 0:    Type (0x00=FindService, 0x01=OfferService)
263/// Byte 1:    Index1stOptions (4-bit) | Index2ndOptions (4-bit)
264/// Byte 2:    # of opt 1 (4-bit) | # of opt 2 (4-bit)
265/// Byte 3:    Service ID (high byte)
266/// Byte 4:    Service ID (low byte)
267/// Byte 5:    Instance ID (high byte)
268/// Byte 6:    Instance ID (low byte)
269/// Byte 7:    Major Version
270/// Byte 8-10: TTL (24-bit, 0xFFFFFF=infinite, 0x000000=stop)
271/// Byte 11-14: Minor Version (32-bit)
272/// ```
273#[derive(Debug, Clone, Copy)]
274pub struct ServiceEntry<T: AsRef<[u8]>> {
275    buffer: T,
276}
277
278impl<T: AsRef<[u8]>> ServiceEntry<T> {
279    /// Size of a service entry in bytes.
280    pub const LENGTH: usize = 16;
281
282    /// Creates a new unchecked ServiceEntry from a buffer.
283    ///
284    /// # Parameters
285    ///
286    /// * `buffer` - Buffer containing service entry data
287    ///
288    /// # Safety
289    ///
290    /// This does not validate buffer length. Use `new_checked` for validation.
291    pub fn new_unchecked(buffer: T) -> Self {
292        ServiceEntry { buffer }
293    }
294
295    /// Create a ServiceEntry from a buffer with length validation.
296    ///
297    /// # Parameters
298    /// * `buffer` - The buffer containing the 16-byte service entry
299    ///
300    /// # Returns
301    /// * `Ok(ServiceEntry)` if buffer is at least 16 bytes
302    /// * `Err(Error)` if buffer is too short
303    pub fn new_checked(buffer: T) -> Result<Self> {
304        let entry = Self::new_unchecked(buffer);
305        entry.check_len()?;
306        Ok(entry)
307    }
308
309    /// Validate that the buffer is at least 16 bytes long.
310    ///
311    /// # Returns
312    /// * `Ok(())` if buffer meets minimum length requirement
313    /// * `Err(Error)` if buffer is too short
314    pub fn check_len(&self) -> Result<()> {
315        if self.buffer.as_ref().len() < Self::LENGTH {
316            return Err(Error::BufferTooShort);
317        }
318        Ok(())
319    }
320
321    /// Validate the entry has a valid service entry type.
322    ///
323    /// # Returns
324    /// * `Ok(())` if entry type is FindService (0x00) or OfferService (0x01)
325    /// * `Err(Error::InvalidEntryType)` if entry type is invalid for service entries
326    pub fn check_entry_type(&self) -> Result<()> {
327        let type_val = self.entry_type();
328        match EntryType::from_u8(type_val) {
329            Some(et) if et.is_service_entry() => Ok(()),
330            _ => Err(Error::InvalidEntryType(type_val)),
331        }
332    }
333
334    /// Get the entry type field (1 byte at offset 0).
335    ///
336    /// # Returns
337    /// Entry type value (0x00=FindService, 0x01=OfferService)
338    pub fn entry_type(&self) -> u8 {
339        self.buffer.as_ref()[field::service_entry::TYPE.start]
340    }
341
342    /// Get the index of the first option run (1 byte at offset 1).
343    ///
344    /// # Returns
345    /// Index into the options array for the first run, or 0 if no options
346    pub fn index_first_option_run(&self) -> u8 {
347        self.buffer.as_ref()[field::service_entry::INDEX_FIRST_OPTION_RUN.start]
348    }
349
350    /// Get the index of the second option run (1 byte at offset 2).
351    ///
352    /// # Returns
353    /// Index into the options array for the second run, or 0 if no second run
354    pub fn index_second_option_run(&self) -> u8 {
355        self.buffer.as_ref()[field::service_entry::INDEX_SECOND_OPTION_RUN.start]
356    }
357
358    /// Get the packed number of options (1 byte at offset 3).
359    ///
360    /// # Returns
361    /// NumberOfOptions containing 4-bit counts for two option runs
362    pub fn number_of_options(&self) -> NumberOfOptions {
363        NumberOfOptions::from_u8(self.buffer.as_ref()[field::service_entry::NUMBER_OF_OPTIONS.start])
364    }
365
366    /// Get the Service ID (2 bytes at offset 4-5, network byte order).
367    ///
368    /// # Returns
369    /// 16-bit Service ID identifying the service
370    pub fn service_id(&self) -> u16 {
371        NetworkEndian::read_u16(&self.buffer.as_ref()[field::service_entry::SERVICE_ID])
372    }
373
374    /// Get the Instance ID (2 bytes at offset 6-7, network byte order).
375    ///
376    /// # Returns
377    /// 16-bit Instance ID identifying the service instance
378    pub fn instance_id(&self) -> u16 {
379        NetworkEndian::read_u16(&self.buffer.as_ref()[field::service_entry::INSTANCE_ID])
380    }
381
382    /// Get the Major Version (1 byte at offset 8).
383    ///
384    /// # Returns
385    /// 8-bit major version of the service interface
386    pub fn major_version(&self) -> u8 {
387        self.buffer.as_ref()[field::service_entry::MAJOR_VERSION.start]
388    }
389
390    /// Get the TTL (Time To Live) field (3 bytes at offset 9-11).
391    ///
392    /// # Returns
393    /// 24-bit TTL in seconds, or 0xFFFFFF for infinite lifetime
394    pub fn ttl(&self) -> u32 {
395        // TTL is 3 bytes
396        let bytes = &self.buffer.as_ref()[field::service_entry::TTL];
397        ((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | (bytes[2] as u32)
398    }
399
400    /// Get the Minor Version (4 bytes at offset 12-15, network byte order).
401    ///
402    /// # Returns
403    /// 32-bit minor version of the service interface
404    pub fn minor_version(&self) -> u32 {
405        NetworkEndian::read_u32(&self.buffer.as_ref()[field::service_entry::MINOR_VERSION])
406    }
407}
408
409impl<T: AsRef<[u8]> + AsMut<[u8]>> ServiceEntry<T> {
410    /// Set the entry type field (1 byte at offset 0).
411    ///
412    /// # Parameters
413    /// * `value` - Entry type value (0x00=FindService, 0x01=OfferService)
414    pub fn set_entry_type(&mut self, value: u8) {
415        self.buffer.as_mut()[field::service_entry::TYPE.start] = value;
416    }
417
418    /// Set the index of the first option run (1 byte at offset 1).
419    ///
420    /// # Parameters
421    /// * `value` - Index into the options array for the first run
422    pub fn set_index_first_option_run(&mut self, value: u8) {
423        self.buffer.as_mut()[field::service_entry::INDEX_FIRST_OPTION_RUN.start] = value;
424    }
425
426    /// Set the index of the second option run (1 byte at offset 2).
427    ///
428    /// # Parameters
429    /// * `value` - Index into the options array for the second run
430    pub fn set_index_second_option_run(&mut self, value: u8) {
431        self.buffer.as_mut()[field::service_entry::INDEX_SECOND_OPTION_RUN.start] = value;
432    }
433
434    /// Set the packed number of options (1 byte at offset 3).
435    ///
436    /// # Parameters
437    /// * `value` - NumberOfOptions containing 4-bit counts for two option runs
438    pub fn set_number_of_options(&mut self, value: NumberOfOptions) {
439        self.buffer.as_mut()[field::service_entry::NUMBER_OF_OPTIONS.start] = value.as_u8();
440    }
441
442    /// Set the Service ID (2 bytes at offset 4-5, network byte order).
443    ///
444    /// # Parameters
445    /// * `value` - 16-bit Service ID identifying the service
446    pub fn set_service_id(&mut self, value: u16) {
447        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::service_entry::SERVICE_ID], value);
448    }
449
450    /// Set the Instance ID (2 bytes at offset 6-7, network byte order).
451    ///
452    /// # Parameters
453    /// * `value` - 16-bit Instance ID identifying the service instance
454    pub fn set_instance_id(&mut self, value: u16) {
455        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::service_entry::INSTANCE_ID], value);
456    }
457
458    /// Set the Major Version (1 byte at offset 8).
459    ///
460    /// # Parameters
461    /// * `value` - 8-bit major version of the service interface
462    pub fn set_major_version(&mut self, value: u8) {
463        self.buffer.as_mut()[field::service_entry::MAJOR_VERSION.start] = value;
464    }
465
466    /// Set the TTL (Time To Live) field (3 bytes at offset 9-11).
467    ///
468    /// # Parameters
469    /// * `value` - 24-bit TTL in seconds (lower 24 bits used), or 0xFFFFFF for infinite
470    pub fn set_ttl(&mut self, value: u32) {
471        let bytes = &mut self.buffer.as_mut()[field::service_entry::TTL];
472        bytes[0] = ((value >> 16) & 0xFF) as u8;
473        bytes[1] = ((value >> 8) & 0xFF) as u8;
474        bytes[2] = (value & 0xFF) as u8;
475    }
476
477    /// Set the Minor Version (4 bytes at offset 12-15, network byte order).
478    ///
479    /// # Parameters
480    /// * `value` - 32-bit minor version of the service interface
481    pub fn set_minor_version(&mut self, value: u32) {
482        NetworkEndian::write_u32(&mut self.buffer.as_mut()[field::service_entry::MINOR_VERSION], value);
483    }
484}
485
486/// Zero-copy wrapper around an EventGroup Entry (16 bytes)
487///
488/// EventGroup entries are used for Subscribe/SubscribeAck messages.
489/// They share the same 16-byte structure as Service entries but use
490/// different fields for EventGroup ID and counter.
491///
492/// Wire format (16 bytes):
493/// ```text
494/// 0               1               2               3
495/// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
496/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
497/// |     Type      |  Index 1st    |  Index 2nd    | # of options  |
498/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
499/// |          Service ID           |         Instance ID           |
500/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
501/// |  Major Ver.   |                     TTL                       |
502/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
503/// |         Reserved (12)         |Cnt|        EventGroup ID      |
504/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
505/// ```
506#[derive(Debug, Clone, Copy)]
507pub struct EventGroupEntry<T: AsRef<[u8]>> {
508    buffer: T,
509}
510
511impl<T: AsRef<[u8]>> EventGroupEntry<T> {
512    /// EventGroup entry wire format size in bytes.
513    pub const LENGTH: usize = 16;
514
515    /// Create an EventGroupEntry without validation.
516    ///
517    /// # Parameters
518    /// * `buffer` - The buffer containing the 16-byte eventgroup entry
519    ///
520    /// # Safety
521    /// This does not validate buffer length. Use `new_checked` for validation.
522    pub fn new_unchecked(buffer: T) -> Self {
523        EventGroupEntry { buffer }
524    }
525
526    /// Create an EventGroupEntry from a buffer with length validation.
527    ///
528    /// # Parameters
529    /// * `buffer` - The buffer containing the 16-byte eventgroup entry
530    ///
531    /// # Returns
532    /// * `Ok(EventGroupEntry)` if buffer is at least 16 bytes
533    /// * `Err(Error)` if buffer is too short
534    pub fn new_checked(buffer: T) -> Result<Self> {
535        let entry = Self::new_unchecked(buffer);
536        entry.check_len()?;
537        Ok(entry)
538    }
539
540    /// Validate that the buffer is at least 16 bytes long.
541    ///
542    /// # Returns
543    /// * `Ok(())` if buffer meets minimum length requirement
544    /// * `Err(Error)` if buffer is too short
545    pub fn check_len(&self) -> Result<()> {
546        if self.buffer.as_ref().len() < Self::LENGTH {
547            return Err(Error::BufferTooShort);
548        }
549        Ok(())
550    }
551
552    /// Validate the entry has a valid eventgroup entry type.
553    ///
554    /// # Returns
555    /// * `Ok(())` if entry type is Subscribe (0x06) or SubscribeAck (0x07)
556    /// * `Err(Error::InvalidEntryType)` if entry type is invalid for eventgroup entries
557    pub fn check_entry_type(&self) -> Result<()> {
558        let type_val = self.entry_type();
559        match EntryType::from_u8(type_val) {
560            Some(et) if et.is_eventgroup_entry() => Ok(()),
561            _ => Err(Error::InvalidEntryType(type_val)),
562        }
563    }
564
565    /// Get the entry type field (1 byte at offset 0).
566    ///
567    /// # Returns
568    /// Entry type value (0x06=Subscribe, 0x07=SubscribeAck)
569    pub fn entry_type(&self) -> u8 {
570        self.buffer.as_ref()[field::event_group_entry::TYPE.start]
571    }
572
573    /// Get the index of the first option run (1 byte at offset 1).
574    ///
575    /// # Returns
576    /// Index into the options array for the first run, or 0 if no options
577    pub fn index_first_option_run(&self) -> u8 {
578        self.buffer.as_ref()[field::event_group_entry::INDEX_FIRST_OPTION_RUN.start]
579    }
580
581    /// Get the index of the second option run (1 byte at offset 2).
582    ///
583    /// # Returns
584    /// Index into the options array for the second run, or 0 if no second run
585    pub fn index_second_option_run(&self) -> u8 {
586        self.buffer.as_ref()[field::event_group_entry::INDEX_SECOND_OPTION_RUN.start]
587    }
588
589    /// Get the packed number of options (1 byte at offset 3).
590    ///
591    /// # Returns
592    /// NumberOfOptions containing 4-bit counts for two option runs
593    pub fn number_of_options(&self) -> NumberOfOptions {
594        NumberOfOptions::from_u8(self.buffer.as_ref()[field::event_group_entry::NUMBER_OF_OPTIONS.start])
595    }
596
597    /// Get the Service ID (2 bytes at offset 4-5, network byte order).
598    ///
599    /// # Returns
600    /// 16-bit Service ID identifying the service
601    pub fn service_id(&self) -> u16 {
602        NetworkEndian::read_u16(&self.buffer.as_ref()[field::event_group_entry::SERVICE_ID])
603    }
604
605    /// Get the Instance ID (2 bytes at offset 6-7, network byte order).
606    ///
607    /// # Returns
608    /// 16-bit Instance ID identifying the service instance
609    pub fn instance_id(&self) -> u16 {
610        NetworkEndian::read_u16(&self.buffer.as_ref()[field::event_group_entry::INSTANCE_ID])
611    }
612
613    /// Get the Major Version (1 byte at offset 8).
614    ///
615    /// # Returns
616    /// 8-bit major version of the service interface
617    pub fn major_version(&self) -> u8 {
618        self.buffer.as_ref()[field::event_group_entry::MAJOR_VERSION.start]
619    }
620
621    /// Get the TTL (Time To Live) field (3 bytes at offset 9-11).
622    ///
623    /// # Returns
624    /// 24-bit TTL in seconds, or 0xFFFFFF for infinite lifetime
625    pub fn ttl(&self) -> u32 {
626        // TTL is 3 bytes
627        let bytes = &self.buffer.as_ref()[field::event_group_entry::TTL];
628        ((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | (bytes[2] as u32)
629    }
630
631    /// Get the packed reserved and counter field (2 bytes at offset 12-13).
632    ///
633    /// # Returns
634    /// ReservedAndCounter containing 12-bit reserved field and 4-bit counter
635    pub fn reserved_and_counter(&self) -> ReservedAndCounter {
636        let value = NetworkEndian::read_u16(&self.buffer.as_ref()[field::event_group_entry::RESERVED_AND_COUNTER]);
637        ReservedAndCounter::from_u16(value)
638    }
639
640    /// Get the EventGroup ID (2 bytes at offset 14-15, network byte order).
641    ///
642    /// # Returns
643    /// 16-bit EventGroup ID identifying the event group
644    pub fn eventgroup_id(&self) -> u16 {
645        NetworkEndian::read_u16(&self.buffer.as_ref()[field::event_group_entry::EVENTGROUP_ID])
646    }
647}
648
649impl<T: AsRef<[u8]> + AsMut<[u8]>> EventGroupEntry<T> {
650    /// Set the entry type field (1 byte at offset 0).
651    ///
652    /// # Parameters
653    /// * `value` - Entry type value (0x06=Subscribe, 0x07=SubscribeAck)
654    pub fn set_entry_type(&mut self, value: u8) {
655        self.buffer.as_mut()[field::event_group_entry::TYPE.start] = value;
656    }
657
658    /// Set the index of the first option run (1 byte at offset 1).
659    ///
660    /// # Parameters
661    /// * `value` - Index into the options array for the first run
662    pub fn set_index_first_option_run(&mut self, value: u8) {
663        self.buffer.as_mut()[field::event_group_entry::INDEX_FIRST_OPTION_RUN.start] = value;
664    }
665
666    /// Set the index of the second option run (1 byte at offset 2).
667    ///
668    /// # Parameters
669    /// * `value` - Index into the options array for the second run
670    pub fn set_index_second_option_run(&mut self, value: u8) {
671        self.buffer.as_mut()[field::event_group_entry::INDEX_SECOND_OPTION_RUN.start] = value;
672    }
673
674    /// Set the packed number of options (1 byte at offset 3).
675    ///
676    /// # Parameters
677    /// * `value` - NumberOfOptions containing 4-bit counts for two option runs
678    pub fn set_number_of_options(&mut self, value: NumberOfOptions) {
679        self.buffer.as_mut()[field::event_group_entry::NUMBER_OF_OPTIONS.start] = value.as_u8();
680    }
681
682    /// Set the Service ID (2 bytes at offset 4-5, network byte order).
683    ///
684    /// # Parameters
685    /// * `value` - 16-bit Service ID identifying the service
686    pub fn set_service_id(&mut self, value: u16) {
687        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::event_group_entry::SERVICE_ID], value);
688    }
689
690    /// Set the Instance ID (2 bytes at offset 6-7, network byte order).
691    ///
692    /// # Parameters
693    /// * `value` - 16-bit Instance ID identifying the service instance
694    pub fn set_instance_id(&mut self, value: u16) {
695        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::event_group_entry::INSTANCE_ID], value);
696    }
697
698    /// Set the Major Version (1 byte at offset 8).
699    ///
700    /// # Parameters
701    /// * `value` - 8-bit major version of the service interface
702    pub fn set_major_version(&mut self, value: u8) {
703        self.buffer.as_mut()[field::event_group_entry::MAJOR_VERSION.start] = value;
704    }
705
706    /// Set the TTL (Time To Live) field (3 bytes at offset 9-11).
707    ///
708    /// # Parameters
709    /// * `value` - 24-bit TTL in seconds (lower 24 bits used), or 0xFFFFFF for infinite
710    pub fn set_ttl(&mut self, value: u32) {
711        let bytes = &mut self.buffer.as_mut()[field::event_group_entry::TTL];
712        bytes[0] = ((value >> 16) & 0xFF) as u8;
713        bytes[1] = ((value >> 8) & 0xFF) as u8;
714        bytes[2] = (value & 0xFF) as u8;
715    }
716
717    /// Set the packed reserved and counter field (2 bytes at offset 12-13).
718    ///
719    /// # Parameters
720    /// * `value` - ReservedAndCounter containing 12-bit reserved field and 4-bit counter
721    pub fn set_reserved_and_counter(&mut self, value: ReservedAndCounter) {
722        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::event_group_entry::RESERVED_AND_COUNTER], value.as_u16());
723    }
724
725    /// Set the EventGroup ID (2 bytes at offset 14-15, network byte order).
726    ///
727    /// # Parameters
728    /// * `value` - 16-bit EventGroup ID identifying the event group
729    pub fn set_eventgroup_id(&mut self, value: u16) {
730        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::event_group_entry::EVENTGROUP_ID], value);
731    }
732}
733
734#[cfg(test)]
735mod tests {
736    use super::*;
737
738    #[test]
739    fn test_service_entry() {
740        let mut buffer = [0u8; 16];
741        let mut entry = ServiceEntry::new_unchecked(&mut buffer[..]);
742        
743        entry.set_entry_type(EntryType::OfferService.as_u8());
744        entry.set_service_id(0x1234);
745        entry.set_instance_id(0x5678);
746        entry.set_major_version(1);
747        entry.set_minor_version(0x0000_0001);
748        entry.set_ttl(0xFFFFFF);
749        
750        assert_eq!(entry.entry_type(), 0x01);
751        assert_eq!(entry.service_id(), 0x1234);
752        assert_eq!(entry.instance_id(), 0x5678);
753        assert_eq!(entry.major_version(), 1);
754        assert_eq!(entry.minor_version(), 1);
755        assert_eq!(entry.ttl(), 0xFFFFFF);
756    }
757
758    #[test]
759    fn test_eventgroup_entry() {
760        let mut buffer = [0u8; 16];
761        let mut entry = EventGroupEntry::new_unchecked(&mut buffer[..]);
762        
763        entry.set_entry_type(EntryType::Subscribe.as_u8());
764        entry.set_service_id(0x1234);
765        entry.set_instance_id(0x5678);
766        entry.set_major_version(1);
767        entry.set_ttl(0xFFFFFF);
768        entry.set_eventgroup_id(0xABCD);
769        entry.set_reserved_and_counter(ReservedAndCounter::from_counter(5));
770        
771        assert_eq!(entry.entry_type(), 0x06);
772        assert_eq!(entry.service_id(), 0x1234);
773        assert_eq!(entry.instance_id(), 0x5678);
774        assert_eq!(entry.major_version(), 1);
775        assert_eq!(entry.ttl(), 0xFFFFFF);
776        assert_eq!(entry.eventgroup_id(), 0xABCD);
777        assert_eq!(entry.reserved_and_counter().counter(), 5);
778    }
779
780    #[test]
781    fn test_number_of_options() {
782        let opts = NumberOfOptions::from_options(3, 7);
783        assert_eq!(opts.options1(), 3);
784        assert_eq!(opts.options2(), 7);
785        assert_eq!(opts.as_u8(), 0x37);
786
787        let mut opts = NumberOfOptions::new();
788        opts.set_options1(15);
789        opts.set_options2(8);
790        assert_eq!(opts.options1(), 15);
791        assert_eq!(opts.options2(), 8);
792    }
793
794    #[test]
795    fn test_reserved_and_counter() {
796        let rc = ReservedAndCounter::from_counter(5);
797        assert_eq!(rc.reserved(), 0x000);
798        assert_eq!(rc.counter(), 5);
799
800        let rc = ReservedAndCounter::from_fields(0xABC, 0xF);
801        assert_eq!(rc.reserved(), 0xABC);
802        assert_eq!(rc.counter(), 0xF);
803        assert_eq!(rc.as_u16(), 0xABCF);
804
805        let bytes = rc.to_be_bytes();
806        let rc2 = ReservedAndCounter::from_be_bytes(bytes);
807        assert_eq!(rc.as_u16(), rc2.as_u16());
808    }
809
810    #[test]
811    fn test_service_entry_type_validation() {
812        // Valid service entry types
813        let mut buffer = [0u8; 16];
814        buffer[0] = 0x00; // FindService
815        let entry = ServiceEntry::new_unchecked(&buffer[..]);
816        assert!(entry.check_entry_type().is_ok());
817
818        buffer[0] = 0x01; // OfferService
819        let entry = ServiceEntry::new_unchecked(&buffer[..]);
820        assert!(entry.check_entry_type().is_ok());
821
822        // Invalid service entry types
823        buffer[0] = 0x06; // Subscribe (eventgroup type)
824        let entry = ServiceEntry::new_unchecked(&buffer[..]);
825        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0x06)));
826
827        buffer[0] = 0x07; // SubscribeAck (eventgroup type)
828        let entry = ServiceEntry::new_unchecked(&buffer[..]);
829        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0x07)));
830
831        buffer[0] = 0xFF; // Unknown type
832        let entry = ServiceEntry::new_unchecked(&buffer[..]);
833        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0xFF)));
834
835        buffer[0] = 0x42; // Random invalid type
836        let entry = ServiceEntry::new_unchecked(&buffer[..]);
837        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0x42)));
838    }
839
840    #[test]
841    fn test_eventgroup_entry_type_validation() {
842        // Valid eventgroup entry types
843        let mut buffer = [0u8; 16];
844        buffer[0] = 0x06; // Subscribe
845        let entry = EventGroupEntry::new_unchecked(&buffer[..]);
846        assert!(entry.check_entry_type().is_ok());
847
848        buffer[0] = 0x07; // SubscribeAck
849        let entry = EventGroupEntry::new_unchecked(&buffer[..]);
850        assert!(entry.check_entry_type().is_ok());
851
852        // Invalid eventgroup entry types
853        buffer[0] = 0x00; // FindService (service type)
854        let entry = EventGroupEntry::new_unchecked(&buffer[..]);
855        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0x00)));
856
857        buffer[0] = 0x01; // OfferService (service type)
858        let entry = EventGroupEntry::new_unchecked(&buffer[..]);
859        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0x01)));
860
861        buffer[0] = 0xFF; // Unknown type
862        let entry = EventGroupEntry::new_unchecked(&buffer[..]);
863        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0xFF)));
864
865        buffer[0] = 0x99; // Random invalid type
866        let entry = EventGroupEntry::new_unchecked(&buffer[..]);
867        assert_eq!(entry.check_entry_type(), Err(Error::InvalidEntryType(0x99)));
868    }
869}
870
871/// High-level representation of a Service Entry.
872///
873/// This provides a builder-style API for constructing and parsing service entries
874/// without manually managing byte arrays.
875#[derive(Debug, Clone, Copy, PartialEq, Eq)]
876pub struct ServiceEntryRepr {
877    /// Entry type (FindService or OfferService)
878    pub entry_type: EntryType,
879    /// Index of first option run
880    pub index_first_option_run: u8,
881    /// Index of second option run
882    pub index_second_option_run: u8,
883    /// Number of options in both runs
884    pub number_of_options: NumberOfOptions,
885    /// Service ID
886    pub service_id: u16,
887    /// Instance ID
888    pub instance_id: u16,
889    /// Major version
890    pub major_version: u8,
891    /// TTL in seconds (0xFFFFFF = infinite, 0 = stop offer)
892    pub ttl: u32,
893    /// Minor version
894    pub minor_version: u32,
895}
896
897impl ServiceEntryRepr {
898    /// Parse a ServiceEntry into a high-level representation.
899    ///
900    /// # Parameters
901    /// * `entry` - The ServiceEntry to parse
902    ///
903    /// # Returns
904    /// ServiceEntryRepr with all fields populated
905    ///
906    /// # Errors
907    /// Returns Error::InvalidEntryType if entry type is not FindService or OfferService
908    pub fn parse<T: AsRef<[u8]>>(entry: &ServiceEntry<T>) -> Result<Self> {
909        entry.check_entry_type()?;
910        
911        let entry_type = EntryType::from_u8(entry.entry_type())
912            .ok_or(Error::InvalidEntryType(entry.entry_type()))?;
913        
914        if !entry_type.is_service_entry() {
915            return Err(Error::InvalidEntryType(entry.entry_type()));
916        }
917
918        Ok(ServiceEntryRepr {
919            entry_type,
920            index_first_option_run: entry.index_first_option_run(),
921            index_second_option_run: entry.index_second_option_run(),
922            number_of_options: entry.number_of_options(),
923            service_id: entry.service_id(),
924            instance_id: entry.instance_id(),
925            major_version: entry.major_version(),
926            ttl: entry.ttl(),
927            minor_version: entry.minor_version(),
928        })
929    }
930
931    /// Emit this representation into a ServiceEntry buffer.
932    ///
933    /// # Parameters
934    /// * `entry` - Mutable ServiceEntry to write into
935    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, entry: &mut ServiceEntry<T>) {
936        entry.set_entry_type(self.entry_type.as_u8());
937        entry.set_index_first_option_run(self.index_first_option_run);
938        entry.set_index_second_option_run(self.index_second_option_run);
939        entry.set_number_of_options(self.number_of_options);
940        entry.set_service_id(self.service_id);
941        entry.set_instance_id(self.instance_id);
942        entry.set_major_version(self.major_version);
943        entry.set_ttl(self.ttl);
944        entry.set_minor_version(self.minor_version);
945    }
946
947    /// Get the wire format size of this entry (always 16 bytes).
948    pub const fn buffer_len() -> usize {
949        field::service_entry::MINOR_VERSION.end
950    }
951}
952
953/// High-level representation of an EventGroup Entry.
954///
955/// This provides a builder-style API for constructing and parsing eventgroup entries
956/// without manually managing byte arrays.
957#[derive(Debug, Clone, Copy, PartialEq, Eq)]
958pub struct EventGroupEntryRepr {
959    /// Entry type (Subscribe or SubscribeAck)
960    pub entry_type: EntryType,
961    /// Index of first option run
962    pub index_first_option_run: u8,
963    /// Index of second option run
964    pub index_second_option_run: u8,
965    /// Number of options in both runs
966    pub number_of_options: NumberOfOptions,
967    /// Service ID
968    pub service_id: u16,
969    /// Instance ID
970    pub instance_id: u16,
971    /// Major version
972    pub major_version: u8,
973    /// TTL in seconds (0xFFFFFF = infinite, 0 = stop subscribe)
974    pub ttl: u32,
975    /// Reserved and counter field
976    pub reserved_and_counter: ReservedAndCounter,
977    /// EventGroup ID
978    pub eventgroup_id: u16,
979}
980
981impl EventGroupEntryRepr {
982    /// Parse an EventGroupEntry into a high-level representation.
983    ///
984    /// # Parameters
985    /// * `entry` - The EventGroupEntry to parse
986    ///
987    /// # Returns
988    /// EventGroupEntryRepr with all fields populated
989    ///
990    /// # Errors
991    /// Returns Error::InvalidEntryType if entry type is not Subscribe or SubscribeAck
992    pub fn parse<T: AsRef<[u8]>>(entry: &EventGroupEntry<T>) -> Result<Self> {
993        entry.check_entry_type()?;
994        
995        let entry_type = EntryType::from_u8(entry.entry_type())
996            .ok_or(Error::InvalidEntryType(entry.entry_type()))?;
997        
998        if !entry_type.is_eventgroup_entry() {
999            return Err(Error::InvalidEntryType(entry.entry_type()));
1000        }
1001
1002        Ok(EventGroupEntryRepr {
1003            entry_type,
1004            index_first_option_run: entry.index_first_option_run(),
1005            index_second_option_run: entry.index_second_option_run(),
1006            number_of_options: entry.number_of_options(),
1007            service_id: entry.service_id(),
1008            instance_id: entry.instance_id(),
1009            major_version: entry.major_version(),
1010            ttl: entry.ttl(),
1011            reserved_and_counter: entry.reserved_and_counter(),
1012            eventgroup_id: entry.eventgroup_id(),
1013        })
1014    }
1015
1016    /// Emit this representation into an EventGroupEntry buffer.
1017    ///
1018    /// # Parameters
1019    /// * `entry` - Mutable EventGroupEntry to write into
1020    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, entry: &mut EventGroupEntry<T>) {
1021        entry.set_entry_type(self.entry_type.as_u8());
1022        entry.set_index_first_option_run(self.index_first_option_run);
1023        entry.set_index_second_option_run(self.index_second_option_run);
1024        entry.set_number_of_options(self.number_of_options);
1025        entry.set_service_id(self.service_id);
1026        entry.set_instance_id(self.instance_id);
1027        entry.set_major_version(self.major_version);
1028        entry.set_ttl(self.ttl);
1029        entry.set_reserved_and_counter(self.reserved_and_counter);
1030        entry.set_eventgroup_id(self.eventgroup_id);
1031    }
1032
1033    /// Get the wire format size of this entry (always 16 bytes).
1034    pub const fn buffer_len() -> usize {
1035        field::event_group_entry::EVENTGROUP_ID.end
1036    }
1037}