flize/
tag.rs

1use core::mem;
2use generic_array::{
3    typenum::{UTerm, Unsigned},
4    ArrayLength, GenericArray,
5};
6
7pub enum TagPosition {
8    Lo,
9    Hi,
10}
11
12impl TagPosition {
13    /// Calculates the start bit offset of the tag depending on the position and type.
14    fn to_skip<T: Tag>(&self) -> usize {
15        match self {
16            // low tags always start at 0
17            TagPosition::Lo => 0,
18
19            // high tags occupy the highest bits so the start offset is the max index minus the size
20            TagPosition::Hi => {
21                let usize_bits = mem::size_of::<usize>() * 8;
22                usize_bits - <T::Size as Unsigned>::to_usize()
23            }
24        }
25    }
26}
27
28/// Zeroes all the tag bits.
29pub fn strip<T1: Tag, T2: Tag>(data: usize) -> usize {
30    // mask for zeroing the low tag
31    let mask1: usize = core::usize::MAX >> <T1::Size as Unsigned>::to_usize();
32
33    // mask for zeroing the high tag
34    let mask2: usize = core::usize::MAX << <T2::Size as Unsigned>::to_usize();
35
36    // apply the masks with an AND to zero the bits
37    data & mask1 & mask2
38}
39
40/// Read the bits of a tag a a certain position.
41pub fn read_tag<T: Tag>(data: usize, position: TagPosition) -> GenericArray<bool, T::Size> {
42    let to_skip = position.to_skip::<T>();
43    let mut array = GenericArray::default();
44
45    array
46        .iter_mut()
47        .enumerate()
48        .skip(to_skip)
49        .for_each(|(index, bit)| *bit = ((data >> index) & 1) == 1);
50
51    array
52}
53
54/// Set the bits of a tag at a certain position.
55pub fn set_tag<T: Tag>(
56    mut data: usize,
57    bits: GenericArray<bool, T::Size>,
58    position: TagPosition,
59) -> usize {
60    let to_skip = position.to_skip::<T>();
61
62    bits.iter()
63        .enumerate()
64        .skip(to_skip)
65        .for_each(|(index, bit)| {
66            let value = if *bit { 1 } else { 0 };
67            data = (data & !(1 << index)) | (value << index);
68        });
69
70    data
71}
72
73/// The `Tag` trait represents any struct that can be serialized
74/// and packed into the unused bits of a pointer producing
75/// a so called "tagged" pointer.
76/// The amount of bits available are variable and the amount
77/// you can use depends on whether the tag is in in the low or high position.
78///
79/// In low position you can use as many bits as must be zero due to
80/// alignment. If you don't know the alignment of your pointer you can assume it is
81/// that of the value it is pointing to. The amount of available bits in the low
82/// position is the binary logarithm of the alignment in bytes.
83///
84/// In high position the number of available bits is determined by your compilation target.
85/// On 32 bit architectures this number shall be assumed to be equal to 0.
86/// On x86_64 with 4 level paging the number of available bits is 16 and with level
87/// 5 paging it is 8 bits. On 64 bit ARM without pointer authentication you also have 16
88/// available bits. With pointer authentication you can only reasonably assume you have 0 available
89/// bits unless you know otherwise for your compiler. On all other architectures assume you have
90/// 0 available bits unless you know otherwise.
91pub trait Tag: Copy {
92    /// The size in bits of the tag.
93    type Size: ArrayLength<bool>;
94
95    /// Deserialize an array of bits into the tag.
96    fn deserialize(bits: GenericArray<bool, Self::Size>) -> Self;
97
98    /// Serialize the tag to an array of bits.
99    fn serialize(self) -> GenericArray<bool, Self::Size>;
100}
101
102/// This tag is a placeholder type that has a size of 0 and stores no state.
103/// If you don't have any tag with information you want to store, this is the default.
104#[derive(Debug, Clone, Copy)]
105pub struct NullTag;
106
107impl Tag for NullTag {
108    type Size = UTerm;
109
110    fn deserialize(_bits: GenericArray<bool, Self::Size>) -> Self {
111        Self
112    }
113
114    fn serialize(self) -> GenericArray<bool, Self::Size> {
115        GenericArray::default()
116    }
117}