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}