Coverage Report

Created: 2024-11-19 11:03

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