@@ -47,7 +47,12 @@ mod imp {
47
47
/// Get a reference to the ingredient in the database.
48
48
///
49
49
/// If the ingredient index is not already in the cache, it will be loaded and cached.
50
- pub fn get_or_create < ' db > (
50
+ ///
51
+ /// # Safety
52
+ ///
53
+ /// The `IngredientIndex` returned by the closure must reference a valid ingredient of
54
+ /// type `I` in the provided zalsa database.
55
+ pub unsafe fn get_or_create < ' db > (
51
56
& self ,
52
57
zalsa : & ' db Zalsa ,
53
58
load_index : impl Fn ( ) -> IngredientIndex ,
@@ -57,9 +62,21 @@ mod imp {
57
62
ingredient_index = self . get_or_create_index_slow ( load_index) . as_u32 ( ) ;
58
63
} ;
59
64
60
- zalsa
61
- . lookup_ingredient ( IngredientIndex :: from_unchecked ( ingredient_index) )
62
- . assert_type ( )
65
+ // SAFETY: `ingredient_index` is initialized from a valid `IngredientIndex`.
66
+ let ingredient_index = unsafe { IngredientIndex :: new_unchecked ( ingredient_index) } ;
67
+
68
+ // SAFETY: There are a two cases here:
69
+ // - The `create_index` closure was called due to the data being uncached. In this
70
+ // case, the caller guarantees the index is in-bounds and has the correct type.
71
+ // - The index was cached. While the current database might not be the same database
72
+ // the ingredient was initially loaded from, the `inventory` feature is enabled, so
73
+ // ingredient indices are stable across databases. Thus the index is still in-bounds
74
+ // and has the correct type.
75
+ unsafe {
76
+ zalsa
77
+ . lookup_ingredient_unchecked ( ingredient_index)
78
+ . assert_type_unchecked ( )
79
+ }
63
80
}
64
81
65
82
#[ cold]
@@ -134,14 +151,30 @@ mod imp {
134
151
/// Get a reference to the ingredient in the database.
135
152
///
136
153
/// If the ingredient is not already in the cache, it will be created.
154
+ ///
155
+ /// # Safety
156
+ ///
157
+ /// The `IngredientIndex` returned by the closure must reference a valid ingredient of
158
+ /// type `I` in the provided zalsa database.
137
159
#[ inline( always) ]
138
- pub fn get_or_create < ' db > (
160
+ pub unsafe fn get_or_create < ' db > (
139
161
& self ,
140
162
zalsa : & ' db Zalsa ,
141
163
create_index : impl Fn ( ) -> IngredientIndex ,
142
164
) -> & ' db I {
143
165
let index = self . get_or_create_index ( zalsa, create_index) ;
144
- zalsa. lookup_ingredient ( index) . assert_type :: < I > ( )
166
+
167
+ // SAFETY: There are a two cases here:
168
+ // - The `create_index` closure was called due to the data being uncached for the
169
+ // provided database. In this case, the caller guarantees the index is in-bounds
170
+ // and has the correct type.
171
+ // - We verified the index was cached for the same database, by the nonce check.
172
+ // Thus the initial safety argument still applies.
173
+ unsafe {
174
+ zalsa
175
+ . lookup_ingredient_unchecked ( index)
176
+ . assert_type_unchecked :: < I > ( )
177
+ }
145
178
}
146
179
147
180
pub fn get_or_create_index (
@@ -159,7 +192,9 @@ mod imp {
159
192
} ;
160
193
161
194
// Unpack our `u64` into the nonce and index.
162
- let index = IngredientIndex :: from_unchecked ( cached_data as u32 ) ;
195
+ //
196
+ // SAFETY: The lower bits of `cached_data` are initialized from a valid `IngredientIndex`.
197
+ let index = unsafe { IngredientIndex :: new_unchecked ( cached_data as u32 ) } ;
163
198
164
199
// SAFETY: We've checked against `UNINITIALIZED` (0) above and so the upper bits must be non-zero.
165
200
let nonce = crate :: nonce:: Nonce :: < StorageNonce > :: from_u32 ( unsafe {
0 commit comments