1
1
use std:: fmt:: Debug ;
2
2
use std:: hash:: Hash ;
3
- use std:: num:: NonZeroU32 ;
3
+ use std:: num:: NonZeroU64 ;
4
4
5
5
use crate :: zalsa:: Zalsa ;
6
6
7
7
/// The `Id` of a salsa struct in the database [`Table`](`crate::table::Table`).
8
8
///
9
- /// The higher -order bits of an `Id` identify a [`Page`](`crate::table::Page`)
10
- /// and the low-order bits identify a slot within the page .
9
+ /// The high -order bits of an `Id` store a 16-bit generation counter
10
+ /// as well as an optional 16-bit ingredient index .
11
11
///
12
- /// An Id is a newtype'd u32 ranging from `0..Id::MAX_U32`.
13
- /// The maximum range is smaller than a standard u32 to leave
12
+ /// The low-order bits pack a [`PageIndex`](`crate::table::PageIndex`) and
13
+ /// [`SlotIndex`](`crate::table::SlotIndex`) within the page.
14
+ ///
15
+ /// The low-order bits of `Id` are a `u32` ranging from `0..Id::MAX_U32`.
16
+ /// The maximum range is smaller than a standard `u32` to leave
14
17
/// room for niches; currently there is only one niche, so that
15
18
/// `Option<Id>` is the same size as an `Id`.
16
19
///
17
20
/// As an end-user of `Salsa` you will generally not use `Id` directly,
18
21
/// it is wrapped in new types.
19
22
#[ derive( Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
20
23
pub struct Id {
21
- value : NonZeroU32 ,
24
+ value : NonZeroU64 ,
22
25
}
23
26
24
27
impl Id {
25
28
pub const MAX_U32 : u32 = u32:: MAX - 0xFF ;
26
29
pub const MAX_USIZE : usize = Self :: MAX_U32 as usize ;
27
30
28
- /// Create a `salsa::Id` from a u32 value. This value should
29
- /// be less than [`Self::MAX_U32`].
31
+ const INGREDIENT_MASK : u64 = 0x0000FFFFFFFFFFFF ;
32
+ const GENERATION_MASK : u64 = 0xFFFF0000FFFFFFFF ;
33
+
34
+ /// Create a `salsa::Id` from a u32 value, without a generation. This
35
+ /// value should be less than [`Self::MAX_U32`].
30
36
///
31
37
/// In general, you should not need to create salsa ids yourself,
32
38
/// but it can be useful if you are using the type as a general
@@ -38,23 +44,98 @@ impl Id {
38
44
#[ doc( hidden) ]
39
45
#[ track_caller]
40
46
#[ inline]
41
- pub const unsafe fn from_u32 ( v : u32 ) -> Self {
47
+ pub const unsafe fn from_data ( v : u32 ) -> Self {
42
48
debug_assert ! ( v < Self :: MAX_U32 ) ;
49
+
50
+ Id {
51
+ // SAFETY: Caller obligation.
52
+ value : unsafe { NonZeroU64 :: new_unchecked ( ( v + 1 ) as u64 ) } ,
53
+ }
54
+ }
55
+
56
+ /// Create a `salsa::Id` from a u64 value.
57
+ ///
58
+ /// This should only be used to recreate an `Id` together with `Id::as_u64`.
59
+ ///
60
+ /// # Safety
61
+ ///
62
+ /// The data bits of the supplied value must represent a valid `Id` returned
63
+ /// by `Id::as_u64`.
64
+ #[ doc( hidden) ]
65
+ #[ track_caller]
66
+ #[ inline]
67
+ pub const unsafe fn from_bits ( v : u64 ) -> Self {
43
68
Id {
44
- // SAFETY: Caller obligation
45
- value : unsafe { NonZeroU32 :: new_unchecked ( v + 1 ) } ,
69
+ // SAFETY: Caller obligation.
70
+ value : unsafe { NonZeroU64 :: new_unchecked ( v) } ,
46
71
}
47
72
}
48
73
74
+ /// Mark the `Id` with a generation.
75
+ ///
76
+ /// This `Id` will refer to the same page and slot in the database,
77
+ /// but will differ from other identifiers of the slot based on the
78
+ /// provided generation.
49
79
#[ inline]
50
- pub const fn as_u32 ( self ) -> u32 {
51
- self . value . get ( ) - 1
80
+ pub fn with_generation ( self , generation : u16 ) -> Id {
81
+ let mut value = self . value . get ( ) ;
82
+
83
+ value &= Id :: GENERATION_MASK ;
84
+ value |= ( generation as u64 ) << 32 ;
85
+
86
+ Id {
87
+ // SAFETY: The niche of `value` is in the lower bits, which we did not touch.
88
+ value : unsafe { NonZeroU64 :: new_unchecked ( value) } ,
89
+ }
90
+ }
91
+
92
+ /// Mark the `Id` with an ingredient index.
93
+ #[ inline]
94
+ pub fn with_ingredient_index ( self , ingredient : u16 ) -> Id {
95
+ let mut value = self . value . get ( ) ;
96
+
97
+ value &= Id :: INGREDIENT_MASK ;
98
+ value |= ( ingredient as u64 ) << 48 ;
99
+
100
+ Id {
101
+ // SAFETY: The niche of `value` is in the lower bits, which we did not touch.
102
+ value : unsafe { NonZeroU64 :: new_unchecked ( value) } ,
103
+ }
104
+ }
105
+
106
+ /// Return the internal `u64` representation of this `Id`.
107
+ #[ inline]
108
+ pub const fn as_bits ( self ) -> u64 {
109
+ self . value . get ( )
110
+ }
111
+
112
+ /// Return the data portion of this `Id`.
113
+ #[ inline]
114
+ pub const fn data ( self ) -> u32 {
115
+ // Truncate the high-order bits.
116
+ ( self . value . get ( ) as u32 ) - 1
117
+ }
118
+
119
+ /// Return the generation of this `Id`.
120
+ #[ inline]
121
+ pub const fn generation ( self ) -> u16 {
122
+ ( ( self . value . get ( ) & !Id :: GENERATION_MASK ) >> 32 ) as u16
123
+ }
124
+
125
+ /// Return the ingredient index of this `Id`.
126
+ #[ inline]
127
+ pub const fn ingredient_index ( self ) -> u16 {
128
+ ( self . value . get ( ) >> 48 ) as u16
52
129
}
53
130
}
54
131
55
132
impl Debug for Id {
56
133
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
57
- write ! ( f, "Id({:x})" , self . as_u32( ) )
134
+ if self . generation ( ) == 0 {
135
+ write ! ( f, "Id({:x})" , self . data( ) )
136
+ } else {
137
+ write ! ( f, "Id({:x}g{:x})" , self . data( ) , self . generation( ) )
138
+ }
58
139
}
59
140
}
60
141
0 commit comments