1use crate::util::Address;
2use std::collections::HashMap;
3use std::io::{Error, ErrorKind, Result};
4use std::sync::{Mutex, RwLock};
5
6use super::constants::{
7 LOG_GLOBAL_SIDE_METADATA_WORST_CASE_RATIO, LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO,
8};
9use super::{SideMetadataContext, SideMetadataSpec};
10#[cfg(target_pointer_width = "64")]
11use crate::util::heap::layout::vm_layout::vm_layout;
12use crate::util::heap::layout::vm_layout::VMLayout;
13#[cfg(target_pointer_width = "32")]
14use crate::util::heap::layout::vm_layout::LOG_BYTES_IN_CHUNK;
15
16#[cfg(feature = "extreme_assertions")]
18enum MathOp {
19 Add,
20 Sub,
21}
22
23static GLOBAL_META_NAME: &str = "Global";
26
27pub struct SideMetadataSanity {
36 specs_sanity_map: HashMap<&'static str, Vec<SideMetadataSpec>>,
37}
38
39lazy_static! {
40 static ref CONTENT_SANITY_MAP: RwLock<HashMap<SideMetadataSpec, HashMap<Address, u64>>> =
45 RwLock::new(HashMap::new());
46 pub(crate) static ref SANITY_LOCK: Mutex<()> = Mutex::new(());
47}
48
49#[cfg(test)]
51pub(crate) fn reset() {
52 CONTENT_SANITY_MAP.write().unwrap().clear()
53}
54
55fn verify_global_specs_total_size(g_specs: &[SideMetadataSpec]) -> Result<()> {
63 let mut total_size = 0usize;
64 for spec in g_specs {
65 total_size += super::metadata_address_range_size(spec);
66 }
67
68 if total_size
69 <= 1usize << (VMLayout::LOG_ARCH_ADDRESS_SPACE - LOG_GLOBAL_SIDE_METADATA_WORST_CASE_RATIO)
70 {
71 Ok(())
72 } else {
73 Err(Error::new(
74 ErrorKind::InvalidInput,
75 format!("Not enough global metadata space for: \n{:?}", g_specs),
76 ))
77 }
78}
79
80#[cfg(target_pointer_width = "64")]
88fn verify_local_specs_size(l_specs: &[SideMetadataSpec]) -> Result<()> {
89 for spec in l_specs {
90 if super::metadata_address_range_size(spec)
91 > 1usize
92 << (VMLayout::LOG_ARCH_ADDRESS_SPACE - LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO)
93 {
94 return Err(Error::new(
95 ErrorKind::InvalidInput,
96 format!("Local metadata is too big: \n{:?}", spec),
97 ));
98 }
99 }
100
101 Ok(())
102}
103
104#[cfg(target_pointer_width = "32")]
112fn verify_local_specs_size(l_specs: &[SideMetadataSpec]) -> Result<()> {
113 let mut total_size = 0usize;
114 for spec in l_specs {
115 total_size +=
116 super::metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
117 }
118
119 if total_size > 1usize << (LOG_BYTES_IN_CHUNK - LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO) {
120 return Err(Error::new(
121 ErrorKind::InvalidInput,
122 format!(
123 "Not enough local metadata space per chunk for: \n{:?}",
124 l_specs
125 ),
126 ));
127 }
128
129 Ok(())
130}
131
132fn verify_no_overlap_contiguous(
141 spec_1: &SideMetadataSpec,
142 spec_2: &SideMetadataSpec,
143) -> Result<()> {
144 let end_1 = spec_1.get_absolute_offset() + super::metadata_address_range_size(spec_1);
145 let end_2 = spec_2.get_absolute_offset() + super::metadata_address_range_size(spec_2);
146
147 if !(spec_1.get_absolute_offset() >= end_2 || spec_2.get_absolute_offset() >= end_1) {
148 return Err(Error::new(
149 ErrorKind::InvalidInput,
150 format!(
151 "Overlapping metadata specs detected:\nTHIS:\n{:#?}\nAND:\n{:#?}",
152 spec_1, spec_2
153 ),
154 ));
155 }
156 Ok(())
157}
158
159#[cfg(target_pointer_width = "32")]
168fn verify_no_overlap_chunked(spec_1: &SideMetadataSpec, spec_2: &SideMetadataSpec) -> Result<()> {
169 let end_1 = spec_1.get_rel_offset()
170 + super::metadata_bytes_per_chunk(spec_1.log_bytes_in_region, spec_1.log_num_of_bits);
171 let end_2 = spec_2.get_rel_offset()
172 + super::metadata_bytes_per_chunk(spec_2.log_bytes_in_region, spec_2.log_num_of_bits);
173
174 if !(spec_1.get_rel_offset() >= end_2 || spec_2.get_rel_offset() >= end_1) {
175 return Err(Error::new(
176 ErrorKind::InvalidInput,
177 format!(
178 "Overlapping metadata specs detected:\nTHIS:\n{:#?}\nAND:\n{:#?}",
179 spec_1, spec_2
180 ),
181 ));
182 }
183 Ok(())
184}
185
186fn verify_global_specs(g_specs: &[SideMetadataSpec]) -> Result<()> {
194 verify_global_specs_total_size(g_specs)?;
195
196 for spec_1 in g_specs {
197 for spec_2 in g_specs {
198 if spec_1 != spec_2 {
199 verify_no_overlap_contiguous(spec_1, spec_2)?;
200 }
201 }
202 }
203
204 Ok(())
205}
206
207impl Default for SideMetadataSanity {
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214impl SideMetadataSanity {
215 pub fn new() -> SideMetadataSanity {
217 SideMetadataSanity {
218 specs_sanity_map: HashMap::new(),
219 }
220 }
221 fn get_all_specs(&self, global: bool) -> Vec<SideMetadataSpec> {
229 let mut specs = vec![];
230 for (k, v) in self.specs_sanity_map.iter() {
231 if !(global ^ (*k == GLOBAL_META_NAME)) {
232 specs.append(&mut (*v).clone());
233 }
234 }
235 std::collections::HashSet::<SideMetadataSpec>::from_iter(specs)
237 .into_iter()
238 .collect()
239 }
240
241 fn verify_local_specs(&self) -> Result<()> {
248 let local_specs = self.get_all_specs(false);
249
250 verify_local_specs_size(&local_specs)?;
251
252 for spec_1 in &local_specs {
253 for spec_2 in &local_specs {
254 if spec_1 != spec_2 {
255 #[cfg(target_pointer_width = "64")]
256 verify_no_overlap_contiguous(spec_1, spec_2)?;
257 #[cfg(target_pointer_width = "32")]
258 verify_no_overlap_chunked(spec_1, spec_2)?;
259 }
260 }
261 }
262 Ok(())
263 }
264
265 pub(crate) fn verify_metadata_context(
278 &mut self,
279 policy_name: &'static str,
280 metadata_context: &SideMetadataContext,
281 ) {
282 let mut content_sanity_map = CONTENT_SANITY_MAP.write().unwrap();
283
284 let first_call = !self.specs_sanity_map.contains_key(&GLOBAL_META_NAME);
286
287 if first_call {
288 verify_global_specs(&metadata_context.global).unwrap();
290 self.specs_sanity_map
291 .insert(GLOBAL_META_NAME, metadata_context.global.clone());
292 } else {
293 let g_specs = self.specs_sanity_map.get(&GLOBAL_META_NAME).unwrap();
295 assert!(
296 g_specs.len() == metadata_context.global.len(),
297 "Global metadata must not change between policies! NEW SPECS: {:#?} OLD SPECS: {:#?}",
298 metadata_context.global,
299 g_specs
300 );
301 }
302
303 for spec in &metadata_context.global {
304 assert!(
306 spec.is_global,
307 "Policy-specific spec {:#?} detected in the global specs: {:#?}",
308 spec, metadata_context.global
309 );
310 if first_call {
313 content_sanity_map.insert(*spec, HashMap::new());
315 } else if !self
316 .specs_sanity_map
317 .get(&GLOBAL_META_NAME)
318 .unwrap()
319 .contains(spec)
320 {
321 panic!("Global metadata must not change between policies! NEW SPEC: {:#?} OLD SPECS: {:#?}", spec, self.get_all_specs(true));
322 }
323 }
324
325 let first_call = !self.specs_sanity_map.contains_key(&policy_name);
327
328 if first_call {
329 self.specs_sanity_map
330 .insert(policy_name, metadata_context.local.clone());
331 }
332
333 for spec in &metadata_context.local {
334 assert!(
336 !spec.is_global,
337 "Global spec {:#?} detected in the policy-specific specs: {:#?}",
338 spec, metadata_context.local
339 );
340 if first_call {
344 content_sanity_map.insert(*spec, HashMap::new());
346 } else if !self
347 .specs_sanity_map
348 .get(policy_name)
349 .unwrap()
350 .contains(spec)
351 {
352 panic!(
353 "Policy-specific metadata for -{}- changed from {:#?} to {:#?}",
354 policy_name,
355 self.specs_sanity_map.get(policy_name).unwrap(),
356 metadata_context.local
357 )
358 }
359 }
360
361 self.verify_local_specs().unwrap();
362 }
363
364 #[cfg(test)]
365 pub fn reset(&mut self) {
366 let mut content_sanity_map = CONTENT_SANITY_MAP.write().unwrap();
367 self.specs_sanity_map.clear();
368 content_sanity_map.clear();
369 }
370}
371
372fn verify_metadata_address_bound(spec: &SideMetadataSpec, data_addr: Address) {
376 #[cfg(target_pointer_width = "32")]
377 assert_eq!(VMLayout::LOG_ARCH_ADDRESS_SPACE, 32, "We assume we use all address space in 32 bits. This seems not true any more, we need a proper check here.");
378 #[cfg(target_pointer_width = "32")]
379 let data_addr_in_address_space = true;
380 #[cfg(target_pointer_width = "64")]
381 let data_addr_in_address_space =
382 data_addr <= unsafe { Address::from_usize(1usize << vm_layout().log_address_space) };
383
384 if !data_addr_in_address_space {
385 warn!(
386 "We try get metadata {} for {}, which is not within the address space we should use",
387 data_addr, spec.name
388 );
389 }
390
391 let metadata_addr =
392 crate::util::metadata::side_metadata::address_to_meta_address(spec, data_addr);
393 let metadata_addr_bound = if spec.is_absolute_offset() {
394 spec.upper_bound_address_for_contiguous()
395 } else {
396 #[cfg(target_pointer_width = "32")]
397 {
398 spec.upper_bound_address_for_chunked(data_addr)
399 }
400 #[cfg(target_pointer_width = "64")]
401 {
402 unreachable!()
403 }
404 };
405 assert!(
406 metadata_addr < metadata_addr_bound,
407 "We try access metadata address for address {} of spec {} that is not within the bound {}.",
408 data_addr,
409 spec.name,
410 metadata_addr_bound
411 );
412}
413
414#[cfg(feature = "extreme_assertions")]
423pub fn verify_bzero(metadata_spec: &SideMetadataSpec, start: Address, size: usize) {
424 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
425 let start = align_to_region_start(metadata_spec, start);
426 let end = align_to_region_start(metadata_spec, start + size);
427 match sanity_map.get_mut(metadata_spec) {
428 Some(spec_sanity_map) => {
429 for (k, v) in spec_sanity_map.iter_mut() {
431 if *k >= start && *k < end {
433 *v = 0;
434 }
435 }
436 }
437 None => {
438 panic!("Invalid Metadata Spec: {}", metadata_spec.name);
439 }
440 }
441}
442
443#[cfg(feature = "extreme_assertions")]
451pub fn verify_bset(metadata_spec: &SideMetadataSpec, start: Address, size: usize) {
452 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
453 let start = align_to_region_start(metadata_spec, start);
454 let end = align_to_region_start(metadata_spec, start + size);
455 let max_value = (1 << (1 << metadata_spec.log_num_of_bits)) - 1;
456 match sanity_map.get_mut(metadata_spec) {
457 Some(spec_sanity_map) => {
458 let mut cursor = start;
459 let step: usize = 1 << metadata_spec.log_bytes_in_region;
460 while cursor < end {
461 spec_sanity_map.insert(cursor, max_value);
462 cursor += step;
463 }
464 }
465 None => {
466 panic!("Invalid Metadata Spec!");
467 }
468 }
469}
470
471#[cfg(feature = "extreme_assertions")]
483pub fn verify_bcopy(
484 dst_spec: &SideMetadataSpec,
485 start: Address,
486 size: usize,
487 src_spec: &SideMetadataSpec,
488) {
489 assert_eq!(src_spec.log_num_of_bits, dst_spec.log_num_of_bits);
490 assert_eq!(src_spec.log_bytes_in_region, dst_spec.log_bytes_in_region);
491
492 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
493 let start = align_to_region_start(dst_spec, start);
494 let end = align_to_region_start(dst_spec, start + size);
495
496 let mut tmp_map = HashMap::new();
501
502 {
503 let src_map = sanity_map
504 .get_mut(src_spec)
505 .expect("Invalid source Metadata Spec!");
506
507 let mut cursor = start;
508 let step: usize = 1 << src_spec.log_bytes_in_region;
509 while cursor < end {
510 let src_value = src_map.get(&cursor).copied().unwrap_or(0u64);
511 tmp_map.insert(cursor, src_value);
512 cursor += step;
513 }
514 }
515 {
516 let dst_map = sanity_map
517 .get_mut(dst_spec)
518 .expect("Invalid destination Metadata Spec!");
519
520 let mut cursor = start;
521 let step: usize = 1 << dst_spec.log_bytes_in_region;
522 while cursor < end {
523 let src_value = tmp_map.get(&cursor).copied().unwrap();
524 dst_map.insert(cursor, src_value);
525 cursor += step;
526 }
527 }
528}
529
530#[cfg(feature = "extreme_assertions")]
531use crate::util::metadata::metadata_val_traits::*;
532
533#[cfg(feature = "extreme_assertions")]
534fn truncate_value<T: MetadataValue>(log_num_of_bits: usize, val: u64) -> u64 {
535 if log_num_of_bits < T::LOG2 as usize {
537 val & ((1 << (1 << log_num_of_bits)) - 1)
538 } else {
539 val
540 }
541}
542
543#[cfg(feature = "extreme_assertions")]
544#[cfg(test)]
545mod truncate_tests {
546 use super::*;
547
548 #[test]
549 fn test_truncate() {
550 assert_eq!(truncate_value::<u8>(2, 0), 0);
551 assert_eq!(truncate_value::<u8>(2, 15), 15);
552 assert_eq!(truncate_value::<u8>(2, 16), 0);
553 assert_eq!(truncate_value::<u8>(2, 17), 1);
554 }
555}
556
557fn align_to_region_start(spec: &SideMetadataSpec, data_addr: Address) -> Address {
560 data_addr.align_down(1 << spec.log_bytes_in_region)
561}
562
563#[cfg(feature = "extreme_assertions")]
574pub fn verify_load<T: MetadataValue>(
575 metadata_spec: &SideMetadataSpec,
576 data_addr: Address,
577 actual_val: T,
578) {
579 let data_addr = align_to_region_start(metadata_spec, data_addr);
580 let actual_val: u64 = actual_val.to_u64().unwrap();
581 verify_metadata_address_bound(metadata_spec, data_addr);
582 let sanity_map = &mut CONTENT_SANITY_MAP.read().unwrap();
583 match sanity_map.get(metadata_spec) {
584 Some(spec_sanity_map) => {
585 let expected_val = if let Some(expected_val) = spec_sanity_map.get(&data_addr) {
587 *expected_val
588 } else {
589 0u64
590 };
591 assert!(
592 expected_val == actual_val,
593 "verify_load({:#?}, {}) -> Expected (0x{:x}) but found (0x{:x})",
594 metadata_spec,
595 data_addr,
596 expected_val,
597 actual_val
598 );
599 }
600 None => panic!("Invalid Metadata Spec: {:#?}", metadata_spec),
601 }
602}
603
604#[cfg(feature = "extreme_assertions")]
613pub fn verify_store<T: MetadataValue>(
614 metadata_spec: &SideMetadataSpec,
615 data_addr: Address,
616 metadata: T,
617) {
618 let data_addr = align_to_region_start(metadata_spec, data_addr);
619 let metadata: u64 = metadata.to_u64().unwrap();
620 verify_metadata_address_bound(metadata_spec, data_addr);
621 let new_val_wrapped = truncate_value::<T>(metadata_spec.log_num_of_bits, metadata);
622 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
623 match sanity_map.get_mut(metadata_spec) {
624 Some(spec_sanity_map) => {
625 let content = spec_sanity_map.entry(data_addr).or_insert(0);
627 *content = new_val_wrapped;
628 }
629 None => panic!("Invalid Metadata Spec: {:#?}", metadata_spec),
630 }
631}
632
633#[cfg(feature = "extreme_assertions")]
644pub fn verify_update<T: MetadataValue>(
645 metadata_spec: &SideMetadataSpec,
646 data_addr: Address,
647 old_val: T,
648 new_val: T,
649) {
650 let data_addr = align_to_region_start(metadata_spec, data_addr);
651 verify_metadata_address_bound(metadata_spec, data_addr);
652
653 let new_val_wrapped =
655 truncate_value::<T>(metadata_spec.log_num_of_bits, new_val.to_u64().unwrap());
656
657 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
658 match sanity_map.get_mut(metadata_spec) {
659 Some(spec_sanity_map) => {
660 let cur_val = spec_sanity_map.entry(data_addr).or_insert(0);
661 assert_eq!(
662 old_val.to_u64().unwrap(),
663 *cur_val,
664 "Expected old value: {} but found {}",
665 old_val,
666 cur_val
667 );
668 *cur_val = new_val_wrapped;
669 }
670 None => panic!("Invalid metadata spec: {:#?}", metadata_spec),
671 }
672}
673
674#[cfg(test)]
675mod tests {
676 use super::super::*;
677 use super::*;
678
679 #[test]
680 fn test_side_metadata_sanity_verify_global_specs_total_size() {
681 let spec_1 = SideMetadataSpec {
682 name: "spec_1",
683 is_global: true,
684 offset: SideMetadataOffset::addr(Address::ZERO),
685 log_num_of_bits: 0,
686 log_bytes_in_region: 0,
687 };
688 let spec_2 = SideMetadataSpec {
689 name: "spec_2",
690 is_global: true,
691 offset: SideMetadataOffset::layout_after(&spec_1),
692 log_num_of_bits: 0,
693 log_bytes_in_region: 0,
694 };
695
696 assert!(verify_global_specs_total_size(&[spec_1]).is_ok());
697 #[cfg(target_pointer_width = "64")]
698 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_ok());
699 #[cfg(target_pointer_width = "32")]
700 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_err());
701
702 let spec_2 = SideMetadataSpec {
703 name: "spec_2",
704 is_global: true,
705 offset: SideMetadataOffset::layout_after(&spec_1),
706 log_num_of_bits: 3,
707 log_bytes_in_region: 1,
708 };
709
710 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_err());
711
712 let spec_1 = SideMetadataSpec {
713 name: "spec_1",
714 is_global: true,
715 offset: SideMetadataOffset::addr(Address::ZERO),
716 log_num_of_bits: 1,
717 #[cfg(target_pointer_width = "64")]
718 log_bytes_in_region: 0,
719 #[cfg(target_pointer_width = "32")]
720 log_bytes_in_region: 2,
721 };
722 let spec_2 = SideMetadataSpec {
723 name: "spec_2",
724 is_global: true,
725 offset: SideMetadataOffset::layout_after(&spec_1),
726 log_num_of_bits: 3,
727 #[cfg(target_pointer_width = "64")]
728 log_bytes_in_region: 2,
729 #[cfg(target_pointer_width = "32")]
730 log_bytes_in_region: 4,
731 };
732
733 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_ok());
734 assert!(verify_global_specs_total_size(&[spec_1, spec_2, spec_1]).is_err());
735 }
736
737 #[test]
738 fn test_side_metadata_sanity_verify_no_overlap_contiguous() {
739 let spec_1 = SideMetadataSpec {
740 name: "spec_1",
741 is_global: true,
742 offset: SideMetadataOffset::addr(Address::ZERO),
743 log_num_of_bits: 0,
744 log_bytes_in_region: 0,
745 };
746 let spec_2 = SideMetadataSpec {
747 name: "spec_2",
748 is_global: true,
749 offset: SideMetadataOffset::layout_after(&spec_1),
750 log_num_of_bits: 0,
751 log_bytes_in_region: 0,
752 };
753
754 assert!(verify_no_overlap_contiguous(&spec_1, &spec_1).is_err());
755 assert!(verify_no_overlap_contiguous(&spec_1, &spec_2).is_ok());
756
757 let spec_1 = SideMetadataSpec {
758 name: "spec_1",
759 is_global: true,
760 offset: SideMetadataOffset::addr(unsafe { Address::from_usize(1) }),
761 log_num_of_bits: 0,
762 log_bytes_in_region: 0,
763 };
764
765 assert!(verify_no_overlap_contiguous(&spec_1, &spec_2).is_err());
766
767 let spec_1 = SideMetadataSpec {
768 name: "spec_1",
769 is_global: true,
770 offset: SideMetadataOffset::addr(Address::ZERO),
771 log_num_of_bits: 0,
772 log_bytes_in_region: 0,
773 };
774 let spec_2 = SideMetadataSpec {
775 name: "spec_2",
776 is_global: true,
777 offset: SideMetadataOffset::addr(
779 spec_1.get_absolute_offset() + metadata_address_range_size(&spec_1) - 1,
780 ),
781 log_num_of_bits: 0,
782 log_bytes_in_region: 0,
783 };
784
785 assert!(verify_no_overlap_contiguous(&spec_1, &spec_2).is_err());
786 }
787
788 #[cfg(target_pointer_width = "32")]
789 #[test]
790 fn test_side_metadata_sanity_verify_no_overlap_chunked() {
791 let spec_1 = SideMetadataSpec {
792 name: "spec_1",
793 is_global: false,
794 offset: SideMetadataOffset::rel(0),
795 log_num_of_bits: 0,
796 log_bytes_in_region: 0,
797 };
798 let spec_2 = SideMetadataSpec {
799 name: "spec_2",
800 is_global: false,
801 offset: SideMetadataOffset::layout_after(&spec_1),
802 log_num_of_bits: 0,
803 log_bytes_in_region: 0,
804 };
805
806 assert!(verify_no_overlap_chunked(&spec_1, &spec_1).is_err());
807 assert!(verify_no_overlap_chunked(&spec_1, &spec_2).is_ok());
808
809 let spec_1 = SideMetadataSpec {
810 name: "spec_1",
811 is_global: false,
812 offset: SideMetadataOffset::rel(1),
813 log_num_of_bits: 0,
814 log_bytes_in_region: 0,
815 };
816
817 assert!(verify_no_overlap_chunked(&spec_1, &spec_2).is_err());
818
819 let spec_1 = SideMetadataSpec {
820 name: "spec_1",
821 is_global: false,
822 offset: SideMetadataOffset::rel(0),
823 log_num_of_bits: 0,
824 log_bytes_in_region: 0,
825 };
826 let spec_2 = SideMetadataSpec {
827 name: "spec_2",
828 is_global: false,
829 offset: SideMetadataOffset::rel(
831 spec_1.get_rel_offset()
832 + metadata_bytes_per_chunk(spec_1.log_bytes_in_region, spec_1.log_num_of_bits)
833 - 1,
834 ),
835 log_num_of_bits: 0,
836 log_bytes_in_region: 0,
837 };
838
839 assert!(verify_no_overlap_chunked(&spec_1, &spec_2).is_err());
840 }
841
842 #[cfg(target_pointer_width = "32")]
843 #[test]
844 fn test_side_metadata_sanity_verify_local_specs_size() {
845 let spec_1 = SideMetadataSpec {
846 name: "spec_1",
847 is_global: false,
848 offset: SideMetadataOffset::rel(0),
849 log_num_of_bits: 0,
850 log_bytes_in_region: 0,
851 };
852
853 assert!(verify_local_specs_size(&[spec_1]).is_ok());
854 assert!(verify_local_specs_size(&[spec_1, spec_1]).is_err());
855 assert!(verify_local_specs_size(&[spec_1, spec_1, spec_1, spec_1, spec_1]).is_err());
856 }
857
858 #[test]
859 fn test_side_metadata_sanity_get_all_local_specs() {
860 let spec_1 = SideMetadataSpec {
861 name: "spec_1",
862 is_global: false,
863 offset: SideMetadataOffset::rel(0),
864 log_num_of_bits: 0,
865 log_bytes_in_region: 0,
866 };
867
868 let mut sanity = SideMetadataSanity::new();
869 sanity.verify_metadata_context(
870 "policy1",
871 &SideMetadataContext {
872 global: vec![],
873 local: vec![spec_1],
874 },
875 );
876 sanity.verify_metadata_context(
877 "policy2",
878 &SideMetadataContext {
879 global: vec![],
880 local: vec![spec_1],
881 },
882 );
883
884 let local_specs = sanity.get_all_specs(false);
885 assert_eq!(local_specs.len(), 1);
886 }
887}