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