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 main design principle for this module is to centralize all uses of `as`.
7The thinking here is that `as` makes it very easy to perform accidental lossy
8conversions, and if we centralize all its uses here under more descriptive
9higher level operations, its use and correctness becomes easier to audit.
10
11This was copied mostly wholesale from `regex-automata`.
12
13NOTE: for simplicity, we don't take target pointer width into account here for
14`usize` conversions. Since we currently only panic in debug mode, skipping the
15check when it can be proven it isn't needed at compile time doesn't really
16matter. Now, if we wind up wanting to do as many checks as possible in release
17mode, then we would want to skip those when we know the conversions are always
18non-lossy.
19*/
20
21pub(crate) trait U8 {
22 fn as_usize(self) -> usize;
23}
24
25impl U8 for u8 {
26 fn as_usize(self) -> usize {
27 usize::from(self)
28 }
29}
30
31pub(crate) trait U16 {
32 fn as_usize(self) -> usize;
33 fn low_u8(self) -> u8;
34 fn high_u8(self) -> u8;
35}
36
37impl U16 for u16 {
38 fn as_usize(self) -> usize {
39 usize::from(self)
40 }
41
42 fn low_u8(self) -> u8 {
43 self as u8
44 }
45
46 fn high_u8(self) -> u8 {
47 (self >> 8) as u8
48 }
49}
50
51pub(crate) trait U32 {
52 fn as_usize(self) -> usize;
53 fn low_u8(self) -> u8;
54 fn low_u16(self) -> u16;
55 fn high_u16(self) -> u16;
56}
57
58impl U32 for u32 {
59 #[inline]
60 fn as_usize(self) -> usize {
61 #[cfg(debug_assertions)]
62 {
63 usize::try_from(self).expect("u32 overflowed usize")
64 }
65 #[cfg(not(debug_assertions))]
66 {
67 self as usize
68 }
69 }
70
71 fn low_u8(self) -> u8 {
72 self as u8
73 }
74
75 fn low_u16(self) -> u16 {
76 self as u16
77 }
78
79 fn high_u16(self) -> u16 {
80 (self >> 16) as u16
81 }
82}
83
84pub(crate) trait U64 {
85 fn as_usize(self) -> usize;
86 fn low_u8(self) -> u8;
87 fn low_u16(self) -> u16;
88 fn low_u32(self) -> u32;
89 fn high_u32(self) -> u32;
90}
91
92impl U64 for u64 {
93 fn as_usize(self) -> usize {
94 #[cfg(debug_assertions)]
95 {
96 usize::try_from(self).expect("u64 overflowed usize")
97 }
98 #[cfg(not(debug_assertions))]
99 {
100 self as usize
101 }
102 }
103
104 fn low_u8(self) -> u8 {
105 self as u8
106 }
107
108 fn low_u16(self) -> u16 {
109 self as u16
110 }
111
112 fn low_u32(self) -> u32 {
113 self as u32
114 }
115
116 fn high_u32(self) -> u32 {
117 (self >> 32) as u32
118 }
119}
120
121pub(crate) trait I8 {
122 fn as_usize(self) -> usize;
123 fn to_bits(self) -> u8;
124 fn from_bits(n: u8) -> i8;
125}
126
127impl I8 for i8 {
128 fn as_usize(self) -> usize {
129 #[cfg(debug_assertions)]
130 {
131 usize::try_from(self).expect("i8 overflowed usize")
132 }
133 #[cfg(not(debug_assertions))]
134 {
135 self as usize
136 }
137 }
138
139 fn to_bits(self) -> u8 {
140 self as u8
141 }
142
143 fn from_bits(n: u8) -> i8 {
144 n as i8
145 }
146}
147
148pub(crate) trait I32 {
149 fn as_usize(self) -> usize;
150 fn to_bits(self) -> u32;
151 fn from_bits(n: u32) -> i32;
152}
153
154impl I32 for i32 {
155 fn as_usize(self) -> usize {
156 #[cfg(debug_assertions)]
157 {
158 usize::try_from(self).expect("i32 overflowed usize")
159 }
160 #[cfg(not(debug_assertions))]
161 {
162 self as usize
163 }
164 }
165
166 fn to_bits(self) -> u32 {
167 self as u32
168 }
169
170 fn from_bits(n: u32) -> i32 {
171 n as i32
172 }
173}
174
175pub(crate) trait I64 {
176 fn as_usize(self) -> usize;
177 fn to_bits(self) -> u64;
178 fn from_bits(n: u64) -> i64;
179}
180
181impl I64 for i64 {
182 fn as_usize(self) -> usize {
183 #[cfg(debug_assertions)]
184 {
185 usize::try_from(self).expect("i64 overflowed usize")
186 }
187 #[cfg(not(debug_assertions))]
188 {
189 self as usize
190 }
191 }
192
193 fn to_bits(self) -> u64 {
194 self as u64
195 }
196
197 fn from_bits(n: u64) -> i64 {
198 n as i64
199 }
200}
201
202pub(crate) trait Usize {
203 fn as_u8(self) -> u8;
204 fn as_u16(self) -> u16;
205 fn as_u32(self) -> u32;
206 fn as_u64(self) -> u64;
207}
208
209impl Usize for usize {
210 fn as_u8(self) -> u8 {
211 #[cfg(debug_assertions)]
212 {
213 u8::try_from(self).expect("usize overflowed u8")
214 }
215 #[cfg(not(debug_assertions))]
216 {
217 self as u8
218 }
219 }
220
221 fn as_u16(self) -> u16 {
222 #[cfg(debug_assertions)]
223 {
224 u16::try_from(self).expect("usize overflowed u16")
225 }
226 #[cfg(not(debug_assertions))]
227 {
228 self as u16
229 }
230 }
231
232 fn as_u32(self) -> u32 {
233 #[cfg(debug_assertions)]
234 {
235 u32::try_from(self).expect("usize overflowed u32")
236 }
237 #[cfg(not(debug_assertions))]
238 {
239 self as u32
240 }
241 }
242
243 fn as_u64(self) -> u64 {
244 #[cfg(debug_assertions)]
245 {
246 u64::try_from(self).expect("usize overflowed u64")
247 }
248 #[cfg(not(debug_assertions))]
249 {
250 self as u64
251 }
252 }
253}
254
255// Pointers aren't integers, but we convert pointers to integers to perform
256// offset arithmetic in some places. (And no, we don't convert the integers
257// back to pointers.) So add 'as_usize' conversions here too for completeness.
258//
259// These 'as' casts are actually okay because they're always non-lossy. But the
260// idea here is to just try and remove as much 'as' as possible, particularly
261// in this crate where we are being really paranoid about offsets and making
262// sure we don't panic on inputs that might be untrusted. This way, the 'as'
263// casts become easier to audit if they're all in one place, even when some of
264// them are actually okay 100% of the time.
265
266pub(crate) trait Pointer {
267 fn as_usize(self) -> usize;
268}
269
270impl<T> Pointer for *const T {
271 fn as_usize(self) -> usize {
272 self as usize
273 }
274}
275
276pub(crate) trait PointerMut {
277 fn as_usize(self) -> usize;
278}
279
280impl<T> PointerMut for *mut T {
281 fn as_usize(self) -> usize {
282 self as usize
283 }
284}
285