Skip to main content

nnrp_core/
object.rs

1use crate::{CommonHeader, MessageType, NnrpError};
2
3pub const OBJECT_DESCRIPTOR_METADATA_LEN: usize = 48;
4pub const OBJECT_REFERENCE_METADATA_LEN: usize = 48;
5pub const OBJECT_RELEASE_METADATA_LEN: usize = 32;
6pub const OBJECT_DELTA_METADATA_LEN: usize = 40;
7pub const CACHE_REFERENCE_METADATA_LEN: usize = 56;
8pub const CACHE_MISS_METADATA_LEN: usize = 32;
9
10pub const OBJECT_REFERENCE_FLAGS_KNOWN_MASK: u32 = 0x0000_0007;
11pub const OBJECT_RELEASE_FLAGS_KNOWN_MASK: u8 = 0x03;
12pub const OBJECT_DELTA_FLAGS_KNOWN_MASK: u32 = 0x0000_0007;
13pub const CACHE_REFERENCE_FLAGS_KNOWN_MASK: u32 = 0x0000_0003;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(u16)]
17pub enum RuntimeObjectKind {
18    Unspecified = 0x0000,
19    Tensor = 0x0001,
20    TokenBlock = 0x0002,
21    ImageTile = 0x0003,
22    FeatureMap = 0x0004,
23    ToolResult = 0x0005,
24    TraceSegment = 0x0006,
25    OpaqueBytes = 0x0007,
26    DocumentChunk = 0x0008,
27    AudioChunk = 0x0009,
28    VideoChunk = 0x000a,
29    RoutePlan = 0x000b,
30    CacheManifest = 0x000c,
31}
32
33impl RuntimeObjectKind {
34    pub fn try_from_u16(value: u16) -> Result<Self, NnrpError> {
35        match value {
36            0x0000 => Ok(Self::Unspecified),
37            0x0001 => Ok(Self::Tensor),
38            0x0002 => Ok(Self::TokenBlock),
39            0x0003 => Ok(Self::ImageTile),
40            0x0004 => Ok(Self::FeatureMap),
41            0x0005 => Ok(Self::ToolResult),
42            0x0006 => Ok(Self::TraceSegment),
43            0x0007 => Ok(Self::OpaqueBytes),
44            0x0008 => Ok(Self::DocumentChunk),
45            0x0009 => Ok(Self::AudioChunk),
46            0x000a => Ok(Self::VideoChunk),
47            0x000b => Ok(Self::RoutePlan),
48            0x000c => Ok(Self::CacheManifest),
49            _ => Err(NnrpError::UnknownEnumValue {
50                enum_name: "runtime_object_kind",
51                value: value as u64,
52            }),
53        }
54    }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58#[repr(u8)]
59pub enum RuntimeRole {
60    Unspecified = 0x00,
61    Client = 0x01,
62    Server = 0x02,
63    Runtime = 0x03,
64    Subagent = 0x04,
65    Tool = 0x05,
66    Scheduler = 0x06,
67    ConformanceRunner = 0x07,
68}
69
70impl RuntimeRole {
71    pub fn try_from_u8(value: u8) -> Result<Self, NnrpError> {
72        match value {
73            0x00 => Ok(Self::Unspecified),
74            0x01 => Ok(Self::Client),
75            0x02 => Ok(Self::Server),
76            0x03 => Ok(Self::Runtime),
77            0x04 => Ok(Self::Subagent),
78            0x05 => Ok(Self::Tool),
79            0x06 => Ok(Self::Scheduler),
80            0x07 => Ok(Self::ConformanceRunner),
81            _ => Err(NnrpError::UnknownEnumValue {
82                enum_name: "runtime_role",
83                value: value as u64,
84            }),
85        }
86    }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[repr(u16)]
91pub enum MemoryLocationHint {
92    Unspecified = 0x0000,
93    HostMemory = 0x0001,
94    DeviceMemory = 0x0002,
95    SharedMemory = 0x0003,
96    RemoteMemory = 0x0004,
97    MmapFile = 0x0005,
98    ObjectStore = 0x0006,
99}
100
101impl MemoryLocationHint {
102    pub fn try_from_u16(value: u16) -> Result<Self, NnrpError> {
103        match value {
104            0x0000 => Ok(Self::Unspecified),
105            0x0001 => Ok(Self::HostMemory),
106            0x0002 => Ok(Self::DeviceMemory),
107            0x0003 => Ok(Self::SharedMemory),
108            0x0004 => Ok(Self::RemoteMemory),
109            0x0005 => Ok(Self::MmapFile),
110            0x0006 => Ok(Self::ObjectStore),
111            _ => Err(NnrpError::UnknownEnumValue {
112                enum_name: "memory_location_hint",
113                value: value as u64,
114            }),
115        }
116    }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120#[repr(u16)]
121pub enum OwnershipHint {
122    Unspecified = 0x0000,
123    ProducerOwned = 0x0001,
124    ConsumerOwned = 0x0002,
125    SessionOwned = 0x0003,
126    Borrowed = 0x0004,
127    TransferOnRef = 0x0005,
128    ReleaseOnDrop = 0x0006,
129}
130
131impl OwnershipHint {
132    pub fn try_from_u16(value: u16) -> Result<Self, NnrpError> {
133        match value {
134            0x0000 => Ok(Self::Unspecified),
135            0x0001 => Ok(Self::ProducerOwned),
136            0x0002 => Ok(Self::ConsumerOwned),
137            0x0003 => Ok(Self::SessionOwned),
138            0x0004 => Ok(Self::Borrowed),
139            0x0005 => Ok(Self::TransferOnRef),
140            0x0006 => Ok(Self::ReleaseOnDrop),
141            _ => Err(NnrpError::UnknownEnumValue {
142                enum_name: "ownership_hint",
143                value: value as u64,
144            }),
145        }
146    }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150#[repr(u16)]
151pub enum ObjectReleaseReason {
152    Completed = 0x0000,
153    Cancelled = 0x0001,
154    Expired = 0x0002,
155    Replaced = 0x0003,
156    Invalidated = 0x0004,
157    OwnerClosed = 0x0005,
158    LeaseExpired = 0x0006,
159    ConformanceInjection = 0x0007,
160}
161
162impl ObjectReleaseReason {
163    pub fn try_from_u16(value: u16) -> Result<Self, NnrpError> {
164        match value {
165            0x0000 => Ok(Self::Completed),
166            0x0001 => Ok(Self::Cancelled),
167            0x0002 => Ok(Self::Expired),
168            0x0003 => Ok(Self::Replaced),
169            0x0004 => Ok(Self::Invalidated),
170            0x0005 => Ok(Self::OwnerClosed),
171            0x0006 => Ok(Self::LeaseExpired),
172            0x0007 => Ok(Self::ConformanceInjection),
173            _ => Err(NnrpError::UnknownEnumValue {
174                enum_name: "object_release_reason",
175                value: value as u64,
176            }),
177        }
178    }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182#[repr(u16)]
183pub enum CacheReuseScope {
184    Operation = 0x0000,
185    Session = 0x0001,
186    Connection = 0x0002,
187    Global = 0x0003,
188    Tenant = 0x0004,
189    Profile = 0x0005,
190}
191
192impl CacheReuseScope {
193    pub fn try_from_u16(value: u16) -> Result<Self, NnrpError> {
194        match value {
195            0x0000 => Ok(Self::Operation),
196            0x0001 => Ok(Self::Session),
197            0x0002 => Ok(Self::Connection),
198            0x0003 => Ok(Self::Global),
199            0x0004 => Ok(Self::Tenant),
200            0x0005 => Ok(Self::Profile),
201            _ => Err(NnrpError::UnknownEnumValue {
202                enum_name: "cache_reuse_scope",
203                value: value as u64,
204            }),
205        }
206    }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210#[repr(u16)]
211pub enum CacheMissReason {
212    Unknown = 0x0000,
213    NotFound = 0x0001,
214    Expired = 0x0002,
215    Invalidated = 0x0003,
216    SchemaMismatch = 0x0004,
217    ProducerUnavailable = 0x0005,
218    LeaseRequired = 0x0006,
219    PermissionDenied = 0x0007,
220}
221
222impl CacheMissReason {
223    pub fn try_from_u16(value: u16) -> Result<Self, NnrpError> {
224        match value {
225            0x0000 => Ok(Self::Unknown),
226            0x0001 => Ok(Self::NotFound),
227            0x0002 => Ok(Self::Expired),
228            0x0003 => Ok(Self::Invalidated),
229            0x0004 => Ok(Self::SchemaMismatch),
230            0x0005 => Ok(Self::ProducerUnavailable),
231            0x0006 => Ok(Self::LeaseRequired),
232            0x0007 => Ok(Self::PermissionDenied),
233            _ => Err(NnrpError::UnknownEnumValue {
234                enum_name: "cache_miss_reason",
235                value: value as u64,
236            }),
237        }
238    }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub struct ObjectDescriptorMetadata {
243    pub object_id: u64,
244    pub object_kind: RuntimeObjectKind,
245    pub producer_role: RuntimeRole,
246    pub consumer_role: RuntimeRole,
247    pub session_id: u32,
248    pub byte_size: u64,
249    pub compute_cost_units: u32,
250    pub memory_location_hint: MemoryLocationHint,
251    pub ownership_hint: OwnershipHint,
252    pub lifetime_hint_ms: u32,
253    pub metadata_bytes: u32,
254}
255
256impl ObjectDescriptorMetadata {
257    pub fn parse(source: &[u8]) -> Result<Self, NnrpError> {
258        require_len(source, OBJECT_DESCRIPTOR_METADATA_LEN)?;
259        validate_zero_u64("object_descriptor.reserved", read_u64(source, 40))?;
260        Ok(Self {
261            object_id: read_u64(source, 0),
262            object_kind: RuntimeObjectKind::try_from_u16(read_u16(source, 8))?,
263            producer_role: RuntimeRole::try_from_u8(source[10])?,
264            consumer_role: RuntimeRole::try_from_u8(source[11])?,
265            session_id: read_u32(source, 12),
266            byte_size: read_u64(source, 16),
267            compute_cost_units: read_u32(source, 24),
268            memory_location_hint: MemoryLocationHint::try_from_u16(read_u16(source, 28))?,
269            ownership_hint: OwnershipHint::try_from_u16(read_u16(source, 30))?,
270            lifetime_hint_ms: read_u32(source, 32),
271            metadata_bytes: read_u32(source, 36),
272        })
273    }
274
275    pub fn write(&self, destination: &mut [u8]) -> Result<(), NnrpError> {
276        require_destination_len(destination, OBJECT_DESCRIPTOR_METADATA_LEN)?;
277        destination[..OBJECT_DESCRIPTOR_METADATA_LEN].fill(0);
278        write_u64(destination, 0, self.object_id);
279        write_u16(destination, 8, self.object_kind as u16);
280        destination[10] = self.producer_role as u8;
281        destination[11] = self.consumer_role as u8;
282        write_u32(destination, 12, self.session_id);
283        write_u64(destination, 16, self.byte_size);
284        write_u32(destination, 24, self.compute_cost_units);
285        write_u16(destination, 28, self.memory_location_hint as u16);
286        write_u16(destination, 30, self.ownership_hint as u16);
287        write_u32(destination, 32, self.lifetime_hint_ms);
288        write_u32(destination, 36, self.metadata_bytes);
289        Ok(())
290    }
291
292    pub fn to_bytes(&self) -> Result<[u8; OBJECT_DESCRIPTOR_METADATA_LEN], NnrpError> {
293        let mut bytes = [0u8; OBJECT_DESCRIPTOR_METADATA_LEN];
294        self.write(&mut bytes)?;
295        Ok(bytes)
296    }
297
298    pub fn parse_with_extension(source: &[u8]) -> Result<(Self, &[u8]), NnrpError> {
299        let metadata = Self::parse(source)?;
300        let extension = split_declared_tail(
301            source,
302            OBJECT_DESCRIPTOR_METADATA_LEN,
303            metadata.metadata_bytes as usize,
304            "object_descriptor.metadata_bytes",
305        )?;
306        Ok((metadata, extension))
307    }
308
309    pub fn to_vec_with_extension(&self, extension: &[u8]) -> Result<Vec<u8>, NnrpError> {
310        require_declared_len(
311            "object_descriptor.metadata_bytes",
312            self.metadata_bytes as usize,
313            extension.len(),
314        )?;
315        let mut bytes = self.to_bytes()?.to_vec();
316        bytes.extend_from_slice(extension);
317        Ok(bytes)
318    }
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322pub struct ObjectReferenceMetadata {
323    pub object_id: u64,
324    pub operation_id: u64,
325    pub object_version: u64,
326    pub offset: u64,
327    pub length: u64,
328    pub flags: u32,
329    pub metadata_bytes: u32,
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub struct ObjectReferencePacket<'a> {
334    pub header: CommonHeader,
335    pub metadata: ObjectReferenceMetadata,
336    pub extension_metadata: &'a [u8],
337}
338
339impl ObjectReferenceMetadata {
340    pub fn parse(source: &[u8]) -> Result<Self, NnrpError> {
341        require_len(source, OBJECT_REFERENCE_METADATA_LEN)?;
342        let flags = read_u32(source, 40);
343        validate_mask_u32(flags, OBJECT_REFERENCE_FLAGS_KNOWN_MASK)?;
344        Ok(Self {
345            object_id: read_u64(source, 0),
346            operation_id: read_u64(source, 8),
347            object_version: read_u64(source, 16),
348            offset: read_u64(source, 24),
349            length: read_u64(source, 32),
350            flags,
351            metadata_bytes: read_u32(source, 44),
352        })
353    }
354
355    pub fn write(&self, destination: &mut [u8]) -> Result<(), NnrpError> {
356        require_destination_len(destination, OBJECT_REFERENCE_METADATA_LEN)?;
357        validate_mask_u32(self.flags, OBJECT_REFERENCE_FLAGS_KNOWN_MASK)?;
358        write_u64(destination, 0, self.object_id);
359        write_u64(destination, 8, self.operation_id);
360        write_u64(destination, 16, self.object_version);
361        write_u64(destination, 24, self.offset);
362        write_u64(destination, 32, self.length);
363        write_u32(destination, 40, self.flags);
364        write_u32(destination, 44, self.metadata_bytes);
365        Ok(())
366    }
367
368    pub fn to_bytes(&self) -> Result<[u8; OBJECT_REFERENCE_METADATA_LEN], NnrpError> {
369        let mut bytes = [0u8; OBJECT_REFERENCE_METADATA_LEN];
370        self.write(&mut bytes)?;
371        Ok(bytes)
372    }
373
374    pub fn parse_with_extension(source: &[u8]) -> Result<(Self, &[u8]), NnrpError> {
375        let metadata = Self::parse(source)?;
376        let extension = split_declared_tail(
377            source,
378            OBJECT_REFERENCE_METADATA_LEN,
379            metadata.metadata_bytes as usize,
380            "object_reference.metadata_bytes",
381        )?;
382        Ok((metadata, extension))
383    }
384
385    pub fn to_vec_with_extension(&self, extension: &[u8]) -> Result<Vec<u8>, NnrpError> {
386        require_declared_len(
387            "object_reference.metadata_bytes",
388            self.metadata_bytes as usize,
389            extension.len(),
390        )?;
391        let mut bytes = self.to_bytes()?.to_vec();
392        bytes.extend_from_slice(extension);
393        Ok(bytes)
394    }
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq)]
398pub struct ObjectReleaseMetadata {
399    pub object_id: u64,
400    pub operation_id: u64,
401    pub release_reason: ObjectReleaseReason,
402    pub source_role: RuntimeRole,
403    pub flags: u8,
404    pub diagnostic_bytes: u32,
405}
406
407impl ObjectReleaseMetadata {
408    pub fn parse(source: &[u8]) -> Result<Self, NnrpError> {
409        require_len(source, OBJECT_RELEASE_METADATA_LEN)?;
410        let flags = source[19];
411        validate_mask_u8(flags, OBJECT_RELEASE_FLAGS_KNOWN_MASK)?;
412        validate_zero_u64("object_release.reserved", read_u64(source, 24))?;
413        Ok(Self {
414            object_id: read_u64(source, 0),
415            operation_id: read_u64(source, 8),
416            release_reason: ObjectReleaseReason::try_from_u16(read_u16(source, 16))?,
417            source_role: RuntimeRole::try_from_u8(source[18])?,
418            flags,
419            diagnostic_bytes: read_u32(source, 20),
420        })
421    }
422
423    pub fn write(&self, destination: &mut [u8]) -> Result<(), NnrpError> {
424        require_destination_len(destination, OBJECT_RELEASE_METADATA_LEN)?;
425        validate_mask_u8(self.flags, OBJECT_RELEASE_FLAGS_KNOWN_MASK)?;
426        destination[..OBJECT_RELEASE_METADATA_LEN].fill(0);
427        write_u64(destination, 0, self.object_id);
428        write_u64(destination, 8, self.operation_id);
429        write_u16(destination, 16, self.release_reason as u16);
430        destination[18] = self.source_role as u8;
431        destination[19] = self.flags;
432        write_u32(destination, 20, self.diagnostic_bytes);
433        Ok(())
434    }
435
436    pub fn to_bytes(&self) -> Result<[u8; OBJECT_RELEASE_METADATA_LEN], NnrpError> {
437        let mut bytes = [0u8; OBJECT_RELEASE_METADATA_LEN];
438        self.write(&mut bytes)?;
439        Ok(bytes)
440    }
441
442    pub fn parse_with_diagnostics(source: &[u8]) -> Result<(Self, &[u8]), NnrpError> {
443        let metadata = Self::parse(source)?;
444        let diagnostics = split_declared_tail(
445            source,
446            OBJECT_RELEASE_METADATA_LEN,
447            metadata.diagnostic_bytes as usize,
448            "object_release.diagnostic_bytes",
449        )?;
450        Ok((metadata, diagnostics))
451    }
452
453    pub fn to_vec_with_diagnostics(&self, diagnostics: &[u8]) -> Result<Vec<u8>, NnrpError> {
454        require_declared_len(
455            "object_release.diagnostic_bytes",
456            self.diagnostic_bytes as usize,
457            diagnostics.len(),
458        )?;
459        let mut bytes = self.to_bytes()?.to_vec();
460        bytes.extend_from_slice(diagnostics);
461        Ok(bytes)
462    }
463}
464
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub struct ObjectDeltaMetadata {
467    pub object_id: u64,
468    pub delta_sequence: u64,
469    pub region_offset: u64,
470    pub region_bytes: u32,
471    pub delta_bytes: u32,
472    pub flags: u32,
473    pub metadata_bytes: u32,
474}
475
476#[derive(Debug, Clone, Copy, PartialEq, Eq)]
477pub struct ObjectDeltaPacket<'a> {
478    pub header: CommonHeader,
479    pub metadata: ObjectDeltaMetadata,
480    pub extension_metadata: &'a [u8],
481    pub delta_payload: &'a [u8],
482}
483
484impl ObjectDeltaMetadata {
485    pub fn parse(source: &[u8]) -> Result<Self, NnrpError> {
486        require_len(source, OBJECT_DELTA_METADATA_LEN)?;
487        let flags = read_u32(source, 32);
488        validate_mask_u32(flags, OBJECT_DELTA_FLAGS_KNOWN_MASK)?;
489        Ok(Self {
490            object_id: read_u64(source, 0),
491            delta_sequence: read_u64(source, 8),
492            region_offset: read_u64(source, 16),
493            region_bytes: read_u32(source, 24),
494            delta_bytes: read_u32(source, 28),
495            flags,
496            metadata_bytes: read_u32(source, 36),
497        })
498    }
499
500    pub fn write(&self, destination: &mut [u8]) -> Result<(), NnrpError> {
501        require_destination_len(destination, OBJECT_DELTA_METADATA_LEN)?;
502        validate_mask_u32(self.flags, OBJECT_DELTA_FLAGS_KNOWN_MASK)?;
503        write_u64(destination, 0, self.object_id);
504        write_u64(destination, 8, self.delta_sequence);
505        write_u64(destination, 16, self.region_offset);
506        write_u32(destination, 24, self.region_bytes);
507        write_u32(destination, 28, self.delta_bytes);
508        write_u32(destination, 32, self.flags);
509        write_u32(destination, 36, self.metadata_bytes);
510        Ok(())
511    }
512
513    pub fn to_bytes(&self) -> Result<[u8; OBJECT_DELTA_METADATA_LEN], NnrpError> {
514        let mut bytes = [0u8; OBJECT_DELTA_METADATA_LEN];
515        self.write(&mut bytes)?;
516        Ok(bytes)
517    }
518
519    pub fn parse_with_extension(source: &[u8]) -> Result<(Self, &[u8]), NnrpError> {
520        let metadata = Self::parse(source)?;
521        let extension = split_declared_tail(
522            source,
523            OBJECT_DELTA_METADATA_LEN,
524            metadata.metadata_bytes as usize,
525            "object_delta.metadata_bytes",
526        )?;
527        Ok((metadata, extension))
528    }
529
530    pub fn to_vec_with_extension(&self, extension: &[u8]) -> Result<Vec<u8>, NnrpError> {
531        require_declared_len(
532            "object_delta.metadata_bytes",
533            self.metadata_bytes as usize,
534            extension.len(),
535        )?;
536        let mut bytes = self.to_bytes()?.to_vec();
537        bytes.extend_from_slice(extension);
538        Ok(bytes)
539    }
540}
541
542pub fn parse_object_reference_packet(
543    source: &[u8],
544) -> Result<ObjectReferencePacket<'_>, NnrpError> {
545    let (header, metadata_bytes, body) = CommonHeader::parse_packet(source)?;
546    require_message_type(header.message_type, MessageType::ObjectRef)?;
547    require_declared_len("object_reference.body_len", 0, body.len())?;
548    let (metadata, extension_metadata) =
549        ObjectReferenceMetadata::parse_with_extension(metadata_bytes)?;
550    Ok(ObjectReferencePacket {
551        header,
552        metadata,
553        extension_metadata,
554    })
555}
556
557pub fn object_reference_packet_bytes(
558    metadata: &ObjectReferenceMetadata,
559    extension_metadata: &[u8],
560) -> Result<Vec<u8>, NnrpError> {
561    let metadata_bytes = metadata.to_vec_with_extension(extension_metadata)?;
562    let header = CommonHeader::new(
563        MessageType::ObjectRef,
564        checked_u32_len(metadata_bytes.len())?,
565        0,
566    );
567    let mut packet = header.to_bytes()?.to_vec();
568    packet.extend_from_slice(&metadata_bytes);
569    Ok(packet)
570}
571
572pub fn parse_object_delta_packet(source: &[u8]) -> Result<ObjectDeltaPacket<'_>, NnrpError> {
573    let (header, metadata_bytes, body) = CommonHeader::parse_packet(source)?;
574    require_message_type(header.message_type, MessageType::ObjectDelta)?;
575    let (metadata, extension_metadata) = ObjectDeltaMetadata::parse_with_extension(metadata_bytes)?;
576    require_declared_len(
577        "object_delta.delta_bytes",
578        metadata.delta_bytes as usize,
579        body.len(),
580    )?;
581    Ok(ObjectDeltaPacket {
582        header,
583        metadata,
584        extension_metadata,
585        delta_payload: body,
586    })
587}
588
589pub fn object_delta_packet_bytes(
590    metadata: &ObjectDeltaMetadata,
591    extension_metadata: &[u8],
592    delta_payload: &[u8],
593) -> Result<Vec<u8>, NnrpError> {
594    require_declared_len(
595        "object_delta.delta_bytes",
596        metadata.delta_bytes as usize,
597        delta_payload.len(),
598    )?;
599    let metadata_bytes = metadata.to_vec_with_extension(extension_metadata)?;
600    let header = CommonHeader::new(
601        MessageType::ObjectDelta,
602        checked_u32_len(metadata_bytes.len())?,
603        checked_u32_len(delta_payload.len())?,
604    );
605    let mut packet = header.to_bytes()?.to_vec();
606    packet.extend_from_slice(&metadata_bytes);
607    packet.extend_from_slice(delta_payload);
608    Ok(packet)
609}
610
611#[derive(Debug, Clone, Copy, PartialEq, Eq)]
612pub struct CacheReferenceMetadata {
613    pub cache_namespace: u32,
614    pub cache_key_hi: u64,
615    pub cache_key_lo: u64,
616    pub profile_id: u16,
617    pub reuse_scope: CacheReuseScope,
618    pub lease_id: u64,
619    pub producer_trace_id: u64,
620    pub expiration_hint_ms: u32,
621    pub metadata_bytes: u32,
622    pub flags: u32,
623}
624
625impl CacheReferenceMetadata {
626    pub fn parse(source: &[u8]) -> Result<Self, NnrpError> {
627        require_len(source, CACHE_REFERENCE_METADATA_LEN)?;
628        let flags = read_u32(source, 48);
629        validate_mask_u32(flags, CACHE_REFERENCE_FLAGS_KNOWN_MASK)?;
630        validate_zero_u32("cache_reference.reserved", read_u32(source, 52))?;
631        Ok(Self {
632            cache_namespace: read_u32(source, 0),
633            profile_id: read_u16(source, 4),
634            reuse_scope: CacheReuseScope::try_from_u16(read_u16(source, 6))?,
635            cache_key_hi: read_u64(source, 8),
636            cache_key_lo: read_u64(source, 16),
637            lease_id: read_u64(source, 24),
638            producer_trace_id: read_u64(source, 32),
639            expiration_hint_ms: read_u32(source, 40),
640            metadata_bytes: read_u32(source, 44),
641            flags,
642        })
643    }
644
645    pub fn write(&self, destination: &mut [u8]) -> Result<(), NnrpError> {
646        require_destination_len(destination, CACHE_REFERENCE_METADATA_LEN)?;
647        validate_mask_u32(self.flags, CACHE_REFERENCE_FLAGS_KNOWN_MASK)?;
648        destination[..CACHE_REFERENCE_METADATA_LEN].fill(0);
649        write_u32(destination, 0, self.cache_namespace);
650        write_u16(destination, 4, self.profile_id);
651        write_u16(destination, 6, self.reuse_scope as u16);
652        write_u64(destination, 8, self.cache_key_hi);
653        write_u64(destination, 16, self.cache_key_lo);
654        write_u64(destination, 24, self.lease_id);
655        write_u64(destination, 32, self.producer_trace_id);
656        write_u32(destination, 40, self.expiration_hint_ms);
657        write_u32(destination, 44, self.metadata_bytes);
658        write_u32(destination, 48, self.flags);
659        Ok(())
660    }
661
662    pub fn to_bytes(&self) -> Result<[u8; CACHE_REFERENCE_METADATA_LEN], NnrpError> {
663        let mut bytes = [0u8; CACHE_REFERENCE_METADATA_LEN];
664        self.write(&mut bytes)?;
665        Ok(bytes)
666    }
667
668    pub fn parse_with_extension(source: &[u8]) -> Result<(Self, &[u8]), NnrpError> {
669        let metadata = Self::parse(source)?;
670        let extension = split_declared_tail(
671            source,
672            CACHE_REFERENCE_METADATA_LEN,
673            metadata.metadata_bytes as usize,
674            "cache_reference.metadata_bytes",
675        )?;
676        Ok((metadata, extension))
677    }
678
679    pub fn to_vec_with_extension(&self, extension: &[u8]) -> Result<Vec<u8>, NnrpError> {
680        require_declared_len(
681            "cache_reference.metadata_bytes",
682            self.metadata_bytes as usize,
683            extension.len(),
684        )?;
685        let mut bytes = self.to_bytes()?.to_vec();
686        bytes.extend_from_slice(extension);
687        Ok(bytes)
688    }
689}
690
691#[derive(Debug, Clone, Copy, PartialEq, Eq)]
692pub struct CacheMissMetadata {
693    pub cache_namespace: u32,
694    pub cache_key_hi: u64,
695    pub cache_key_lo: u64,
696    pub miss_reason: CacheMissReason,
697    pub profile_id: u16,
698    pub diagnostic_bytes: u32,
699}
700
701impl CacheMissMetadata {
702    pub fn parse(source: &[u8]) -> Result<Self, NnrpError> {
703        require_len(source, CACHE_MISS_METADATA_LEN)?;
704        validate_zero_u32("cache_miss.reserved", read_u32(source, 28))?;
705        Ok(Self {
706            cache_namespace: read_u32(source, 0),
707            profile_id: read_u16(source, 4),
708            miss_reason: CacheMissReason::try_from_u16(read_u16(source, 6))?,
709            cache_key_hi: read_u64(source, 8),
710            cache_key_lo: read_u64(source, 16),
711            diagnostic_bytes: read_u32(source, 24),
712        })
713    }
714
715    pub fn write(&self, destination: &mut [u8]) -> Result<(), NnrpError> {
716        require_destination_len(destination, CACHE_MISS_METADATA_LEN)?;
717        destination[..CACHE_MISS_METADATA_LEN].fill(0);
718        write_u32(destination, 0, self.cache_namespace);
719        write_u16(destination, 4, self.profile_id);
720        write_u16(destination, 6, self.miss_reason as u16);
721        write_u64(destination, 8, self.cache_key_hi);
722        write_u64(destination, 16, self.cache_key_lo);
723        write_u32(destination, 24, self.diagnostic_bytes);
724        Ok(())
725    }
726
727    pub fn to_bytes(&self) -> Result<[u8; CACHE_MISS_METADATA_LEN], NnrpError> {
728        let mut bytes = [0u8; CACHE_MISS_METADATA_LEN];
729        self.write(&mut bytes)?;
730        Ok(bytes)
731    }
732
733    pub fn parse_with_diagnostics(source: &[u8]) -> Result<(Self, &[u8]), NnrpError> {
734        let metadata = Self::parse(source)?;
735        let diagnostics = split_declared_tail(
736            source,
737            CACHE_MISS_METADATA_LEN,
738            metadata.diagnostic_bytes as usize,
739            "cache_miss.diagnostic_bytes",
740        )?;
741        Ok((metadata, diagnostics))
742    }
743
744    pub fn to_vec_with_diagnostics(&self, diagnostics: &[u8]) -> Result<Vec<u8>, NnrpError> {
745        require_declared_len(
746            "cache_miss.diagnostic_bytes",
747            self.diagnostic_bytes as usize,
748            diagnostics.len(),
749        )?;
750        let mut bytes = self.to_bytes()?.to_vec();
751        bytes.extend_from_slice(diagnostics);
752        Ok(bytes)
753    }
754}
755
756fn require_len(source: &[u8], expected: usize) -> Result<(), NnrpError> {
757    if source.len() < expected {
758        return Err(NnrpError::SourceTooShort {
759            expected,
760            actual: source.len(),
761        });
762    }
763    Ok(())
764}
765
766fn require_destination_len(destination: &[u8], expected: usize) -> Result<(), NnrpError> {
767    if destination.len() < expected {
768        return Err(NnrpError::DestinationTooShort {
769            expected,
770            actual: destination.len(),
771        });
772    }
773    Ok(())
774}
775
776fn require_declared_len(
777    field: &'static str,
778    declared: usize,
779    actual: usize,
780) -> Result<(), NnrpError> {
781    if declared != actual {
782        return Err(NnrpError::DeclaredLengthMismatch {
783            field,
784            declared,
785            actual,
786        });
787    }
788    Ok(())
789}
790
791fn require_message_type(actual: MessageType, expected: MessageType) -> Result<(), NnrpError> {
792    if actual != expected {
793        return Err(NnrpError::InvalidProtocolCombination {
794            rule: "object packet message type does not match parser",
795        });
796    }
797    Ok(())
798}
799
800fn checked_u32_len(value: usize) -> Result<u32, NnrpError> {
801    u32::try_from(value).map_err(|_| NnrpError::MessageLengthOverflow)
802}
803
804fn split_declared_tail<'a>(
805    source: &'a [u8],
806    fixed_len: usize,
807    declared_tail_len: usize,
808    field: &'static str,
809) -> Result<&'a [u8], NnrpError> {
810    require_len(source, fixed_len)?;
811    let actual_tail_len = source.len() - fixed_len;
812    require_declared_len(field, declared_tail_len, actual_tail_len)?;
813    Ok(&source[fixed_len..])
814}
815
816fn validate_mask_u8(value: u8, allowed: u8) -> Result<(), NnrpError> {
817    if value & !allowed != 0 {
818        return Err(NnrpError::ReservedBitsSet {
819            value: value as u64,
820            allowed: allowed as u64,
821        });
822    }
823    Ok(())
824}
825
826fn validate_mask_u32(value: u32, allowed: u32) -> Result<(), NnrpError> {
827    if value & !allowed != 0 {
828        return Err(NnrpError::ReservedBitsSet {
829            value: value as u64,
830            allowed: allowed as u64,
831        });
832    }
833    Ok(())
834}
835
836fn validate_zero_u64(field: &'static str, value: u64) -> Result<(), NnrpError> {
837    if value != 0 {
838        return Err(NnrpError::NonZeroReservedField { field });
839    }
840    Ok(())
841}
842
843fn validate_zero_u32(field: &'static str, value: u32) -> Result<(), NnrpError> {
844    if value != 0 {
845        return Err(NnrpError::NonZeroReservedField { field });
846    }
847    Ok(())
848}
849
850fn read_u16(source: &[u8], offset: usize) -> u16 {
851    u16::from_le_bytes(source[offset..offset + 2].try_into().expect("slice length"))
852}
853
854fn read_u32(source: &[u8], offset: usize) -> u32 {
855    u32::from_le_bytes(source[offset..offset + 4].try_into().expect("slice length"))
856}
857
858fn read_u64(source: &[u8], offset: usize) -> u64 {
859    u64::from_le_bytes(source[offset..offset + 8].try_into().expect("slice length"))
860}
861
862fn write_u16(destination: &mut [u8], offset: usize, value: u16) {
863    destination[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
864}
865
866fn write_u32(destination: &mut [u8], offset: usize, value: u32) {
867    destination[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
868}
869
870fn write_u64(destination: &mut [u8], offset: usize, value: u64) {
871    destination[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
872}
873
874#[cfg(test)]
875mod tests {
876    use super::*;
877
878    #[test]
879    fn object_and_cache_metadata_round_trip_fixed_layouts() {
880        let descriptor = ObjectDescriptorMetadata {
881            object_id: 1,
882            object_kind: RuntimeObjectKind::Tensor,
883            producer_role: RuntimeRole::Runtime,
884            consumer_role: RuntimeRole::Client,
885            session_id: 2,
886            byte_size: 4096,
887            compute_cost_units: 7,
888            memory_location_hint: MemoryLocationHint::DeviceMemory,
889            ownership_hint: OwnershipHint::Borrowed,
890            lifetime_hint_ms: 500,
891            metadata_bytes: 12,
892        };
893        assert_eq!(
894            ObjectDescriptorMetadata::parse(&descriptor.to_bytes().unwrap()).unwrap(),
895            descriptor
896        );
897
898        let reference = ObjectReferenceMetadata {
899            object_id: 1,
900            operation_id: 3,
901            object_version: 4,
902            offset: 8,
903            length: 16,
904            flags: OBJECT_REFERENCE_FLAGS_KNOWN_MASK,
905            metadata_bytes: 20,
906        };
907        assert_eq!(
908            ObjectReferenceMetadata::parse(&reference.to_bytes().unwrap()).unwrap(),
909            reference
910        );
911
912        let release = ObjectReleaseMetadata {
913            object_id: 1,
914            operation_id: 3,
915            release_reason: ObjectReleaseReason::Cancelled,
916            source_role: RuntimeRole::Scheduler,
917            flags: OBJECT_RELEASE_FLAGS_KNOWN_MASK,
918            diagnostic_bytes: 8,
919        };
920        assert_eq!(
921            ObjectReleaseMetadata::parse(&release.to_bytes().unwrap()).unwrap(),
922            release
923        );
924
925        let delta = ObjectDeltaMetadata {
926            object_id: 1,
927            delta_sequence: 2,
928            region_offset: 64,
929            region_bytes: 32,
930            delta_bytes: 16,
931            flags: OBJECT_DELTA_FLAGS_KNOWN_MASK,
932            metadata_bytes: 4,
933        };
934        assert_eq!(
935            ObjectDeltaMetadata::parse(&delta.to_bytes().unwrap()).unwrap(),
936            delta
937        );
938
939        let cache_ref = CacheReferenceMetadata {
940            cache_namespace: 7,
941            cache_key_hi: 0x1122_3344_5566_7788,
942            cache_key_lo: 0x99aa_bbcc_ddee_ff00,
943            profile_id: 3,
944            reuse_scope: CacheReuseScope::Session,
945            lease_id: 5,
946            producer_trace_id: 6,
947            expiration_hint_ms: 700,
948            metadata_bytes: 24,
949            flags: CACHE_REFERENCE_FLAGS_KNOWN_MASK,
950        };
951        assert_eq!(
952            CacheReferenceMetadata::parse(&cache_ref.to_bytes().unwrap()).unwrap(),
953            cache_ref
954        );
955
956        let miss = CacheMissMetadata {
957            cache_namespace: 7,
958            cache_key_hi: 0x1122_3344_5566_7788,
959            cache_key_lo: 0x99aa_bbcc_ddee_ff00,
960            miss_reason: CacheMissReason::SchemaMismatch,
961            profile_id: 3,
962            diagnostic_bytes: 9,
963        };
964        assert_eq!(
965            CacheMissMetadata::parse(&miss.to_bytes().unwrap()).unwrap(),
966            miss
967        );
968    }
969
970    #[test]
971    fn object_and_cache_metadata_round_trips_declared_extension_segments() {
972        let descriptor = ObjectDescriptorMetadata {
973            object_id: 1,
974            object_kind: RuntimeObjectKind::Tensor,
975            producer_role: RuntimeRole::Runtime,
976            consumer_role: RuntimeRole::Client,
977            session_id: 2,
978            byte_size: 4096,
979            compute_cost_units: 7,
980            memory_location_hint: MemoryLocationHint::DeviceMemory,
981            ownership_hint: OwnershipHint::Borrowed,
982            lifetime_hint_ms: 500,
983            metadata_bytes: 3,
984        };
985        let descriptor_bytes = descriptor.to_vec_with_extension(&[1, 2, 3]).unwrap();
986        assert_eq!(
987            ObjectDescriptorMetadata::parse_with_extension(&descriptor_bytes).unwrap(),
988            (descriptor, &[1, 2, 3][..])
989        );
990
991        let reference = ObjectReferenceMetadata {
992            object_id: 1,
993            operation_id: 3,
994            object_version: 4,
995            offset: 8,
996            length: 16,
997            flags: OBJECT_REFERENCE_FLAGS_KNOWN_MASK,
998            metadata_bytes: 2,
999        };
1000        let reference_bytes = reference.to_vec_with_extension(&[9, 8]).unwrap();
1001        assert_eq!(
1002            ObjectReferenceMetadata::parse_with_extension(&reference_bytes).unwrap(),
1003            (reference, &[9, 8][..])
1004        );
1005
1006        let delta = ObjectDeltaMetadata {
1007            object_id: 1,
1008            delta_sequence: 2,
1009            region_offset: 64,
1010            region_bytes: 32,
1011            delta_bytes: 16,
1012            flags: OBJECT_DELTA_FLAGS_KNOWN_MASK,
1013            metadata_bytes: 4,
1014        };
1015        let delta_bytes = delta.to_vec_with_extension(&[4, 3, 2, 1]).unwrap();
1016        assert_eq!(
1017            ObjectDeltaMetadata::parse_with_extension(&delta_bytes).unwrap(),
1018            (delta, &[4, 3, 2, 1][..])
1019        );
1020
1021        let release = ObjectReleaseMetadata {
1022            object_id: 1,
1023            operation_id: 3,
1024            release_reason: ObjectReleaseReason::Cancelled,
1025            source_role: RuntimeRole::Scheduler,
1026            flags: OBJECT_RELEASE_FLAGS_KNOWN_MASK,
1027            diagnostic_bytes: 3,
1028        };
1029        let release_bytes = release.to_vec_with_diagnostics(&[7, 8, 9]).unwrap();
1030        assert_eq!(
1031            ObjectReleaseMetadata::parse_with_diagnostics(&release_bytes).unwrap(),
1032            (release, &[7, 8, 9][..])
1033        );
1034
1035        let cache_ref = CacheReferenceMetadata {
1036            cache_namespace: 7,
1037            cache_key_hi: 0x1122,
1038            cache_key_lo: 0x3344,
1039            profile_id: 3,
1040            reuse_scope: CacheReuseScope::Session,
1041            lease_id: 5,
1042            producer_trace_id: 6,
1043            expiration_hint_ms: 700,
1044            metadata_bytes: 2,
1045            flags: CACHE_REFERENCE_FLAGS_KNOWN_MASK,
1046        };
1047        let cache_ref_bytes = cache_ref.to_vec_with_extension(&[5, 6]).unwrap();
1048        assert_eq!(
1049            CacheReferenceMetadata::parse_with_extension(&cache_ref_bytes).unwrap(),
1050            (cache_ref, &[5, 6][..])
1051        );
1052
1053        let miss = CacheMissMetadata {
1054            cache_namespace: 7,
1055            cache_key_hi: 0x1122,
1056            cache_key_lo: 0x3344,
1057            miss_reason: CacheMissReason::SchemaMismatch,
1058            profile_id: 3,
1059            diagnostic_bytes: 3,
1060        };
1061        let miss_bytes = miss.to_vec_with_diagnostics(&[1, 3, 5]).unwrap();
1062        assert_eq!(
1063            CacheMissMetadata::parse_with_diagnostics(&miss_bytes).unwrap(),
1064            (miss, &[1, 3, 5][..])
1065        );
1066    }
1067
1068    #[test]
1069    fn object_reference_and_delta_packets_borrow_payload_boundaries() {
1070        let reference = ObjectReferenceMetadata {
1071            object_id: 1,
1072            operation_id: 3,
1073            object_version: 4,
1074            offset: 8,
1075            length: 16,
1076            flags: OBJECT_REFERENCE_FLAGS_KNOWN_MASK,
1077            metadata_bytes: 2,
1078        };
1079        let reference_packet = object_reference_packet_bytes(&reference, &[9, 8]).unwrap();
1080        let parsed_reference = parse_object_reference_packet(&reference_packet).unwrap();
1081        assert_eq!(parsed_reference.header.message_type, MessageType::ObjectRef);
1082        assert_eq!(parsed_reference.metadata, reference);
1083        assert_eq!(parsed_reference.extension_metadata, &[9, 8]);
1084
1085        let delta = ObjectDeltaMetadata {
1086            object_id: 1,
1087            delta_sequence: 2,
1088            region_offset: 64,
1089            region_bytes: 32,
1090            delta_bytes: 3,
1091            flags: OBJECT_DELTA_FLAGS_KNOWN_MASK,
1092            metadata_bytes: 2,
1093        };
1094        let delta_packet = object_delta_packet_bytes(&delta, &[4, 5], &[7, 8, 9]).unwrap();
1095        let parsed_delta = parse_object_delta_packet(&delta_packet).unwrap();
1096        assert_eq!(parsed_delta.header.message_type, MessageType::ObjectDelta);
1097        assert_eq!(parsed_delta.metadata, delta);
1098        assert_eq!(parsed_delta.extension_metadata, &[4, 5]);
1099        assert_eq!(parsed_delta.delta_payload, &[7, 8, 9]);
1100    }
1101
1102    #[test]
1103    fn object_and_cache_metadata_reject_reserved_bits_and_fields() {
1104        assert_eq!(
1105            ObjectReferenceMetadata {
1106                object_id: 1,
1107                operation_id: 0,
1108                object_version: 0,
1109                offset: 0,
1110                length: 0,
1111                flags: 0x08,
1112                metadata_bytes: 0,
1113            }
1114            .to_bytes(),
1115            Err(NnrpError::ReservedBitsSet {
1116                value: 0x08,
1117                allowed: OBJECT_REFERENCE_FLAGS_KNOWN_MASK as u64
1118            })
1119        );
1120
1121        let mut release = ObjectReleaseMetadata {
1122            object_id: 1,
1123            operation_id: 0,
1124            release_reason: ObjectReleaseReason::Completed,
1125            source_role: RuntimeRole::Runtime,
1126            flags: 0,
1127            diagnostic_bytes: 0,
1128        }
1129        .to_bytes()
1130        .unwrap();
1131        write_u64(&mut release, 24, 1);
1132        assert_eq!(
1133            ObjectReleaseMetadata::parse(&release),
1134            Err(NnrpError::NonZeroReservedField {
1135                field: "object_release.reserved"
1136            })
1137        );
1138
1139        assert_eq!(
1140            ObjectDeltaMetadata {
1141                object_id: 1,
1142                delta_sequence: 1,
1143                region_offset: 0,
1144                region_bytes: 1,
1145                delta_bytes: 1,
1146                flags: 0x08,
1147                metadata_bytes: 0,
1148            }
1149            .to_bytes(),
1150            Err(NnrpError::ReservedBitsSet {
1151                value: 0x08,
1152                allowed: OBJECT_DELTA_FLAGS_KNOWN_MASK as u64
1153            })
1154        );
1155
1156        assert_eq!(
1157            CacheReferenceMetadata {
1158                cache_namespace: 7,
1159                cache_key_hi: 1,
1160                cache_key_lo: 2,
1161                profile_id: 3,
1162                reuse_scope: CacheReuseScope::Operation,
1163                lease_id: 0,
1164                producer_trace_id: 0,
1165                expiration_hint_ms: 0,
1166                metadata_bytes: 0,
1167                flags: 0x04,
1168            }
1169            .to_bytes(),
1170            Err(NnrpError::ReservedBitsSet {
1171                value: 0x04,
1172                allowed: CACHE_REFERENCE_FLAGS_KNOWN_MASK as u64
1173            })
1174        );
1175
1176        let mut miss = CacheMissMetadata {
1177            cache_namespace: 7,
1178            cache_key_hi: 1,
1179            cache_key_lo: 2,
1180            miss_reason: CacheMissReason::NotFound,
1181            profile_id: 3,
1182            diagnostic_bytes: 0,
1183        }
1184        .to_bytes()
1185        .unwrap();
1186        write_u32(&mut miss, 28, 1);
1187        assert_eq!(
1188            CacheMissMetadata::parse(&miss),
1189            Err(NnrpError::NonZeroReservedField {
1190                field: "cache_miss.reserved"
1191            })
1192        );
1193    }
1194
1195    #[test]
1196    fn object_reference_and_delta_packets_reject_mismatched_payload_boundaries() {
1197        let reference = ObjectReferenceMetadata {
1198            object_id: 1,
1199            operation_id: 3,
1200            object_version: 4,
1201            offset: 8,
1202            length: 16,
1203            flags: OBJECT_REFERENCE_FLAGS_KNOWN_MASK,
1204            metadata_bytes: 0,
1205        };
1206        let reference_metadata = reference.to_bytes().unwrap();
1207        let mut bad_reference_header = CommonHeader::new(
1208            MessageType::ObjectRef,
1209            OBJECT_REFERENCE_METADATA_LEN as u32,
1210            1,
1211        );
1212        bad_reference_header.trace_id = 0x77;
1213        let mut bad_reference = bad_reference_header.to_bytes().unwrap().to_vec();
1214        bad_reference.extend_from_slice(&reference_metadata);
1215        bad_reference.push(0xaa);
1216        assert_eq!(
1217            parse_object_reference_packet(&bad_reference),
1218            Err(NnrpError::DeclaredLengthMismatch {
1219                field: "object_reference.body_len",
1220                declared: 0,
1221                actual: 1
1222            })
1223        );
1224
1225        let delta = ObjectDeltaMetadata {
1226            object_id: 1,
1227            delta_sequence: 2,
1228            region_offset: 64,
1229            region_bytes: 32,
1230            delta_bytes: 2,
1231            flags: 0,
1232            metadata_bytes: 0,
1233        };
1234        assert_eq!(
1235            object_delta_packet_bytes(&delta, &[], &[1, 2, 3]),
1236            Err(NnrpError::DeclaredLengthMismatch {
1237                field: "object_delta.delta_bytes",
1238                declared: 2,
1239                actual: 3
1240            })
1241        );
1242
1243        let wrong_type_header =
1244            CommonHeader::new(MessageType::ObjectRef, OBJECT_DELTA_METADATA_LEN as u32, 2);
1245        let mut wrong_type_packet = wrong_type_header.to_bytes().unwrap().to_vec();
1246        wrong_type_packet.extend_from_slice(&delta.to_bytes().unwrap());
1247        wrong_type_packet.extend_from_slice(&[1, 2]);
1248        assert_eq!(
1249            parse_object_delta_packet(&wrong_type_packet),
1250            Err(NnrpError::InvalidProtocolCombination {
1251                rule: "object packet message type does not match parser"
1252            })
1253        );
1254    }
1255
1256    #[test]
1257    fn object_and_cache_metadata_reject_declared_extension_length_mismatch() {
1258        let descriptor = ObjectDescriptorMetadata {
1259            object_id: 1,
1260            object_kind: RuntimeObjectKind::Tensor,
1261            producer_role: RuntimeRole::Runtime,
1262            consumer_role: RuntimeRole::Client,
1263            session_id: 2,
1264            byte_size: 4096,
1265            compute_cost_units: 7,
1266            memory_location_hint: MemoryLocationHint::DeviceMemory,
1267            ownership_hint: OwnershipHint::Borrowed,
1268            lifetime_hint_ms: 500,
1269            metadata_bytes: 2,
1270        };
1271        assert_eq!(
1272            descriptor.to_vec_with_extension(&[1, 2, 3]),
1273            Err(NnrpError::DeclaredLengthMismatch {
1274                field: "object_descriptor.metadata_bytes",
1275                declared: 2,
1276                actual: 3
1277            })
1278        );
1279
1280        let mut descriptor_bytes = descriptor.to_bytes().unwrap().to_vec();
1281        descriptor_bytes.extend_from_slice(&[1]);
1282        assert_eq!(
1283            ObjectDescriptorMetadata::parse_with_extension(&descriptor_bytes),
1284            Err(NnrpError::DeclaredLengthMismatch {
1285                field: "object_descriptor.metadata_bytes",
1286                declared: 2,
1287                actual: 1
1288            })
1289        );
1290
1291        let miss = CacheMissMetadata {
1292            cache_namespace: 7,
1293            cache_key_hi: 0x1122,
1294            cache_key_lo: 0x3344,
1295            miss_reason: CacheMissReason::SchemaMismatch,
1296            profile_id: 3,
1297            diagnostic_bytes: 1,
1298        };
1299        assert_eq!(
1300            miss.to_vec_with_diagnostics(&[]),
1301            Err(NnrpError::DeclaredLengthMismatch {
1302                field: "cache_miss.diagnostic_bytes",
1303                declared: 1,
1304                actual: 0
1305            })
1306        );
1307    }
1308
1309    #[test]
1310    fn object_and_cache_metadata_reject_short_buffers_and_destinations() {
1311        assert_eq!(
1312            ObjectDescriptorMetadata::parse(&[0; OBJECT_DESCRIPTOR_METADATA_LEN - 1]),
1313            Err(NnrpError::SourceTooShort {
1314                expected: OBJECT_DESCRIPTOR_METADATA_LEN,
1315                actual: OBJECT_DESCRIPTOR_METADATA_LEN - 1
1316            })
1317        );
1318        assert_eq!(
1319            ObjectReferenceMetadata::parse(&[0; OBJECT_REFERENCE_METADATA_LEN - 1]),
1320            Err(NnrpError::SourceTooShort {
1321                expected: OBJECT_REFERENCE_METADATA_LEN,
1322                actual: OBJECT_REFERENCE_METADATA_LEN - 1
1323            })
1324        );
1325
1326        let descriptor = ObjectDescriptorMetadata {
1327            object_id: 1,
1328            object_kind: RuntimeObjectKind::Tensor,
1329            producer_role: RuntimeRole::Runtime,
1330            consumer_role: RuntimeRole::Client,
1331            session_id: 2,
1332            byte_size: 4096,
1333            compute_cost_units: 7,
1334            memory_location_hint: MemoryLocationHint::DeviceMemory,
1335            ownership_hint: OwnershipHint::Borrowed,
1336            lifetime_hint_ms: 500,
1337            metadata_bytes: 12,
1338        };
1339        let mut descriptor_destination = [0u8; OBJECT_DESCRIPTOR_METADATA_LEN - 1];
1340        assert_eq!(
1341            descriptor.write(&mut descriptor_destination),
1342            Err(NnrpError::DestinationTooShort {
1343                expected: OBJECT_DESCRIPTOR_METADATA_LEN,
1344                actual: OBJECT_DESCRIPTOR_METADATA_LEN - 1
1345            })
1346        );
1347
1348        let cache_miss = CacheMissMetadata {
1349            cache_namespace: 7,
1350            cache_key_hi: 1,
1351            cache_key_lo: 2,
1352            miss_reason: CacheMissReason::NotFound,
1353            profile_id: 3,
1354            diagnostic_bytes: 4,
1355        };
1356        let mut cache_miss_destination = [0u8; CACHE_MISS_METADATA_LEN - 1];
1357        assert_eq!(
1358            cache_miss.write(&mut cache_miss_destination),
1359            Err(NnrpError::DestinationTooShort {
1360                expected: CACHE_MISS_METADATA_LEN,
1361                actual: CACHE_MISS_METADATA_LEN - 1
1362            })
1363        );
1364    }
1365
1366    #[test]
1367    fn object_and_cache_registries_accept_standard_values() {
1368        assert_eq!(
1369            (0x0000..=0x000c)
1370                .map(RuntimeObjectKind::try_from_u16)
1371                .collect::<Result<Vec<_>, _>>()
1372                .unwrap(),
1373            vec![
1374                RuntimeObjectKind::Unspecified,
1375                RuntimeObjectKind::Tensor,
1376                RuntimeObjectKind::TokenBlock,
1377                RuntimeObjectKind::ImageTile,
1378                RuntimeObjectKind::FeatureMap,
1379                RuntimeObjectKind::ToolResult,
1380                RuntimeObjectKind::TraceSegment,
1381                RuntimeObjectKind::OpaqueBytes,
1382                RuntimeObjectKind::DocumentChunk,
1383                RuntimeObjectKind::AudioChunk,
1384                RuntimeObjectKind::VideoChunk,
1385                RuntimeObjectKind::RoutePlan,
1386                RuntimeObjectKind::CacheManifest,
1387            ]
1388        );
1389        assert_eq!(
1390            (0x00..=0x07)
1391                .map(RuntimeRole::try_from_u8)
1392                .collect::<Result<Vec<_>, _>>()
1393                .unwrap(),
1394            vec![
1395                RuntimeRole::Unspecified,
1396                RuntimeRole::Client,
1397                RuntimeRole::Server,
1398                RuntimeRole::Runtime,
1399                RuntimeRole::Subagent,
1400                RuntimeRole::Tool,
1401                RuntimeRole::Scheduler,
1402                RuntimeRole::ConformanceRunner,
1403            ]
1404        );
1405        assert_eq!(
1406            (0x0000..=0x0006)
1407                .map(MemoryLocationHint::try_from_u16)
1408                .collect::<Result<Vec<_>, _>>()
1409                .unwrap(),
1410            vec![
1411                MemoryLocationHint::Unspecified,
1412                MemoryLocationHint::HostMemory,
1413                MemoryLocationHint::DeviceMemory,
1414                MemoryLocationHint::SharedMemory,
1415                MemoryLocationHint::RemoteMemory,
1416                MemoryLocationHint::MmapFile,
1417                MemoryLocationHint::ObjectStore,
1418            ]
1419        );
1420        assert_eq!(
1421            (0x0000..=0x0006)
1422                .map(OwnershipHint::try_from_u16)
1423                .collect::<Result<Vec<_>, _>>()
1424                .unwrap(),
1425            vec![
1426                OwnershipHint::Unspecified,
1427                OwnershipHint::ProducerOwned,
1428                OwnershipHint::ConsumerOwned,
1429                OwnershipHint::SessionOwned,
1430                OwnershipHint::Borrowed,
1431                OwnershipHint::TransferOnRef,
1432                OwnershipHint::ReleaseOnDrop,
1433            ]
1434        );
1435        assert_eq!(
1436            (0x0000..=0x0007)
1437                .map(ObjectReleaseReason::try_from_u16)
1438                .collect::<Result<Vec<_>, _>>()
1439                .unwrap(),
1440            vec![
1441                ObjectReleaseReason::Completed,
1442                ObjectReleaseReason::Cancelled,
1443                ObjectReleaseReason::Expired,
1444                ObjectReleaseReason::Replaced,
1445                ObjectReleaseReason::Invalidated,
1446                ObjectReleaseReason::OwnerClosed,
1447                ObjectReleaseReason::LeaseExpired,
1448                ObjectReleaseReason::ConformanceInjection,
1449            ]
1450        );
1451        assert_eq!(
1452            (0x0000..=0x0005)
1453                .map(CacheReuseScope::try_from_u16)
1454                .collect::<Result<Vec<_>, _>>()
1455                .unwrap(),
1456            vec![
1457                CacheReuseScope::Operation,
1458                CacheReuseScope::Session,
1459                CacheReuseScope::Connection,
1460                CacheReuseScope::Global,
1461                CacheReuseScope::Tenant,
1462                CacheReuseScope::Profile,
1463            ]
1464        );
1465        assert_eq!(
1466            (0x0000..=0x0007)
1467                .map(CacheMissReason::try_from_u16)
1468                .collect::<Result<Vec<_>, _>>()
1469                .unwrap(),
1470            vec![
1471                CacheMissReason::Unknown,
1472                CacheMissReason::NotFound,
1473                CacheMissReason::Expired,
1474                CacheMissReason::Invalidated,
1475                CacheMissReason::SchemaMismatch,
1476                CacheMissReason::ProducerUnavailable,
1477                CacheMissReason::LeaseRequired,
1478                CacheMissReason::PermissionDenied,
1479            ]
1480        );
1481    }
1482
1483    #[test]
1484    fn object_and_cache_registries_reject_reserved_standard_values() {
1485        assert_eq!(
1486            RuntimeObjectKind::try_from_u16(0x000d),
1487            Err(NnrpError::UnknownEnumValue {
1488                enum_name: "runtime_object_kind",
1489                value: 0x000d
1490            })
1491        );
1492        assert_eq!(
1493            RuntimeRole::try_from_u8(0x08),
1494            Err(NnrpError::UnknownEnumValue {
1495                enum_name: "runtime_role",
1496                value: 0x08
1497            })
1498        );
1499        assert_eq!(
1500            MemoryLocationHint::try_from_u16(0x0007),
1501            Err(NnrpError::UnknownEnumValue {
1502                enum_name: "memory_location_hint",
1503                value: 0x0007
1504            })
1505        );
1506        assert_eq!(
1507            OwnershipHint::try_from_u16(0x0007),
1508            Err(NnrpError::UnknownEnumValue {
1509                enum_name: "ownership_hint",
1510                value: 0x0007
1511            })
1512        );
1513        assert_eq!(
1514            ObjectReleaseReason::try_from_u16(0x0008),
1515            Err(NnrpError::UnknownEnumValue {
1516                enum_name: "object_release_reason",
1517                value: 0x0008
1518            })
1519        );
1520        assert_eq!(
1521            CacheReuseScope::try_from_u16(0x0006),
1522            Err(NnrpError::UnknownEnumValue {
1523                enum_name: "cache_reuse_scope",
1524                value: 0x0006
1525            })
1526        );
1527        assert_eq!(
1528            CacheMissReason::try_from_u16(0x0008),
1529            Err(NnrpError::UnknownEnumValue {
1530                enum_name: "cache_miss_reason",
1531                value: 0x0008
1532            })
1533        );
1534    }
1535}