1#[cfg(not(feature = "std"))]
20use alloc::vec::Vec;
21
22use crate::btree_v2::{BTreeV2Header, collect_btree_v2_records};
23use crate::error::FormatError;
24use crate::fractal_heap::FractalHeapHeader;
25use crate::message_type::MessageType;
26use crate::object_header::ObjectHeader;
27
28const FHEAP_ID_LEN: usize = 8;
30
31#[derive(Debug, Clone)]
33pub struct SharedMessageRef {
34 pub ref_type: u8,
36 pub version: u8,
38 pub object_header_address: Option<u64>,
40 pub heap_id: Option<[u8; FHEAP_ID_LEN]>,
42}
43
44#[derive(Debug, Clone)]
46pub struct SohmTableMessage {
47 pub version: u8,
49 pub table_address: u64,
51 pub nindexes: u8,
53}
54
55#[derive(Debug, Clone)]
57pub struct SohmIndex {
58 pub index_type: u8,
60 pub mesg_types: u16,
62 pub min_mesg_size: u32,
64 pub list_max: u16,
66 pub btree_min: u16,
68 pub num_messages: u16,
70 pub index_addr: u64,
72 pub heap_addr: u64,
74}
75
76#[derive(Debug, Clone)]
78pub struct SohmTable {
79 pub indexes: Vec<SohmIndex>,
81}
82
83#[derive(Debug, Clone)]
85pub struct SohmEntry {
86 pub location: u8,
88 pub hash: u32,
90 pub heap_id: Option<[u8; FHEAP_ID_LEN]>,
92 pub ref_count: Option<u32>,
94 pub mesg_index: Option<u16>,
96 pub oh_addr: Option<u64>,
98}
99
100fn read_offset(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
101 let s = size as usize;
102 if pos + s > data.len() {
103 return Err(FormatError::UnexpectedEof {
104 expected: pos + s,
105 available: data.len(),
106 });
107 }
108 Ok(match size {
109 2 => u16::from_le_bytes([data[pos], data[pos + 1]]) as u64,
110 4 => u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as u64,
111 8 => u64::from_le_bytes([
112 data[pos], data[pos + 1], data[pos + 2], data[pos + 3],
113 data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7],
114 ]),
115 _ => return Err(FormatError::InvalidOffsetSize(size)),
116 })
117}
118
119fn ensure_len(data: &[u8], pos: usize, needed: usize) -> Result<(), FormatError> {
120 match pos.checked_add(needed) {
121 Some(end) if end <= data.len() => Ok(()),
122 _ => Err(FormatError::UnexpectedEof {
123 expected: pos.saturating_add(needed),
124 available: data.len(),
125 }),
126 }
127}
128
129pub fn is_shared(msg_flags: u8) -> bool {
131 msg_flags & 0x02 != 0
132}
133
134pub fn parse_shared_ref(
139 data: &[u8],
140 offset_size: u8,
141) -> Result<SharedMessageRef, FormatError> {
142 ensure_len(data, 0, 2)?;
143 let version = data[0];
144 let ref_type = data[1];
145
146 match version {
147 1 | 2 => {
148 let pos = 2 + 6; ensure_len(data, pos, offset_size as usize)?;
151 let addr = read_offset(data, pos, offset_size)?;
152 Ok(SharedMessageRef {
153 ref_type,
154 version,
155 object_header_address: Some(addr),
156 heap_id: None,
157 })
158 }
159 3 => {
160 match ref_type {
161 1 | 3 => {
162 ensure_len(data, 2, offset_size as usize)?;
165 let addr = read_offset(data, 2, offset_size)?;
166 Ok(SharedMessageRef {
167 ref_type,
168 version,
169 object_header_address: Some(addr),
170 heap_id: None,
171 })
172 }
173 2 => {
174 ensure_len(data, 2, FHEAP_ID_LEN)?;
176 let mut id = [0u8; FHEAP_ID_LEN];
177 id.copy_from_slice(&data[2..2 + FHEAP_ID_LEN]);
178 Ok(SharedMessageRef {
179 ref_type,
180 version,
181 object_header_address: None,
182 heap_id: Some(id),
183 })
184 }
185 _ => Err(FormatError::InvalidSharedMessageVersion(ref_type)),
186 }
187 }
188 _ => Err(FormatError::InvalidSharedMessageVersion(version)),
189 }
190}
191
192pub fn parse_sohm_table_message(
198 data: &[u8],
199 offset_size: u8,
200) -> Result<SohmTableMessage, FormatError> {
201 ensure_len(data, 0, 1)?;
202 let version = data[0];
203 if version != 0 {
204 return Err(FormatError::InvalidSohmTableVersion(version));
205 }
206 let pos = 1;
207 ensure_len(data, pos, offset_size as usize + 1)?;
208 let table_address = read_offset(data, pos, offset_size)?;
209 let nindexes = data[pos + offset_size as usize];
210 Ok(SohmTableMessage {
211 version,
212 table_address,
213 nindexes,
214 })
215}
216
217pub fn parse_sohm_table(
225 file_data: &[u8],
226 table_addr: usize,
227 nindexes: u8,
228 offset_size: u8,
229) -> Result<SohmTable, FormatError> {
230 ensure_len(file_data, table_addr, 4)?;
231 if &file_data[table_addr..table_addr + 4] != b"SMTB" {
232 return Err(FormatError::InvalidSohmTableSignature);
233 }
234 let mut pos = table_addr + 4;
235 let os = offset_size as usize;
236 let entry_size = 1 + 2 + 4 + 2 + 2 + 2 + os + os; let mut indexes = Vec::with_capacity(nindexes as usize);
239 for _ in 0..nindexes {
240 ensure_len(file_data, pos, entry_size)?;
241 let index_type = file_data[pos];
242 pos += 1;
243 let mesg_types = u16::from_le_bytes([file_data[pos], file_data[pos + 1]]);
244 pos += 2;
245 let min_mesg_size = u32::from_le_bytes([
246 file_data[pos], file_data[pos + 1], file_data[pos + 2], file_data[pos + 3],
247 ]);
248 pos += 4;
249 let list_max = u16::from_le_bytes([file_data[pos], file_data[pos + 1]]);
250 pos += 2;
251 let btree_min = u16::from_le_bytes([file_data[pos], file_data[pos + 1]]);
252 pos += 2;
253 let num_messages = u16::from_le_bytes([file_data[pos], file_data[pos + 1]]);
254 pos += 2;
255 let index_addr = read_offset(file_data, pos, offset_size)?;
256 pos += os;
257 let heap_addr = read_offset(file_data, pos, offset_size)?;
258 pos += os;
259
260 indexes.push(SohmIndex {
261 index_type,
262 mesg_types,
263 min_mesg_size,
264 list_max,
265 btree_min,
266 num_messages,
267 index_addr,
268 heap_addr,
269 });
270 }
271 Ok(SohmTable { indexes })
273}
274
275fn sohm_entry_size(offset_size: u8) -> usize {
283 let oh_size = 2 + offset_size as usize;
284 let heap_size = FHEAP_ID_LEN + 4;
285 1 + 4 + oh_size.max(heap_size)
286}
287
288fn parse_sohm_entry(data: &[u8], offset_size: u8) -> Result<SohmEntry, FormatError> {
290 let entry_sz = sohm_entry_size(offset_size);
291 ensure_len(data, 0, entry_sz)?;
292 let location = data[0];
293 let hash = u32::from_le_bytes([data[1], data[2], data[3], data[4]]);
294 let pos = 5;
295
296 if location == 0 {
297 ensure_len(data, pos, FHEAP_ID_LEN + 4)?;
299 let mut heap_id = [0u8; FHEAP_ID_LEN];
300 heap_id.copy_from_slice(&data[pos..pos + FHEAP_ID_LEN]);
301 let ref_count = u32::from_le_bytes([
302 data[pos + FHEAP_ID_LEN],
303 data[pos + FHEAP_ID_LEN + 1],
304 data[pos + FHEAP_ID_LEN + 2],
305 data[pos + FHEAP_ID_LEN + 3],
306 ]);
307 Ok(SohmEntry {
308 location,
309 hash,
310 heap_id: Some(heap_id),
311 ref_count: Some(ref_count),
312 mesg_index: None,
313 oh_addr: None,
314 })
315 } else {
316 ensure_len(data, pos, 2 + offset_size as usize)?;
318 let mesg_index = u16::from_le_bytes([data[pos], data[pos + 1]]);
319 let oh_addr = read_offset(data, pos + 2, offset_size)?;
320 Ok(SohmEntry {
321 location,
322 hash,
323 heap_id: None,
324 ref_count: None,
325 mesg_index: Some(mesg_index),
326 oh_addr: Some(oh_addr),
327 })
328 }
329}
330
331pub fn parse_sohm_list(
333 file_data: &[u8],
334 list_addr: usize,
335 num_messages: u16,
336 offset_size: u8,
337) -> Result<Vec<SohmEntry>, FormatError> {
338 ensure_len(file_data, list_addr, 4)?;
339 if &file_data[list_addr..list_addr + 4] != b"SMLI" {
340 return Err(FormatError::InvalidSohmListSignature);
341 }
342 let entry_sz = sohm_entry_size(offset_size);
343 let mut pos = list_addr + 4;
344 let mut entries = Vec::with_capacity(num_messages as usize);
345 for _ in 0..num_messages {
346 ensure_len(file_data, pos, entry_sz)?;
347 let entry = parse_sohm_entry(&file_data[pos..], offset_size)?;
348 entries.push(entry);
349 pos += entry_sz;
350 }
351 Ok(entries)
352}
353
354pub fn parse_sohm_btree_entries(
356 file_data: &[u8],
357 btree_addr: usize,
358 offset_size: u8,
359 length_size: u8,
360) -> Result<Vec<SohmEntry>, FormatError> {
361 let header = BTreeV2Header::parse(file_data, btree_addr, offset_size, length_size)?;
362 let records = collect_btree_v2_records(file_data, &header, offset_size, length_size)?;
363 let mut entries = Vec::with_capacity(records.len());
364 for rec in &records {
365 let entry = parse_sohm_entry(&rec.data, offset_size)?;
366 entries.push(entry);
367 }
368 Ok(entries)
369}
370
371fn find_index_for_msg_type(table: &SohmTable, msg_type: MessageType) -> Option<&SohmIndex> {
375 let type_bit = 1u16 << msg_type.to_u16();
376 table.indexes.iter().find(|idx| idx.mesg_types & type_bit != 0)
377}
378
379fn is_undefined(val: u64, offset_size: u8) -> bool {
380 match offset_size {
381 2 => val == 0xFFFF,
382 4 => val == 0xFFFF_FFFF,
383 8 => val == 0xFFFF_FFFF_FFFF_FFFF,
384 _ => false,
385 }
386}
387
388pub fn resolve_sohm_message(
393 file_data: &[u8],
394 heap_id: &[u8; FHEAP_ID_LEN],
395 sohm_table: &SohmTable,
396 target_msg_type: MessageType,
397 offset_size: u8,
398 length_size: u8,
399) -> Result<Vec<u8>, FormatError> {
400 let index = find_index_for_msg_type(sohm_table, target_msg_type).ok_or(
401 FormatError::InvalidSharedMessageVersion(2),
402 )?;
403
404 if is_undefined(index.heap_addr, offset_size) {
405 return Err(FormatError::InvalidSharedMessageVersion(2));
406 }
407
408 let fh_header = FractalHeapHeader::parse(
409 file_data, index.heap_addr as usize, offset_size, length_size,
410 )?;
411 fh_header.read_managed_object(file_data, heap_id, offset_size)
412}
413
414pub fn resolve_shared_message(
420 file_data: &[u8],
421 shared_ref: &SharedMessageRef,
422 target_msg_type: MessageType,
423 offset_size: u8,
424 length_size: u8,
425) -> Result<Vec<u8>, FormatError> {
426 resolve_shared_message_with_sohm(
427 file_data, shared_ref, target_msg_type, offset_size, length_size, None,
428 )
429}
430
431pub fn resolve_shared_message_with_sohm(
433 file_data: &[u8],
434 shared_ref: &SharedMessageRef,
435 target_msg_type: MessageType,
436 offset_size: u8,
437 length_size: u8,
438 sohm_table: Option<&SohmTable>,
439) -> Result<Vec<u8>, FormatError> {
440 match shared_ref.ref_type {
441 1 | 3 => {
442 let addr = shared_ref.object_header_address.ok_or(
443 FormatError::UnexpectedEof {
444 expected: 1,
445 available: 0,
446 }
447 )?;
448 let target_header =
449 ObjectHeader::parse(file_data, addr as usize, offset_size, length_size)?;
450 for msg in &target_header.messages {
451 if msg.msg_type == target_msg_type && !is_shared(msg.flags) {
452 return Ok(msg.data.clone());
453 }
454 }
455 for msg in &target_header.messages {
459 if msg.msg_type == target_msg_type {
460 return Ok(msg.data.clone());
461 }
462 }
463 for msg in &target_header.messages {
465 if msg.msg_type != MessageType::Nil {
466 return Ok(msg.data.clone());
467 }
468 }
469 Err(FormatError::UnexpectedEof {
470 expected: 1,
471 available: 0,
472 })
473 }
474 2 => {
475 let heap_id = shared_ref.heap_id.as_ref().ok_or(
476 FormatError::InvalidSharedMessageVersion(2),
477 )?;
478 let table = sohm_table.ok_or(
479 FormatError::InvalidSharedMessageVersion(2),
480 )?;
481 resolve_sohm_message(
482 file_data, heap_id, table, target_msg_type, offset_size, length_size,
483 )
484 }
485 _ => {
486 Err(FormatError::InvalidSharedMessageVersion(shared_ref.ref_type))
487 }
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 #[test]
496 fn is_shared_flag() {
497 assert!(!is_shared(0x00));
498 assert!(!is_shared(0x01));
499 assert!(is_shared(0x02));
500 assert!(is_shared(0x03));
501 assert!(is_shared(0x06));
502 }
503
504 #[test]
505 fn parse_v3_type1_ref() {
506 let mut data = Vec::new();
507 data.push(3); data.push(1); data.extend_from_slice(&0x1234u64.to_le_bytes()); let shared = parse_shared_ref(&data, 8).unwrap();
512 assert_eq!(shared.version, 3);
513 assert_eq!(shared.ref_type, 1);
514 assert_eq!(shared.object_header_address, Some(0x1234));
515 assert!(shared.heap_id.is_none());
516 }
517
518 #[test]
519 fn parse_v3_type3_ref() {
520 let mut data = Vec::new();
521 data.push(3); data.push(3); data.extend_from_slice(&0xABCDu64.to_le_bytes());
524
525 let shared = parse_shared_ref(&data, 8).unwrap();
526 assert_eq!(shared.version, 3);
527 assert_eq!(shared.ref_type, 3);
528 assert_eq!(shared.object_header_address, Some(0xABCD));
529 }
530
531 #[test]
532 fn parse_v1_ref() {
533 let mut data = Vec::new();
534 data.push(1); data.push(0); data.extend_from_slice(&[0u8; 6]); data.extend_from_slice(&0x5678u64.to_le_bytes());
538
539 let shared = parse_shared_ref(&data, 8).unwrap();
540 assert_eq!(shared.version, 1);
541 assert_eq!(shared.object_header_address, Some(0x5678));
542 }
543
544 #[test]
545 fn parse_v2_ref() {
546 let mut data = Vec::new();
547 data.push(2); data.push(0); data.extend_from_slice(&[0u8; 6]); data.extend_from_slice(&0x9000u32.to_le_bytes());
551
552 let shared = parse_shared_ref(&data, 4).unwrap();
553 assert_eq!(shared.version, 2);
554 assert_eq!(shared.object_header_address, Some(0x9000));
555 }
556
557 #[test]
558 fn parse_v3_type2_sohm() {
559 let mut data = Vec::new();
560 data.push(3); data.push(2); data.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44]);
563
564 let shared = parse_shared_ref(&data, 8).unwrap();
565 assert_eq!(shared.version, 3);
566 assert_eq!(shared.ref_type, 2);
567 assert_eq!(shared.object_header_address, None);
568 assert_eq!(
569 shared.heap_id,
570 Some([0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44])
571 );
572 }
573
574 #[test]
575 fn parse_v3_type2_too_short() {
576 let mut data = Vec::new();
577 data.push(3); data.push(2); data.extend_from_slice(&[0xAA, 0xBB]); let err = parse_shared_ref(&data, 8).unwrap_err();
582 assert!(matches!(err, FormatError::UnexpectedEof { .. }));
583 }
584
585 #[test]
586 fn invalid_version() {
587 let data = vec![99, 0];
588 let err = parse_shared_ref(&data, 8).unwrap_err();
589 assert_eq!(err, FormatError::InvalidSharedMessageVersion(99));
590 }
591
592 #[test]
593 fn truncated_data() {
594 let data = vec![3u8]; let err = parse_shared_ref(&data, 8).unwrap_err();
596 assert!(matches!(err, FormatError::UnexpectedEof { .. }));
597 }
598
599 #[test]
600 fn parse_four_byte_offsets() {
601 let mut data = Vec::new();
602 data.push(3); data.push(1); data.extend_from_slice(&0x1000u32.to_le_bytes());
605
606 let shared = parse_shared_ref(&data, 4).unwrap();
607 assert_eq!(shared.object_header_address, Some(0x1000));
608 }
609
610 #[test]
613 fn parse_sohm_table_message_8byte() {
614 let mut data = Vec::new();
615 data.push(0); data.extend_from_slice(&0x2000u64.to_le_bytes()); data.push(3); let msg = parse_sohm_table_message(&data, 8).unwrap();
620 assert_eq!(msg.version, 0);
621 assert_eq!(msg.table_address, 0x2000);
622 assert_eq!(msg.nindexes, 3);
623 }
624
625 #[test]
626 fn parse_sohm_table_message_4byte() {
627 let mut data = Vec::new();
628 data.push(0); data.extend_from_slice(&0x1000u32.to_le_bytes()); data.push(1); let msg = parse_sohm_table_message(&data, 4).unwrap();
633 assert_eq!(msg.table_address, 0x1000);
634 assert_eq!(msg.nindexes, 1);
635 }
636
637 #[test]
638 fn parse_sohm_table_message_bad_version() {
639 let data = vec![1]; let err = parse_sohm_table_message(&data, 8).unwrap_err();
641 assert_eq!(err, FormatError::InvalidSohmTableVersion(1));
642 }
643
644 fn build_smtb(indexes: &[SohmIndex], offset_size: u8) -> Vec<u8> {
647 let mut buf = Vec::new();
648 buf.extend_from_slice(b"SMTB");
649 for idx in indexes {
650 buf.push(idx.index_type);
651 buf.extend_from_slice(&idx.mesg_types.to_le_bytes());
652 buf.extend_from_slice(&idx.min_mesg_size.to_le_bytes());
653 buf.extend_from_slice(&idx.list_max.to_le_bytes());
654 buf.extend_from_slice(&idx.btree_min.to_le_bytes());
655 buf.extend_from_slice(&idx.num_messages.to_le_bytes());
656 match offset_size {
657 4 => {
658 buf.extend_from_slice(&(idx.index_addr as u32).to_le_bytes());
659 buf.extend_from_slice(&(idx.heap_addr as u32).to_le_bytes());
660 }
661 8 => {
662 buf.extend_from_slice(&idx.index_addr.to_le_bytes());
663 buf.extend_from_slice(&idx.heap_addr.to_le_bytes());
664 }
665 _ => {}
666 }
667 }
668 buf.extend_from_slice(&[0u8; 4]);
670 buf
671 }
672
673 #[test]
674 fn parse_smtb_one_index() {
675 let indexes = vec![SohmIndex {
676 index_type: 0,
677 mesg_types: 0x0008, min_mesg_size: 50,
679 list_max: 50,
680 btree_min: 40,
681 num_messages: 2,
682 index_addr: 0x3000,
683 heap_addr: 0x4000,
684 }];
685 let data = build_smtb(&indexes, 8);
686 let table = parse_sohm_table(&data, 0, 1, 8).unwrap();
687 assert_eq!(table.indexes.len(), 1);
688 assert_eq!(table.indexes[0].index_type, 0);
689 assert_eq!(table.indexes[0].mesg_types, 0x0008);
690 assert_eq!(table.indexes[0].min_mesg_size, 50);
691 assert_eq!(table.indexes[0].num_messages, 2);
692 assert_eq!(table.indexes[0].index_addr, 0x3000);
693 assert_eq!(table.indexes[0].heap_addr, 0x4000);
694 }
695
696 #[test]
697 fn parse_smtb_two_indexes_4byte() {
698 let indexes = vec![
699 SohmIndex {
700 index_type: 0, mesg_types: 0x0008, min_mesg_size: 50,
701 list_max: 50, btree_min: 40, num_messages: 1,
702 index_addr: 0x1000, heap_addr: 0x2000,
703 },
704 SohmIndex {
705 index_type: 1, mesg_types: 0x0002, min_mesg_size: 100,
706 list_max: 25, btree_min: 15, num_messages: 5,
707 index_addr: 0x5000, heap_addr: 0x6000,
708 },
709 ];
710 let data = build_smtb(&indexes, 4);
711 let table = parse_sohm_table(&data, 0, 2, 4).unwrap();
712 assert_eq!(table.indexes.len(), 2);
713 assert_eq!(table.indexes[1].index_type, 1);
714 assert_eq!(table.indexes[1].mesg_types, 0x0002);
715 assert_eq!(table.indexes[1].num_messages, 5);
716 assert_eq!(table.indexes[1].index_addr, 0x5000);
717 }
718
719 #[test]
720 fn parse_smtb_bad_signature() {
721 let mut data = vec![0u8; 32];
722 data[0..4].copy_from_slice(b"XXXX");
723 let err = parse_sohm_table(&data, 0, 1, 8).unwrap_err();
724 assert_eq!(err, FormatError::InvalidSohmTableSignature);
725 }
726
727 #[test]
730 fn parse_heap_entry() {
731 let mut data = Vec::new();
732 data.push(0); data.extend_from_slice(&0x12345678u32.to_le_bytes()); data.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]); data.extend_from_slice(&3u32.to_le_bytes()); let entry = parse_sohm_entry(&data, 8).unwrap();
741 assert_eq!(entry.location, 0);
742 assert_eq!(entry.hash, 0x12345678);
743 assert_eq!(entry.heap_id, Some([1, 2, 3, 4, 5, 6, 7, 8]));
744 assert_eq!(entry.ref_count, Some(3));
745 assert!(entry.oh_addr.is_none());
746 }
747
748 #[test]
749 fn parse_oh_entry() {
750 let mut data = Vec::new();
751 data.push(1); data.extend_from_slice(&0xAABBCCDDu32.to_le_bytes()); data.extend_from_slice(&5u16.to_le_bytes()); data.extend_from_slice(&0x7000u64.to_le_bytes()); data.extend_from_slice(&[0u8; 2]);
757
758 let entry = parse_sohm_entry(&data, 8).unwrap();
759 assert_eq!(entry.location, 1);
760 assert_eq!(entry.hash, 0xAABBCCDD);
761 assert_eq!(entry.mesg_index, Some(5));
762 assert_eq!(entry.oh_addr, Some(0x7000));
763 assert!(entry.heap_id.is_none());
764 }
765
766 fn build_smli(entries: &[SohmEntry], offset_size: u8) -> Vec<u8> {
769 let mut buf = Vec::new();
770 buf.extend_from_slice(b"SMLI");
771 let entry_sz = sohm_entry_size(offset_size);
772 for entry in entries {
773 let start = buf.len();
774 buf.push(entry.location);
775 buf.extend_from_slice(&entry.hash.to_le_bytes());
776 if entry.location == 0 {
777 buf.extend_from_slice(entry.heap_id.as_ref().unwrap());
778 buf.extend_from_slice(&entry.ref_count.unwrap().to_le_bytes());
779 } else {
780 buf.extend_from_slice(&entry.mesg_index.unwrap().to_le_bytes());
781 match offset_size {
782 4 => buf.extend_from_slice(&(entry.oh_addr.unwrap() as u32).to_le_bytes()),
783 8 => buf.extend_from_slice(&entry.oh_addr.unwrap().to_le_bytes()),
784 _ => {}
785 }
786 }
787 let written = buf.len() - start;
789 if written < entry_sz {
790 buf.resize(buf.len() + entry_sz - written, 0);
791 }
792 }
793 buf.extend_from_slice(&[0u8; 4]); buf
795 }
796
797 #[test]
798 fn parse_smli_two_entries() {
799 let entries = vec![
800 SohmEntry {
801 location: 0, hash: 0x1111,
802 heap_id: Some([10, 20, 30, 40, 50, 60, 70, 80]),
803 ref_count: Some(1), mesg_index: None, oh_addr: None,
804 },
805 SohmEntry {
806 location: 0, hash: 0x2222,
807 heap_id: Some([11, 21, 31, 41, 51, 61, 71, 81]),
808 ref_count: Some(2), mesg_index: None, oh_addr: None,
809 },
810 ];
811 let data = build_smli(&entries, 8);
812 let parsed = parse_sohm_list(&data, 0, 2, 8).unwrap();
813 assert_eq!(parsed.len(), 2);
814 assert_eq!(parsed[0].hash, 0x1111);
815 assert_eq!(parsed[0].heap_id, Some([10, 20, 30, 40, 50, 60, 70, 80]));
816 assert_eq!(parsed[1].hash, 0x2222);
817 assert_eq!(parsed[1].ref_count, Some(2));
818 }
819
820 #[test]
821 fn parse_smli_bad_signature() {
822 let data = vec![b'X', b'X', b'X', b'X'];
823 let err = parse_sohm_list(&data, 0, 0, 8).unwrap_err();
824 assert_eq!(err, FormatError::InvalidSohmListSignature);
825 }
826
827 #[test]
830 fn find_index_for_datatype() {
831 let table = SohmTable {
832 indexes: vec![
833 SohmIndex {
834 index_type: 0,
835 mesg_types: 0x0008, min_mesg_size: 50, list_max: 50, btree_min: 40,
837 num_messages: 1, index_addr: 0x1000, heap_addr: 0x2000,
838 },
839 ],
840 };
841 let idx = find_index_for_msg_type(&table, MessageType::Datatype);
842 assert!(idx.is_some());
843 assert_eq!(idx.unwrap().heap_addr, 0x2000);
844 }
845
846 #[test]
847 fn find_index_no_match() {
848 let table = SohmTable {
849 indexes: vec![
850 SohmIndex {
851 index_type: 0,
852 mesg_types: 0x0002, min_mesg_size: 50, list_max: 50, btree_min: 40,
854 num_messages: 1, index_addr: 0x1000, heap_addr: 0x2000,
855 },
856 ],
857 };
858 let idx = find_index_for_msg_type(&table, MessageType::Datatype);
859 assert!(idx.is_none());
860 }
861
862 #[test]
863 fn entry_size_calculations() {
864 assert_eq!(sohm_entry_size(8), 17);
866 assert_eq!(sohm_entry_size(4), 17);
868 assert_eq!(sohm_entry_size(2), 17);
870 }
871}