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}