1/*!
2This module provides several integer oriented traits for converting between
3both fixed size integers and integers whose size varies based on the target
4(like `usize`).
5
6The driving design principle of this module is to attempt to centralize as many
7`as` casts as possible here. And in particular, we separate casts into two
8buckets:
9
10* Casts that we use for their truncating behavior. In this case, we use more
11descriptive names, like `low_u32` and `high_u32`.
12* Casts that we use for converting back-and-forth between `usize`. These
13conversions are generally necessary because we often store indices in different
14formats to save on memory, which requires converting to and from `usize`. In
15this case, we very specifically do not want to overflow, and so the methods
16defined here will panic if the `as` cast would be lossy in debug mode. (A
17normal `as` cast will never panic!)
18
19For `as` casts between raw pointers, we use `cast`, so `as` isn't needed there.
20
21For regex engines, floating point is just never used, so we don't have to worry
22about `as` casts for those.
23
24Otherwise, this module pretty much covers all of our `as` needs except for one
25thing: const contexts. There are a select few places in this crate where we
26still need to use `as` because const functions on traits aren't stable yet.
27If we wind up significantly expanding our const footprint in this crate, it
28might be worth defining free functions to handle those cases. But at the time
29of writing, that just seemed like too much ceremony. Instead, I comment each
30such use of `as` in a const context with a "fixme" notice.
31
32NOTE: for simplicity, we don't take target pointer width into account here for
33`usize` conversions. Since we currently only panic in debug mode, skipping the
34check when it can be proven it isn't needed at compile time doesn't really
35matter. Now, if we wind up wanting to do as many checks as possible in release
36mode, then we would want to skip those when we know the conversions are always
37non-lossy.
38
39NOTE: this module isn't an exhaustive API. For example, we still use things
40like `u64::from` where possible, or even `usize::try_from()` for when we do
41explicitly want to panic or when we want to return an error for overflow.
42*/
43
44pub(crate) trait U8 {
45 fn as_usize(self) -> usize;
46}
47
48impl U8 for u8 {
49 fn as_usize(self) -> usize {
50 usize::from(self)
51 }
52}
53
54pub(crate) trait U16 {
55 fn as_usize(self) -> usize;
56 fn low_u8(self) -> u8;
57 fn high_u8(self) -> u8;
58}
59
60impl U16 for u16 {
61 fn as_usize(self) -> usize {
62 usize::from(self)
63 }
64
65 fn low_u8(self) -> u8 {
66 self as u8
67 }
68
69 fn high_u8(self) -> u8 {
70 (self >> 8) as u8
71 }
72}
73
74pub(crate) trait U32 {
75 fn as_usize(self) -> usize;
76 fn low_u8(self) -> u8;
77 fn low_u16(self) -> u16;
78 fn high_u16(self) -> u16;
79}
80
81impl U32 for u32 {
82 fn as_usize(self) -> usize {
83 #[cfg(debug_assertions)]
84 {
85 usize::try_from(self).expect("u32 overflowed usize")
86 }
87 #[cfg(not(debug_assertions))]
88 {
89 self as usize
90 }
91 }
92
93 fn low_u8(self) -> u8 {
94 self as u8
95 }
96
97 fn low_u16(self) -> u16 {
98 self as u16
99 }
100
101 fn high_u16(self) -> u16 {
102 (self >> 16) as u16
103 }
104}
105
106pub(crate) trait U64 {
107 fn as_usize(self) -> usize;
108 fn low_u8(self) -> u8;
109 fn low_u16(self) -> u16;
110 fn low_u32(self) -> u32;
111 fn high_u32(self) -> u32;
112}
113
114impl U64 for u64 {
115 fn as_usize(self) -> usize {
116 #[cfg(debug_assertions)]
117 {
118 usize::try_from(self).expect("u64 overflowed usize")
119 }
120 #[cfg(not(debug_assertions))]
121 {
122 self as usize
123 }
124 }
125
126 fn low_u8(self) -> u8 {
127 self as u8
128 }
129
130 fn low_u16(self) -> u16 {
131 self as u16
132 }
133
134 fn low_u32(self) -> u32 {
135 self as u32
136 }
137
138 fn high_u32(self) -> u32 {
139 (self >> 32) as u32
140 }
141}
142
143pub(crate) trait I32 {
144 fn as_usize(self) -> usize;
145 fn to_bits(self) -> u32;
146 fn from_bits(n: u32) -> i32;
147}
148
149impl I32 for i32 {
150 fn as_usize(self) -> usize {
151 #[cfg(debug_assertions)]
152 {
153 usize::try_from(self).expect("i32 overflowed usize")
154 }
155 #[cfg(not(debug_assertions))]
156 {
157 self as usize
158 }
159 }
160
161 fn to_bits(self) -> u32 {
162 self as u32
163 }
164
165 fn from_bits(n: u32) -> i32 {
166 n as i32
167 }
168}
169
170pub(crate) trait Usize {
171 fn as_u8(self) -> u8;
172 fn as_u16(self) -> u16;
173 fn as_u32(self) -> u32;
174 fn as_u64(self) -> u64;
175}
176
177impl Usize for usize {
178 fn as_u8(self) -> u8 {
179 #[cfg(debug_assertions)]
180 {
181 u8::try_from(self).expect("usize overflowed u8")
182 }
183 #[cfg(not(debug_assertions))]
184 {
185 self as u8
186 }
187 }
188
189 fn as_u16(self) -> u16 {
190 #[cfg(debug_assertions)]
191 {
192 u16::try_from(self).expect("usize overflowed u16")
193 }
194 #[cfg(not(debug_assertions))]
195 {
196 self as u16
197 }
198 }
199
200 fn as_u32(self) -> u32 {
201 #[cfg(debug_assertions)]
202 {
203 u32::try_from(self).expect("usize overflowed u32")
204 }
205 #[cfg(not(debug_assertions))]
206 {
207 self as u32
208 }
209 }
210
211 fn as_u64(self) -> u64 {
212 #[cfg(debug_assertions)]
213 {
214 u64::try_from(self).expect("usize overflowed u64")
215 }
216 #[cfg(not(debug_assertions))]
217 {
218 self as u64
219 }
220 }
221}
222
223// Pointers aren't integers, but we convert pointers to integers to perform
224// offset arithmetic in some places. (And no, we don't convert the integers
225// back to pointers.) So add 'as_usize' conversions here too for completeness.
226//
227// These 'as' casts are actually okay because they're always non-lossy. But the
228// idea here is to just try and remove as much 'as' as possible, particularly
229// in this crate where we are being really paranoid about offsets and making
230// sure we don't panic on inputs that might be untrusted. This way, the 'as'
231// casts become easier to audit if they're all in one place, even when some of
232// them are actually okay 100% of the time.
233
234pub(crate) trait Pointer {
235 fn as_usize(self) -> usize;
236}
237
238impl<T> Pointer for *const T {
239 fn as_usize(self) -> usize {
240 self as usize
241 }
242}
243
244pub(crate) trait PointerMut {
245 fn as_usize(self) -> usize;
246}
247
248impl<T> PointerMut for *mut T {
249 fn as_usize(self) -> usize {
250 self as usize
251 }
252}
253