mmtk/util/metadata/side_metadata/
helpers.rs

1use super::ranges::BitOffset;
2use super::SideMetadataSpec;
3use crate::util::constants::LOG_BYTES_IN_PAGE;
4use crate::util::constants::{BITS_IN_WORD, BYTES_IN_PAGE, LOG_BITS_IN_BYTE};
5use crate::util::conversions::rshift_align_up;
6use crate::util::heap::layout::vm_layout::VMLayout;
7#[cfg(target_pointer_width = "32")]
8use crate::util::metadata::side_metadata::address_to_chunked_meta_address;
9use crate::util::os::*;
10use crate::util::Address;
11use crate::MMAPPER;
12
13/// Performs address translation in contiguous metadata spaces (e.g. global and policy-specific in 64-bits, and global in 32-bits)
14pub(super) fn address_to_contiguous_meta_address(
15    metadata_spec: &SideMetadataSpec,
16    data_addr: Address,
17) -> Address {
18    let log_bits_num = metadata_spec.log_num_of_bits as i32;
19    let log_bytes_in_region = metadata_spec.log_bytes_in_region;
20
21    let shift = (LOG_BITS_IN_BYTE as i32) - log_bits_num;
22
23    if shift >= 0 {
24        metadata_spec.get_absolute_offset() + ((data_addr >> log_bytes_in_region) >> shift)
25    } else {
26        metadata_spec.get_absolute_offset() + ((data_addr >> log_bytes_in_region) << (-shift))
27    }
28}
29
30/// Performs reverse address translation from contiguous metadata bits to data addresses.
31/// The input address and bit shift should be aligned.
32///
33/// Arguments:
34/// * `metadata_spec`: The side metadata spec. It should be contiguous side metadata.
35/// * `metadata_addr`; The metadata address. Returned by [`address_to_contiguous_meta_address`].
36/// * `bit`: The bit shift for the metadata. Returned by [`meta_byte_lshift`].
37pub(super) fn contiguous_meta_address_to_address(
38    metadata_spec: &SideMetadataSpec,
39    metadata_addr: Address,
40    bit: u8,
41) -> Address {
42    debug_assert_eq!(
43        align_metadata_address(metadata_spec, metadata_addr, bit),
44        (metadata_addr, bit)
45    );
46    let shift = (LOG_BITS_IN_BYTE as i32) - metadata_spec.log_num_of_bits as i32;
47    let relative_meta_addr = metadata_addr - metadata_spec.get_absolute_offset();
48
49    let data_addr_intermediate = if shift >= 0 {
50        relative_meta_addr << shift
51    } else {
52        relative_meta_addr >> (-shift)
53    };
54    let data_addr_bit_shift = if shift >= 0 {
55        metadata_spec.log_bytes_in_region - metadata_spec.log_num_of_bits
56    } else {
57        metadata_spec.log_bytes_in_region
58    };
59
60    let data_addr = (data_addr_intermediate << metadata_spec.log_bytes_in_region)
61        + ((bit as usize) << data_addr_bit_shift);
62
63    unsafe { Address::from_usize(data_addr) }
64}
65
66/// Align an pair of a metadata address and a metadata bit offset to the start of this metadata value.
67/// For example, when the metadata is 4 bits, it should only start at bit 0 or bit 4.
68/// When the metadata is 16 bits, it should only start at bit 0, and its metadata address should be aligned to 2 bytes.
69/// This is important, as [`contiguous_meta_address_to_address`] can only convert the start address of metadata to
70/// the data address.
71pub(super) fn align_metadata_address(
72    spec: &SideMetadataSpec,
73    metadata_addr: Address,
74    bit: u8,
75) -> (Address, u8) {
76    if spec.log_num_of_bits >= LOG_BITS_IN_BYTE as usize {
77        (
78            metadata_addr.align_down(1 << (spec.log_num_of_bits - LOG_BITS_IN_BYTE as usize)),
79            0,
80        )
81    } else {
82        (
83            metadata_addr,
84            crate::util::conversions::raw_align_down(
85                bit as usize,
86                (1 << spec.log_num_of_bits) as usize,
87            ) as u8,
88        )
89    }
90}
91
92/// Unmaps the specified metadata range, or panics.
93#[cfg(test)]
94pub(crate) fn ensure_munmap_metadata(start: Address, size: usize) {
95    trace!("ensure_munmap_metadata({}, 0x{:x})", start, size);
96
97    assert!(OS::munmap(start, size).is_ok())
98}
99
100/// Unmaps a metadata space (`spec`) for the specified data address range (`start` and `size`)
101/// Returns the size in bytes that get munmapped.
102#[cfg(test)]
103pub(crate) fn ensure_munmap_contiguous_metadata_space(
104    start: Address,
105    size: usize,
106    spec: &SideMetadataSpec,
107) -> usize {
108    // nearest page-aligned starting address
109    let metadata_start = address_to_meta_address(spec, start);
110    let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
111    // nearest page-aligned ending address
112    let metadata_size = data_to_meta_size_round_up(spec, size);
113    let mmap_size = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE) - mmap_start;
114    if mmap_size > 0 {
115        ensure_munmap_metadata(mmap_start, mmap_size);
116    }
117    mmap_size
118}
119
120/// Tries to mmap the metadata space (`spec`) for the specified data address range (`start` and `size`).
121/// Setting `no_reserve` to true means the function will only map address range, without reserving swap-space/physical memory.
122/// Returns the size in bytes that gets mmapped in the function if success.
123pub(super) fn try_mmap_contiguous_metadata_space(
124    start: Address,
125    size: usize,
126    spec: &SideMetadataSpec,
127    no_reserve: bool,
128    anno: &MmapAnnotation,
129) -> MmapResult<usize> {
130    debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
131    debug_assert!(size % BYTES_IN_PAGE == 0);
132
133    // nearest page-aligned starting address
134    let metadata_start = address_to_meta_address(spec, start);
135    let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
136    // nearest page-aligned ending address
137    let metadata_size = data_to_meta_size_round_up(spec, size);
138    let mmap_size = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE) - mmap_start;
139    if mmap_size > 0 {
140        if !no_reserve {
141            MMAPPER.ensure_mapped(
142                mmap_start,
143                mmap_size >> LOG_BYTES_IN_PAGE,
144                HugePageSupport::No,
145                MmapProtection::ReadWrite,
146                anno,
147            )
148        } else {
149            MMAPPER.quarantine_address_range(
150                mmap_start,
151                mmap_size >> LOG_BYTES_IN_PAGE,
152                HugePageSupport::No,
153                anno,
154            )
155        }
156        .map(|_| mmap_size)
157    } else {
158        Ok(0)
159    }
160}
161
162/// Performs the translation of data address (`data_addr`) to metadata address for the specified metadata (`metadata_spec`).
163pub(crate) fn address_to_meta_address(
164    metadata_spec: &SideMetadataSpec,
165    data_addr: Address,
166) -> Address {
167    #[cfg(target_pointer_width = "32")]
168    let res = {
169        if metadata_spec.is_global {
170            address_to_contiguous_meta_address(metadata_spec, data_addr)
171        } else {
172            address_to_chunked_meta_address(metadata_spec, data_addr)
173        }
174    };
175    #[cfg(target_pointer_width = "64")]
176    let res = { address_to_contiguous_meta_address(metadata_spec, data_addr) };
177
178    trace!(
179        "address_to_meta_address({:?}, addr: {}) -> 0x{:x}",
180        metadata_spec,
181        data_addr,
182        res
183    );
184
185    res
186}
187
188/// Return the base-2 logarithm of the ratio of data bits and metadata bits per region.
189///
190/// Suppose a memory region has `data_bits` bits of data, and `meta_bits` bits of metadata for
191/// `metadata_spec`, and the result of `log_data_meta_ratio(metadata_spec)` is `shift`, then
192///
193/// -   `data_bits >> shift == meta_bits`
194/// -   `meta_bits << shift == data_bits`
195pub(super) const fn log_data_meta_ratio(metadata_spec: &SideMetadataSpec) -> usize {
196    let log_data_bits_in_region = (LOG_BITS_IN_BYTE as usize) + metadata_spec.log_bytes_in_region;
197    let log_meta_bits_in_region = metadata_spec.log_num_of_bits;
198
199    // TODO: In theory, it is possible to construct a side metadata that has more metadata bits than
200    // data bits per region.  But such pathological side metadata consumes way too much memory, and
201    // should never be used in any useful applications.  It should be forbidden.
202    log_data_bits_in_region - log_meta_bits_in_region
203}
204
205/// Calculate the amount of metadata needed for the give amount of data memory, round up to nearest
206/// integer. `data_size` can be in any unit, e.g. bits, bytes, pages, blocks, chunks, etc., and the
207/// result has the same unit.
208pub(super) const fn data_to_meta_size_round_up(
209    metadata_spec: &SideMetadataSpec,
210    data_size: usize,
211) -> usize {
212    rshift_align_up(data_size, log_data_meta_ratio(metadata_spec))
213}
214
215/// Calculate the amount of data governed by the give amount of metadata.  `meta_size` can be in any
216/// unit, e.g. bits, bytes, pages, blocks, chunks, etc., and the result has the same unit.
217pub(super) const fn meta_to_data_size(metadata_spec: &SideMetadataSpec, meta_size: usize) -> usize {
218    meta_size << log_data_meta_ratio(metadata_spec)
219}
220
221#[allow(dead_code)]
222pub(super) const fn metadata_address_range_size(metadata_spec: &SideMetadataSpec) -> usize {
223    1usize << (VMLayout::LOG_ARCH_ADDRESS_SPACE - log_data_meta_ratio(metadata_spec))
224}
225
226pub(super) fn meta_byte_lshift(metadata_spec: &SideMetadataSpec, data_addr: Address) -> u8 {
227    let bits_num_log = metadata_spec.log_num_of_bits as i32;
228    if bits_num_log >= 3 {
229        return 0;
230    }
231    let rem_shift = BITS_IN_WORD as i32 - ((LOG_BITS_IN_BYTE as i32) - bits_num_log);
232    ((((data_addr >> metadata_spec.log_bytes_in_region) << rem_shift) >> rem_shift) << bits_num_log)
233        as u8
234}
235
236pub(super) fn meta_byte_mask(metadata_spec: &SideMetadataSpec) -> u8 {
237    let bits_num_log = metadata_spec.log_num_of_bits;
238    ((1usize << (1usize << bits_num_log)) - 1) as u8
239}
240
241/// The result type for find meta bits functions.
242pub enum FindMetaBitResult {
243    Found { addr: Address, bit: u8 },
244    NotFound,
245    UnmappedMetadata,
246}
247
248// Check and find the last bit that is set. We try load words where possible, and fall back to load bytes.
249pub fn find_last_non_zero_bit_in_metadata_bytes(
250    meta_start: Address,
251    meta_end: Address,
252) -> FindMetaBitResult {
253    use crate::util::constants::BYTES_IN_ADDRESS;
254
255    let mmap_granularity = MMAPPER.granularity();
256
257    let mut cur = meta_end;
258    // We need to check if metadata address is mapped or not.  But we make use of the granularity of
259    // the `Mmapper` to reduce the number of checks.  This records the start of a grain that is
260    // tested to be mapped.
261    let mut mapped_grain = Address::MAX;
262    while cur > meta_start {
263        // If we can check the whole word, set step to word size. Otherwise, the step is 1 (byte) and we check byte.
264        let step = if cur.is_aligned_to(BYTES_IN_ADDRESS) && cur - BYTES_IN_ADDRESS >= meta_start {
265            BYTES_IN_ADDRESS
266        } else {
267            1
268        };
269        // Move to the address so we can load from it
270        cur -= step;
271        // The value we check has to be in the range.
272        debug_assert!(
273            cur >= meta_start && cur < meta_end,
274            "Check metadata value at meta address {}, which is not in the range of [{}, {})",
275            cur,
276            meta_start,
277            meta_end
278        );
279
280        // If we are looking at an address that is not in a mapped chunk, we need to check if the chunk if mapped.
281        if cur < mapped_grain {
282            if cur.is_mapped() {
283                // This is mapped. No need to check for this chunk.
284                mapped_grain = cur.align_down(mmap_granularity);
285            } else {
286                return FindMetaBitResult::UnmappedMetadata;
287            }
288        }
289
290        if step == BYTES_IN_ADDRESS {
291            // Load and check a usize word
292            let value = unsafe { cur.load::<usize>() };
293            if value != 0 {
294                let bit = find_last_non_zero_bit::<usize>(value, 0, usize::BITS as u8).unwrap();
295                let byte_offset = bit >> LOG_BITS_IN_BYTE;
296                let bit_offset = bit - ((byte_offset) << LOG_BITS_IN_BYTE);
297                return FindMetaBitResult::Found {
298                    addr: cur + byte_offset as usize,
299                    bit: bit_offset,
300                };
301            }
302        } else {
303            // Load and check a byte
304            let value = unsafe { cur.load::<u8>() };
305            if let Some(bit) = find_last_non_zero_bit::<u8>(value, 0, 8) {
306                return FindMetaBitResult::Found { addr: cur, bit };
307            }
308        }
309    }
310    FindMetaBitResult::NotFound
311}
312
313// Check and find the last non-zero bit in the same byte.
314pub fn find_last_non_zero_bit_in_metadata_bits(
315    addr: Address,
316    start_bit: u8,
317    end_bit: u8,
318) -> FindMetaBitResult {
319    if !addr.is_mapped() {
320        return FindMetaBitResult::UnmappedMetadata;
321    }
322    let byte = unsafe { addr.load::<u8>() };
323    if let Some(bit) = find_last_non_zero_bit::<u8>(byte, start_bit, end_bit) {
324        return FindMetaBitResult::Found { addr, bit };
325    }
326    FindMetaBitResult::NotFound
327}
328
329use num_traits::{CheckedShl, PrimInt};
330fn find_last_non_zero_bit<T>(value: T, start: u8, end: u8) -> Option<u8>
331where
332    T: PrimInt + CheckedShl,
333{
334    let mask = match T::one().checked_shl((end - start) as u32) {
335        Some(shl) => (shl - T::one()) << (start as u32),
336        None => T::max_value() << (start as u32),
337    };
338    let masked = value & mask;
339    if masked.is_zero() {
340        None
341    } else {
342        let leading_zeroes = masked.leading_zeros();
343        let total_bits = std::mem::size_of::<T>() * u8::BITS as usize;
344        Some(total_bits as u8 - leading_zeroes as u8 - 1)
345    }
346}
347
348pub fn scan_non_zero_bits_in_metadata_bytes(
349    meta_start: Address,
350    meta_end: Address,
351    visit_bit: &mut impl FnMut(Address, BitOffset),
352) {
353    use crate::util::constants::BYTES_IN_ADDRESS;
354
355    let mut cursor = meta_start;
356    while cursor < meta_end && !cursor.is_aligned_to(BYTES_IN_ADDRESS) {
357        let byte = unsafe { cursor.load::<u8>() };
358        scan_non_zero_bits_in_metadata_word(cursor, byte as usize, visit_bit);
359        cursor += 1usize;
360    }
361
362    while cursor + BYTES_IN_ADDRESS < meta_end {
363        let word = unsafe { cursor.load::<usize>() };
364        scan_non_zero_bits_in_metadata_word(cursor, word, visit_bit);
365        cursor += BYTES_IN_ADDRESS;
366    }
367
368    while cursor < meta_end {
369        let byte = unsafe { cursor.load::<u8>() };
370        scan_non_zero_bits_in_metadata_word(cursor, byte as usize, visit_bit);
371        cursor += 1usize;
372    }
373}
374
375fn scan_non_zero_bits_in_metadata_word(
376    meta_addr: Address,
377    mut word: usize,
378    visit_bit: &mut impl FnMut(Address, BitOffset),
379) {
380    while word != 0 {
381        let bit = word.trailing_zeros();
382        visit_bit(meta_addr, bit as u8);
383        word = word & (word - 1);
384    }
385}
386
387pub fn scan_non_zero_bits_in_metadata_bits(
388    meta_addr: Address,
389    bit_start: BitOffset,
390    bit_end: BitOffset,
391    visit_bit: &mut impl FnMut(Address, BitOffset),
392) {
393    let byte = unsafe { meta_addr.load::<u8>() };
394    for bit in bit_start..bit_end {
395        if byte & (1 << bit) != 0 {
396            visit_bit(meta_addr, bit);
397        }
398    }
399}
400
401#[cfg(test)]
402mod tests {
403    use super::*;
404    use crate::util::metadata::side_metadata::*;
405
406    fn test_round_trip_conversion(spec: &SideMetadataSpec, test_data: &[Address]) {
407        for ref_addr in test_data {
408            let addr = *ref_addr;
409
410            // This is an aligned address. When we do roundtrip conversion, we will get back the original address.
411            {
412                assert!(addr.is_aligned_to(1 << spec.log_bytes_in_region));
413                let meta_addr = address_to_contiguous_meta_address(spec, addr);
414                let shift = meta_byte_lshift(spec, addr);
415                assert_eq!(
416                    contiguous_meta_address_to_address(spec, meta_addr, shift),
417                    addr
418                );
419            }
420
421            // This is an unaligned address. When we do roundtrip conversion, we will get the aligned address.
422            {
423                let next_addr = addr + 1usize;
424                let meta_addr = address_to_contiguous_meta_address(spec, next_addr);
425                let shift = meta_byte_lshift(spec, next_addr);
426                assert_eq!(
427                    contiguous_meta_address_to_address(spec, meta_addr, shift),
428                    addr
429                ); // we get back addr (which is the aligned address)
430            }
431        }
432    }
433
434    const TEST_ADDRESS_8B_REGION: [Address; 8] = [
435        unsafe { Address::from_usize(0x8000_0000) },
436        unsafe { Address::from_usize(0x8000_0008) },
437        unsafe { Address::from_usize(0x8000_0010) },
438        unsafe { Address::from_usize(0x8000_0018) },
439        unsafe { Address::from_usize(0x8000_0020) },
440        unsafe { Address::from_usize(0x8001_0000) },
441        unsafe { Address::from_usize(0x8001_0008) },
442        unsafe { Address::from_usize(0xd000_0000) },
443    ];
444
445    #[test]
446    fn test_contiguous_metadata_conversion_0_3() {
447        let spec = SideMetadataSpec {
448            name: "ContiguousMetadataTestSpec",
449            is_global: true,
450            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
451            log_num_of_bits: 0,
452            log_bytes_in_region: 3,
453        };
454
455        test_round_trip_conversion(&spec, &TEST_ADDRESS_8B_REGION);
456    }
457
458    #[test]
459    fn test_contiguous_metadata_conversion_1_3() {
460        let spec = SideMetadataSpec {
461            name: "ContiguousMetadataTestSpec",
462            is_global: true,
463            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
464            log_num_of_bits: 1,
465            log_bytes_in_region: 3,
466        };
467
468        test_round_trip_conversion(&spec, &TEST_ADDRESS_8B_REGION);
469    }
470
471    #[test]
472    fn test_contiguous_metadata_conversion_4_3() {
473        let spec = SideMetadataSpec {
474            name: "ContiguousMetadataTestSpec",
475            is_global: true,
476            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
477            log_num_of_bits: 4,
478            log_bytes_in_region: 3,
479        };
480
481        test_round_trip_conversion(&spec, &TEST_ADDRESS_8B_REGION);
482    }
483
484    #[test]
485    fn test_contiguous_metadata_conversion_5_3() {
486        let spec = SideMetadataSpec {
487            name: "ContiguousMetadataTestSpec",
488            is_global: true,
489            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
490            log_num_of_bits: 5,
491            log_bytes_in_region: 3,
492        };
493
494        test_round_trip_conversion(&spec, &TEST_ADDRESS_8B_REGION);
495    }
496
497    const TEST_ADDRESS_4KB_REGION: [Address; 8] = [
498        unsafe { Address::from_usize(0x8000_0000) },
499        unsafe { Address::from_usize(0x8000_1000) },
500        unsafe { Address::from_usize(0x8000_2000) },
501        unsafe { Address::from_usize(0x8000_3000) },
502        unsafe { Address::from_usize(0x8000_4000) },
503        unsafe { Address::from_usize(0x8001_0000) },
504        unsafe { Address::from_usize(0x8001_1000) },
505        unsafe { Address::from_usize(0xd000_0000) },
506    ];
507
508    #[test]
509    fn test_contiguous_metadata_conversion_0_12() {
510        let spec = SideMetadataSpec {
511            name: "ContiguousMetadataTestSpec",
512            is_global: true,
513            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
514            log_num_of_bits: 0,
515            log_bytes_in_region: 12, // 4K
516        };
517
518        test_round_trip_conversion(&spec, &TEST_ADDRESS_4KB_REGION);
519    }
520
521    #[test]
522    fn test_find_last_non_zero_bit_in_u8() {
523        use super::find_last_non_zero_bit;
524        let bit = find_last_non_zero_bit::<u8>(0b100101, 0, 1);
525        assert_eq!(bit, Some(0));
526
527        let bit = find_last_non_zero_bit::<u8>(0b100101, 0, 3);
528        assert_eq!(bit, Some(2));
529
530        let bit = find_last_non_zero_bit::<u8>(0b100101, 0, 8);
531        assert_eq!(bit, Some(5));
532
533        let bit = find_last_non_zero_bit::<u8>(0b0, 0, 1);
534        assert_eq!(bit, None);
535    }
536
537    #[test]
538    fn test_align_metadata_address() {
539        let create_spec = |log_num_of_bits: usize| SideMetadataSpec {
540            name: "AlignMetadataBitTestSpec",
541            is_global: true,
542            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
543            log_num_of_bits,
544            log_bytes_in_region: 3,
545        };
546
547        const ADDR_1000: Address = unsafe { Address::from_usize(0x1000) };
548        const ADDR_1001: Address = unsafe { Address::from_usize(0x1001) };
549        const ADDR_1002: Address = unsafe { Address::from_usize(0x1002) };
550        const ADDR_1003: Address = unsafe { Address::from_usize(0x1003) };
551        const ADDR_1004: Address = unsafe { Address::from_usize(0x1004) };
552        const ADDR_1005: Address = unsafe { Address::from_usize(0x1005) };
553        const ADDR_1006: Address = unsafe { Address::from_usize(0x1006) };
554        const ADDR_1007: Address = unsafe { Address::from_usize(0x1007) };
555        const ADDR_1008: Address = unsafe { Address::from_usize(0x1008) };
556        const ADDR_1009: Address = unsafe { Address::from_usize(0x1009) };
557
558        let metadata_2bits = create_spec(1);
559        assert_eq!(
560            align_metadata_address(&metadata_2bits, ADDR_1000, 0),
561            (ADDR_1000, 0)
562        );
563        assert_eq!(
564            align_metadata_address(&metadata_2bits, ADDR_1000, 1),
565            (ADDR_1000, 0)
566        );
567        assert_eq!(
568            align_metadata_address(&metadata_2bits, ADDR_1000, 2),
569            (ADDR_1000, 2)
570        );
571        assert_eq!(
572            align_metadata_address(&metadata_2bits, ADDR_1000, 3),
573            (ADDR_1000, 2)
574        );
575        assert_eq!(
576            align_metadata_address(&metadata_2bits, ADDR_1000, 4),
577            (ADDR_1000, 4)
578        );
579        assert_eq!(
580            align_metadata_address(&metadata_2bits, ADDR_1000, 5),
581            (ADDR_1000, 4)
582        );
583        assert_eq!(
584            align_metadata_address(&metadata_2bits, ADDR_1000, 6),
585            (ADDR_1000, 6)
586        );
587        assert_eq!(
588            align_metadata_address(&metadata_2bits, ADDR_1000, 7),
589            (ADDR_1000, 6)
590        );
591
592        let metadata_4bits = create_spec(2);
593        assert_eq!(
594            align_metadata_address(&metadata_4bits, ADDR_1000, 0),
595            (ADDR_1000, 0)
596        );
597        assert_eq!(
598            align_metadata_address(&metadata_4bits, ADDR_1000, 1),
599            (ADDR_1000, 0)
600        );
601        assert_eq!(
602            align_metadata_address(&metadata_4bits, ADDR_1000, 2),
603            (ADDR_1000, 0)
604        );
605        assert_eq!(
606            align_metadata_address(&metadata_4bits, ADDR_1000, 3),
607            (ADDR_1000, 0)
608        );
609        assert_eq!(
610            align_metadata_address(&metadata_4bits, ADDR_1000, 4),
611            (ADDR_1000, 4)
612        );
613        assert_eq!(
614            align_metadata_address(&metadata_4bits, ADDR_1000, 5),
615            (ADDR_1000, 4)
616        );
617        assert_eq!(
618            align_metadata_address(&metadata_4bits, ADDR_1000, 6),
619            (ADDR_1000, 4)
620        );
621        assert_eq!(
622            align_metadata_address(&metadata_4bits, ADDR_1000, 7),
623            (ADDR_1000, 4)
624        );
625
626        let metadata_8bits = create_spec(3);
627        assert_eq!(
628            align_metadata_address(&metadata_8bits, ADDR_1000, 0),
629            (ADDR_1000, 0)
630        );
631        assert_eq!(
632            align_metadata_address(&metadata_8bits, ADDR_1000, 1),
633            (ADDR_1000, 0)
634        );
635        assert_eq!(
636            align_metadata_address(&metadata_8bits, ADDR_1000, 2),
637            (ADDR_1000, 0)
638        );
639        assert_eq!(
640            align_metadata_address(&metadata_8bits, ADDR_1000, 3),
641            (ADDR_1000, 0)
642        );
643        assert_eq!(
644            align_metadata_address(&metadata_8bits, ADDR_1000, 4),
645            (ADDR_1000, 0)
646        );
647        assert_eq!(
648            align_metadata_address(&metadata_8bits, ADDR_1000, 5),
649            (ADDR_1000, 0)
650        );
651        assert_eq!(
652            align_metadata_address(&metadata_8bits, ADDR_1000, 6),
653            (ADDR_1000, 0)
654        );
655        assert_eq!(
656            align_metadata_address(&metadata_8bits, ADDR_1000, 7),
657            (ADDR_1000, 0)
658        );
659
660        let metadata_16bits = create_spec(4);
661        assert_eq!(
662            align_metadata_address(&metadata_16bits, ADDR_1000, 0),
663            (ADDR_1000, 0)
664        );
665        assert_eq!(
666            align_metadata_address(&metadata_16bits, ADDR_1000, 1),
667            (ADDR_1000, 0)
668        );
669        assert_eq!(
670            align_metadata_address(&metadata_16bits, ADDR_1000, 2),
671            (ADDR_1000, 0)
672        );
673        assert_eq!(
674            align_metadata_address(&metadata_16bits, ADDR_1000, 3),
675            (ADDR_1000, 0)
676        );
677        assert_eq!(
678            align_metadata_address(&metadata_16bits, ADDR_1000, 4),
679            (ADDR_1000, 0)
680        );
681        assert_eq!(
682            align_metadata_address(&metadata_16bits, ADDR_1000, 5),
683            (ADDR_1000, 0)
684        );
685        assert_eq!(
686            align_metadata_address(&metadata_16bits, ADDR_1000, 6),
687            (ADDR_1000, 0)
688        );
689        assert_eq!(
690            align_metadata_address(&metadata_16bits, ADDR_1000, 7),
691            (ADDR_1000, 0)
692        );
693        assert_eq!(
694            align_metadata_address(&metadata_16bits, ADDR_1001, 0),
695            (ADDR_1000, 0)
696        );
697        assert_eq!(
698            align_metadata_address(&metadata_16bits, ADDR_1001, 1),
699            (ADDR_1000, 0)
700        );
701        assert_eq!(
702            align_metadata_address(&metadata_16bits, ADDR_1001, 2),
703            (ADDR_1000, 0)
704        );
705        assert_eq!(
706            align_metadata_address(&metadata_16bits, ADDR_1001, 3),
707            (ADDR_1000, 0)
708        );
709        assert_eq!(
710            align_metadata_address(&metadata_16bits, ADDR_1001, 4),
711            (ADDR_1000, 0)
712        );
713        assert_eq!(
714            align_metadata_address(&metadata_16bits, ADDR_1001, 5),
715            (ADDR_1000, 0)
716        );
717        assert_eq!(
718            align_metadata_address(&metadata_16bits, ADDR_1001, 6),
719            (ADDR_1000, 0)
720        );
721        assert_eq!(
722            align_metadata_address(&metadata_16bits, ADDR_1001, 7),
723            (ADDR_1000, 0)
724        );
725    }
726}