1use crate::format::bytes::read_le_uint as read_uint;
20use crate::format::checksum::{checksum_metadata, jenkins_lookup3};
21use crate::format::messages::{
22 MSG_ATTRIBUTE, MSG_DATASPACE, MSG_DATATYPE, MSG_FILL_VALUE, MSG_FILL_VALUE_OLD,
23 MSG_FILTER_PIPELINE,
24};
25use crate::format::{FormatContext, FormatError, FormatResult};
26
27pub const SMTB_SIGNATURE: [u8; 4] = *b"SMTB";
29pub const SMLI_SIGNATURE: [u8; 4] = *b"SMLI";
31
32pub const SOHM_HEAP_ID_LEN: usize = 8;
35
36pub const SOHM_POINTER_HEAP_ID_AT: usize = 2;
39
40pub const SOHM_IN_HEAP: u8 = 0;
42pub const SOHM_IN_OH: u8 = 1;
44
45pub const SOHM_INDEX_LIST: u8 = 0;
47pub const SOHM_INDEX_BTREE: u8 = 1;
49
50pub const BT2_TYPE_SOHM_INDEX: u8 = 7;
52
53pub const SOHM_B2_NODE_SIZE: u32 = 512;
55
56pub const MAX_SOHM_INDEXES: usize = 8;
58
59pub fn message_hash(body: &[u8], msg_type: u8) -> u32 {
64 jenkins_lookup3(body, u32::from(msg_type))
65}
66
67pub fn record_size(ctx: &FormatContext) -> usize {
70 let sa = ctx.sizeof_addr as usize;
71 1 + 4 + (4 + SOHM_HEAP_ID_LEN).max(1 + 1 + 2 + sa)
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct SohmRecord {
80 pub hash: u32,
82 pub location: SohmRecordLocation,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum SohmRecordLocation {
89 InHeap {
91 ref_count: u32,
95 heap_id: [u8; SOHM_HEAP_ID_LEN],
97 },
98 InObjectHeader {
104 msg_type: u8,
106 index: u16,
111 oh_addr: u64,
113 },
114}
115
116impl SohmRecord {
117 pub fn encode(&self, ctx: &FormatContext) -> Vec<u8> {
119 let size = record_size(ctx);
120 let mut buf = Vec::with_capacity(size);
121 match self.location {
122 SohmRecordLocation::InHeap { ref_count, heap_id } => {
123 buf.push(SOHM_IN_HEAP);
124 buf.extend_from_slice(&self.hash.to_le_bytes());
125 buf.extend_from_slice(&ref_count.to_le_bytes());
126 buf.extend_from_slice(&heap_id);
127 }
128 SohmRecordLocation::InObjectHeader {
129 msg_type,
130 index,
131 oh_addr,
132 } => {
133 buf.push(SOHM_IN_OH);
134 buf.extend_from_slice(&self.hash.to_le_bytes());
135 buf.push(0);
137 buf.push(msg_type);
138 buf.extend_from_slice(&index.to_le_bytes());
139 buf.extend_from_slice(&oh_addr.to_le_bytes()[..ctx.sizeof_addr as usize]);
140 }
141 }
142 buf.resize(size, 0);
143 buf
144 }
145}
146
147pub fn list_size(ctx: &FormatContext, list_max: u16) -> usize {
150 4 + record_size(ctx) * list_max as usize + 4
151}
152
153pub fn encode_list(records: &[SohmRecord], ctx: &FormatContext) -> Vec<u8> {
160 let mut buf = SMLI_SIGNATURE.to_vec();
161 for record in records {
162 buf.extend_from_slice(&record.encode(ctx));
163 }
164 let sum = checksum_metadata(&buf);
165 buf.extend_from_slice(&sum.to_le_bytes());
166 buf
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum SharedLocation {
172 Unshared,
174 Sohm,
176 Committed,
178 Here,
180}
181
182impl SharedLocation {
183 fn from_byte(b: u8) -> Self {
184 match b {
185 1 => Self::Sohm,
186 2 => Self::Committed,
187 3 => Self::Here,
188 _ => Self::Unshared,
189 }
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
196pub struct SharedMessagePointer {
197 pub version: u8,
199 pub location: SharedLocation,
201 pub heap_id: [u8; SOHM_HEAP_ID_LEN],
203 pub oh_addr: u64,
206}
207
208impl SharedMessagePointer {
209 pub fn decode(buf: &[u8], ctx: &FormatContext) -> FormatResult<Self> {
211 let sa = ctx.sizeof_addr as usize;
212 let ss = ctx.sizeof_size as usize;
213 need(buf, 2)?;
214 let version = buf[0];
215 if version == 0 || version > 3 {
216 return Err(FormatError::InvalidVersion(version));
217 }
218
219 let mut location = if version >= 2 {
222 SharedLocation::from_byte(buf[1])
223 } else {
224 SharedLocation::Committed
225 };
226 let mut pos = 2;
227
228 let mut heap_id = [0u8; SOHM_HEAP_ID_LEN];
229 let mut oh_addr = 0u64;
230 if version == 1 {
231 pos += 6;
234 need(buf, pos + ss + sa)?;
235 pos += ss;
236 oh_addr = read_uint(&buf[pos..], sa);
237 } else if location == SharedLocation::Sohm {
238 if version < 3 {
239 return Err(FormatError::InvalidData(
240 "heap-shared message pointer requires version 3".into(),
241 ));
242 }
243 need(buf, pos + SOHM_HEAP_ID_LEN)?;
244 heap_id.copy_from_slice(&buf[pos..pos + SOHM_HEAP_ID_LEN]);
245 } else {
246 if version < 3 {
249 location = SharedLocation::Committed;
250 }
251 need(buf, pos + sa)?;
252 oh_addr = read_uint(&buf[pos..], sa);
253 }
254
255 Ok(Self {
256 version,
257 location,
258 heap_id,
259 oh_addr,
260 })
261 }
262
263 pub fn committed(oh_addr: u64) -> Self {
271 Self {
272 version: 2,
273 location: SharedLocation::Committed,
274 heap_id: [0u8; SOHM_HEAP_ID_LEN],
275 oh_addr,
276 }
277 }
278
279 pub fn encode_committed(oh_addr: u64, ctx: &FormatContext) -> Vec<u8> {
287 let sa = ctx.sizeof_addr as usize;
288 let mut buf = Vec::with_capacity(2 + sa);
289 buf.push(2); buf.push(2); buf.extend_from_slice(&oh_addr.to_le_bytes()[..sa]);
292 buf
293 }
294
295 pub fn encode_sohm(heap_id: [u8; SOHM_HEAP_ID_LEN]) -> Vec<u8> {
303 let mut buf = Vec::with_capacity(2 + SOHM_HEAP_ID_LEN);
304 buf.push(3); buf.push(1); buf.extend_from_slice(&heap_id);
307 buf
308 }
309}
310
311#[derive(Debug, Clone, PartialEq, Eq)]
313pub struct SohmIndexHeader {
314 pub index_type: u8,
317 pub mesg_types: u16,
319 pub min_mesg_size: u32,
321 pub list_max: u16,
323 pub btree_min: u16,
325 pub num_messages: u16,
327 pub index_addr: u64,
329 pub heap_addr: u64,
331}
332
333#[derive(Debug, Clone, Default, PartialEq, Eq)]
335pub struct SohmMasterTable {
336 pub indexes: Vec<SohmIndexHeader>,
338}
339
340const SM_LIST_VERSION: u8 = 0;
342
343impl SohmMasterTable {
344 pub fn encoded_size(ctx: &FormatContext, nindexes: u8) -> usize {
347 let sa = ctx.sizeof_addr as usize;
348 let per_index = 1 + 1 + 2 + 4 + 2 + 2 + 2 + sa + sa;
349 4 + nindexes as usize * per_index + 4
350 }
351
352 pub fn decode(buf: &[u8], ctx: &FormatContext, nindexes: u8) -> FormatResult<Self> {
356 let sa = ctx.sizeof_addr as usize;
357 let size = Self::encoded_size(ctx, nindexes);
358 need(buf, size)?;
359 if buf[0..4] != SMTB_SIGNATURE {
360 return Err(FormatError::InvalidSignature);
361 }
362
363 let stored =
364 u32::from_le_bytes([buf[size - 4], buf[size - 3], buf[size - 2], buf[size - 1]]);
365 let computed = checksum_metadata(&buf[..size - 4]);
366 if stored != computed {
367 return Err(FormatError::ChecksumMismatch {
368 expected: stored,
369 computed,
370 });
371 }
372
373 let mut pos = 4;
374 let mut indexes = Vec::with_capacity(nindexes as usize);
375 for _ in 0..nindexes {
376 let version = buf[pos];
377 if version != SM_LIST_VERSION {
378 return Err(FormatError::InvalidVersion(version));
379 }
380 pos += 1;
381 let index_type = buf[pos];
382 pos += 1;
383 let mesg_types = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
384 pos += 2;
385 let min_mesg_size =
386 u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
387 pos += 4;
388 let list_max = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
389 pos += 2;
390 let btree_min = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
391 pos += 2;
392 let num_messages = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
393 pos += 2;
394 let index_addr = read_uint(&buf[pos..], sa);
395 pos += sa;
396 let heap_addr = read_uint(&buf[pos..], sa);
397 pos += sa;
398 indexes.push(SohmIndexHeader {
399 index_type,
400 mesg_types,
401 min_mesg_size,
402 list_max,
403 btree_min,
404 num_messages,
405 index_addr,
406 heap_addr,
407 });
408 }
409
410 Ok(Self { indexes })
411 }
412
413 pub fn encode(&self, ctx: &FormatContext) -> Vec<u8> {
417 let sa = ctx.sizeof_addr as usize;
418 let mut buf = SMTB_SIGNATURE.to_vec();
419 for index in &self.indexes {
420 buf.push(SM_LIST_VERSION);
421 buf.push(index.index_type);
422 buf.extend_from_slice(&index.mesg_types.to_le_bytes());
423 buf.extend_from_slice(&index.min_mesg_size.to_le_bytes());
424 buf.extend_from_slice(&index.list_max.to_le_bytes());
425 buf.extend_from_slice(&index.btree_min.to_le_bytes());
426 buf.extend_from_slice(&index.num_messages.to_le_bytes());
427 buf.extend_from_slice(&index.index_addr.to_le_bytes()[..sa]);
428 buf.extend_from_slice(&index.heap_addr.to_le_bytes()[..sa]);
429 }
430 let sum = checksum_metadata(&buf);
431 buf.extend_from_slice(&sum.to_le_bytes());
432 buf
433 }
434
435 pub fn heap_addr(&self, msg_type: u8) -> Option<u64> {
438 let flag = type_flag(msg_type)?;
439 self.indexes
440 .iter()
441 .find(|i| i.mesg_types & flag != 0)
442 .map(|i| i.heap_addr)
443 }
444}
445
446pub fn type_flag(msg_type: u8) -> Option<u16> {
450 let id = match msg_type {
451 MSG_DATASPACE | MSG_DATATYPE | MSG_FILL_VALUE | MSG_FILTER_PIPELINE | MSG_ATTRIBUTE => {
452 msg_type
453 }
454 MSG_FILL_VALUE_OLD => MSG_FILL_VALUE,
455 _ => return None,
456 };
457 Some(1u16 << id)
458}
459
460fn need(buf: &[u8], n: usize) -> FormatResult<()> {
461 if buf.len() < n {
462 Err(FormatError::BufferTooShort {
463 needed: n,
464 available: buf.len(),
465 })
466 } else {
467 Ok(())
468 }
469}
470
471#[cfg(test)]
474mod tests {
475 use super::*;
476
477 fn ctx() -> FormatContext {
478 FormatContext::default_v3()
479 }
480
481 fn index_header(mesg_types: u16, heap_addr: u64) -> Vec<u8> {
482 let mut b = vec![SM_LIST_VERSION, 0];
483 b.extend_from_slice(&mesg_types.to_le_bytes());
484 b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&50u16.to_le_bytes()); b.extend_from_slice(&40u16.to_le_bytes()); b.extend_from_slice(&7u16.to_le_bytes()); b.extend_from_slice(&0x1234u64.to_le_bytes()); b.extend_from_slice(&heap_addr.to_le_bytes());
490 b
491 }
492
493 fn master_table(headers: &[Vec<u8>]) -> Vec<u8> {
494 let mut b = SMTB_SIGNATURE.to_vec();
495 for h in headers {
496 b.extend_from_slice(h);
497 }
498 let sum = checksum_metadata(&b);
499 b.extend_from_slice(&sum.to_le_bytes());
500 b
501 }
502
503 #[test]
504 fn master_table_roundtrip() {
505 let buf = master_table(&[index_header(1 << MSG_DATATYPE, 0x8000)]);
506 assert_eq!(buf.len(), SohmMasterTable::encoded_size(&ctx(), 1));
507 let t = SohmMasterTable::decode(&buf, &ctx(), 1).unwrap();
508 assert_eq!(t.indexes.len(), 1);
509 assert_eq!(t.indexes[0].num_messages, 7);
510 assert_eq!(t.indexes[0].heap_addr, 0x8000);
511 }
512
513 #[test]
514 fn master_table_rejects_a_corrupt_checksum() {
515 let mut buf = master_table(&[index_header(1 << MSG_DATATYPE, 0x8000)]);
516 let n = buf.len();
517 buf[n - 1] ^= 0xff;
518 assert!(matches!(
519 SohmMasterTable::decode(&buf, &ctx(), 1).unwrap_err(),
520 FormatError::ChecksumMismatch { .. }
521 ));
522 }
523
524 #[test]
525 fn master_table_rejects_a_bad_index_version() {
526 let mut hdr = index_header(1 << MSG_DATATYPE, 0x8000);
527 hdr[0] = 1;
528 let buf = master_table(&[hdr]);
529 assert!(matches!(
530 SohmMasterTable::decode(&buf, &ctx(), 1).unwrap_err(),
531 FormatError::InvalidVersion(1)
532 ));
533 }
534
535 #[test]
538 fn heap_address_is_selected_by_message_type() {
539 let buf = master_table(&[
540 index_header(1 << MSG_ATTRIBUTE, 0x1000),
541 index_header((1 << MSG_DATATYPE) | (1 << MSG_DATASPACE), 0x2000),
542 ]);
543 let t = SohmMasterTable::decode(&buf, &ctx(), 2).unwrap();
544 assert_eq!(t.heap_addr(MSG_ATTRIBUTE), Some(0x1000));
545 assert_eq!(t.heap_addr(MSG_DATATYPE), Some(0x2000));
546 assert_eq!(t.heap_addr(MSG_DATASPACE), Some(0x2000));
547 assert_eq!(t.heap_addr(crate::format::messages::MSG_DATA_LAYOUT), None);
549 }
550
551 #[test]
554 fn old_fill_value_shares_the_new_fill_value_bit() {
555 assert_eq!(type_flag(MSG_FILL_VALUE_OLD), type_flag(MSG_FILL_VALUE));
556 assert_eq!(type_flag(MSG_FILL_VALUE), Some(1 << MSG_FILL_VALUE));
557 assert_eq!(type_flag(crate::format::messages::MSG_LINK), None);
558 }
559
560 #[test]
564 fn committed_pointer_encodes_the_bytes_h5o_shared_encode_writes() {
565 let buf = SharedMessagePointer::encode_committed(0x320, &ctx());
566 let mut want = vec![2u8, 2u8];
567 want.extend_from_slice(&0x320u64.to_le_bytes());
568 assert_eq!(buf, want);
569 assert_eq!(
570 SharedMessagePointer::decode(&buf, &ctx()).unwrap(),
571 SharedMessagePointer::committed(0x320)
572 );
573 }
574
575 #[test]
577 fn committed_pointer_follows_the_address_width() {
578 let ctx4 = FormatContext {
579 sizeof_addr: 4,
580 sizeof_size: 4,
581 };
582 let buf = SharedMessagePointer::encode_committed(0x1234, &ctx4);
583 assert_eq!(buf, vec![2u8, 2, 0x34, 0x12, 0, 0]);
584 assert_eq!(
585 SharedMessagePointer::decode(&buf, &ctx4).unwrap().oh_addr,
586 0x1234
587 );
588 }
589
590 #[test]
591 fn version_three_heap_pointer_carries_a_heap_id() {
592 let mut buf = vec![3u8, 1u8];
593 buf.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
594 let p = SharedMessagePointer::decode(&buf, &ctx()).unwrap();
595 assert_eq!(p.location, SharedLocation::Sohm);
596 assert_eq!(p.heap_id, [1, 2, 3, 4, 5, 6, 7, 8]);
597 }
598
599 #[test]
600 fn version_three_committed_pointer_carries_an_address() {
601 let mut buf = vec![3u8, 2u8];
602 buf.extend_from_slice(&0x4321u64.to_le_bytes());
603 let p = SharedMessagePointer::decode(&buf, &ctx()).unwrap();
604 assert_eq!(p.location, SharedLocation::Committed);
605 assert_eq!(p.oh_addr, 0x4321);
606 }
607
608 #[test]
611 fn version_one_pointer_skips_the_symbol_table_entry_prefix() {
612 let mut buf = vec![1u8, 0u8];
613 buf.extend_from_slice(&[0u8; 6]);
614 buf.extend_from_slice(&0xdeadu64.to_le_bytes()); buf.extend_from_slice(&0x9999u64.to_le_bytes());
616 let p = SharedMessagePointer::decode(&buf, &ctx()).unwrap();
617 assert_eq!(p.location, SharedLocation::Committed);
618 assert_eq!(p.oh_addr, 0x9999);
619 }
620
621 #[test]
624 fn a_libhdf5_committed_datatype_reference_names_its_object_header() {
625 let buf = [0x02, 0x02, 0x20, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
626 let p = SharedMessagePointer::decode(&buf, &ctx()).unwrap();
627 assert_eq!(p.location, SharedLocation::Committed);
628 assert_eq!(p.oh_addr, 800);
629 }
630
631 #[test]
634 fn version_two_non_heap_pointer_is_committed() {
635 let mut buf = vec![2u8, 0u8];
636 buf.extend_from_slice(&0x77u64.to_le_bytes());
637 let p = SharedMessagePointer::decode(&buf, &ctx()).unwrap();
638 assert_eq!(p.location, SharedLocation::Committed);
639 assert_eq!(p.oh_addr, 0x77);
640 }
641
642 #[test]
643 fn heap_pointer_before_version_three_is_rejected() {
644 let mut buf = vec![2u8, 1u8];
645 buf.extend_from_slice(&[0u8; 8]);
646 assert!(matches!(
647 SharedMessagePointer::decode(&buf, &ctx()).unwrap_err(),
648 FormatError::InvalidData(_)
649 ));
650 }
651
652 #[test]
653 fn pointer_rejects_unknown_versions() {
654 assert!(matches!(
655 SharedMessagePointer::decode(&[0u8, 1u8], &ctx()).unwrap_err(),
656 FormatError::InvalidVersion(0)
657 ));
658 assert!(matches!(
659 SharedMessagePointer::decode(&[4u8, 1u8], &ctx()).unwrap_err(),
660 FormatError::InvalidVersion(4)
661 ));
662 }
663
664 #[test]
669 fn message_hash_matches_the_fixture_records() {
670 let sdspace = [
672 1u8, 1, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
673 ];
674 assert_eq!(message_hash(&sdspace, MSG_DATASPACE), 701521455);
675 let dtype = [
677 0x11u8, 0x20, 0x3f, 0x00, 8, 0, 0, 0, 0, 0, 0x40, 0x00, 0x34, 0x0b, 0x00, 0x34, 0xff,
678 0x03, 0x00, 0x00,
679 ];
680 assert_eq!(message_hash(&dtype, MSG_DATATYPE), 3573483313);
681 assert_ne!(
682 message_hash(&sdspace, MSG_DATASPACE),
683 message_hash(&sdspace, MSG_DATATYPE)
684 );
685 }
686
687 #[test]
688 fn record_is_seventeen_bytes_for_eight_byte_addresses() {
689 assert_eq!(record_size(&ctx()), 17);
690 assert_eq!(
691 record_size(&FormatContext {
692 sizeof_addr: 4,
693 sizeof_size: 4
694 }),
695 17
696 );
697 }
698
699 #[test]
702 fn list_index_encodes_the_fixture_image() {
703 let heaped = |hash, ref_count, heap_id| SohmRecord {
704 hash,
705 location: SohmRecordLocation::InHeap { ref_count, heap_id },
706 };
707 let records = [
708 heaped(701521455, 5, [0x00, 0x42, 0, 0, 0, 0, 0x18, 0x00]),
709 heaped(3573483313, 1, [0x00, 0x16, 0, 0, 0, 0, 0x14, 0x00]),
710 heaped(826238635, 1, [0x00, 0x2a, 0, 0, 0, 0, 0x18, 0x00]),
711 heaped(2575530442, 4, [0x00, 0x7a, 0, 0, 0, 0, 0x38, 0x00]),
712 ];
713 let image = encode_list(&records, &ctx());
714 let want = concat!(
715 "534d4c49",
716 "002f5ed029050000000042000000001800",
717 "003107ffd4010000000016000000001400",
718 "00ab663f3101000000002a000000001800",
719 "00ca79839904000000007a000000003800",
720 "cbfec07c",
721 );
722 assert_eq!(hex(&image), want);
723 assert_eq!(list_size(&ctx(), 50), 858);
726 assert!(image.len() < list_size(&ctx(), 50));
727 }
728
729 #[test]
733 fn an_object_header_record_names_the_header_holding_the_body() {
734 let record = SohmRecord {
735 hash: 701521455,
736 location: SohmRecordLocation::InObjectHeader {
737 msg_type: MSG_DATASPACE,
738 index: 0,
739 oh_addr: 0x0349,
740 },
741 };
742 assert_eq!(
743 hex(&record.encode(&ctx())),
744 concat!(
745 "01", "2f5ed029", "00", "01", "0000", "4903000000000000", )
752 );
753 assert_eq!(record.encode(&ctx()).len(), record_size(&ctx()));
754 }
755
756 fn hex(bytes: &[u8]) -> String {
757 bytes.iter().map(|b| format!("{b:02x}")).collect()
758 }
759
760 #[test]
763 fn master_table_encodes_what_decode_reads_back() {
764 let table = SohmMasterTable {
765 indexes: vec![SohmIndexHeader {
766 index_type: SOHM_INDEX_LIST,
767 mesg_types: (1 << MSG_DATASPACE) | (1 << MSG_DATATYPE) | (1 << MSG_ATTRIBUTE),
768 min_mesg_size: 0,
769 list_max: 50,
770 btree_min: 40,
771 num_messages: 4,
772 index_addr: 1125,
773 heap_addr: 1983,
774 }],
775 };
776 let image = table.encode(&ctx());
777 assert_eq!(image.len(), SohmMasterTable::encoded_size(&ctx(), 1));
778 assert_eq!(&image[..4], &SMTB_SIGNATURE);
779 assert_eq!(
780 u16::from_le_bytes([image[6], image[7]]),
781 0x100a,
782 "the type mask libhdf5 wrote for DTYPE|SDSPACE|ATTR"
783 );
784 assert_eq!(SohmMasterTable::decode(&image, &ctx(), 1).unwrap(), table);
785 }
786
787 #[test]
788 fn heap_pointer_is_a_version_three_message() {
789 let id = [0x00, 0x7a, 0, 0, 0, 0, 0x38, 0x00];
790 let buf = SharedMessagePointer::encode_sohm(id);
791 assert_eq!(hex(&buf), "0301007a000000003800");
792 let p = SharedMessagePointer::decode(&buf, &ctx()).unwrap();
793 assert_eq!(p.location, SharedLocation::Sohm);
794 assert_eq!(p.heap_id, id);
795 }
796
797 #[test]
798 fn pointer_rejects_a_truncated_body() {
799 assert!(matches!(
800 SharedMessagePointer::decode(&[3u8, 1u8, 0, 0], &ctx()).unwrap_err(),
801 FormatError::BufferTooShort { .. }
802 ));
803 }
804}