Coverage Report

Created: 2025-10-31 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.9/src/util/int.rs
Line
Count
Source
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
// We define a little more than what we need, but I'd rather just have
45
// everything via a consistent and uniform API then have holes.
46
#![allow(dead_code)]
47
48
pub(crate) trait U8 {
49
    fn as_usize(self) -> usize;
50
}
51
52
impl U8 for u8 {
53
0
    fn as_usize(self) -> usize {
54
0
        usize::from(self)
55
0
    }
56
}
57
58
pub(crate) trait U16 {
59
    fn as_usize(self) -> usize;
60
    fn low_u8(self) -> u8;
61
    fn high_u8(self) -> u8;
62
}
63
64
impl U16 for u16 {
65
0
    fn as_usize(self) -> usize {
66
0
        usize::from(self)
67
0
    }
68
69
0
    fn low_u8(self) -> u8 {
70
0
        self as u8
71
0
    }
72
73
0
    fn high_u8(self) -> u8 {
74
0
        (self >> 8) as u8
75
0
    }
76
}
77
78
pub(crate) trait U32 {
79
    fn as_usize(self) -> usize;
80
    fn low_u8(self) -> u8;
81
    fn low_u16(self) -> u16;
82
    fn high_u16(self) -> u16;
83
}
84
85
impl U32 for u32 {
86
0
    fn as_usize(self) -> usize {
87
        #[cfg(debug_assertions)]
88
        {
89
            usize::try_from(self).expect("u32 overflowed usize")
90
        }
91
        #[cfg(not(debug_assertions))]
92
        {
93
0
            self as usize
94
        }
95
0
    }
96
97
0
    fn low_u8(self) -> u8 {
98
0
        self as u8
99
0
    }
100
101
0
    fn low_u16(self) -> u16 {
102
0
        self as u16
103
0
    }
104
105
0
    fn high_u16(self) -> u16 {
106
0
        (self >> 16) as u16
107
0
    }
108
}
109
110
pub(crate) trait U64 {
111
    fn as_usize(self) -> usize;
112
    fn low_u8(self) -> u8;
113
    fn low_u16(self) -> u16;
114
    fn low_u32(self) -> u32;
115
    fn high_u32(self) -> u32;
116
}
117
118
impl U64 for u64 {
119
0
    fn as_usize(self) -> usize {
120
        #[cfg(debug_assertions)]
121
        {
122
            usize::try_from(self).expect("u64 overflowed usize")
123
        }
124
        #[cfg(not(debug_assertions))]
125
        {
126
0
            self as usize
127
        }
128
0
    }
129
130
0
    fn low_u8(self) -> u8 {
131
0
        self as u8
132
0
    }
133
134
0
    fn low_u16(self) -> u16 {
135
0
        self as u16
136
0
    }
137
138
0
    fn low_u32(self) -> u32 {
139
0
        self as u32
140
0
    }
141
142
0
    fn high_u32(self) -> u32 {
143
0
        (self >> 32) as u32
144
0
    }
145
}
146
147
pub(crate) trait I32 {
148
    fn as_usize(self) -> usize;
149
    fn to_bits(self) -> u32;
150
    fn from_bits(n: u32) -> i32;
151
}
152
153
impl I32 for i32 {
154
0
    fn as_usize(self) -> usize {
155
        #[cfg(debug_assertions)]
156
        {
157
            usize::try_from(self).expect("i32 overflowed usize")
158
        }
159
        #[cfg(not(debug_assertions))]
160
        {
161
0
            self as usize
162
        }
163
0
    }
164
165
0
    fn to_bits(self) -> u32 {
166
0
        self as u32
167
0
    }
168
169
0
    fn from_bits(n: u32) -> i32 {
170
0
        n as i32
171
0
    }
172
}
173
174
pub(crate) trait Usize {
175
    fn as_u8(self) -> u8;
176
    fn as_u16(self) -> u16;
177
    fn as_u32(self) -> u32;
178
    fn as_u64(self) -> u64;
179
}
180
181
impl Usize for usize {
182
0
    fn as_u8(self) -> u8 {
183
        #[cfg(debug_assertions)]
184
        {
185
            u8::try_from(self).expect("usize overflowed u8")
186
        }
187
        #[cfg(not(debug_assertions))]
188
        {
189
0
            self as u8
190
        }
191
0
    }
192
193
0
    fn as_u16(self) -> u16 {
194
        #[cfg(debug_assertions)]
195
        {
196
            u16::try_from(self).expect("usize overflowed u16")
197
        }
198
        #[cfg(not(debug_assertions))]
199
        {
200
0
            self as u16
201
        }
202
0
    }
203
204
0
    fn as_u32(self) -> u32 {
205
        #[cfg(debug_assertions)]
206
        {
207
            u32::try_from(self).expect("usize overflowed u32")
208
        }
209
        #[cfg(not(debug_assertions))]
210
        {
211
0
            self as u32
212
        }
213
0
    }
214
215
0
    fn as_u64(self) -> u64 {
216
        #[cfg(debug_assertions)]
217
        {
218
            u64::try_from(self).expect("usize overflowed u64")
219
        }
220
        #[cfg(not(debug_assertions))]
221
        {
222
0
            self as u64
223
        }
224
0
    }
225
}
226
227
// Pointers aren't integers, but we convert pointers to integers to perform
228
// offset arithmetic in some places. (And no, we don't convert the integers
229
// back to pointers.) So add 'as_usize' conversions here too for completeness.
230
//
231
// These 'as' casts are actually okay because they're always non-lossy. But the
232
// idea here is to just try and remove as much 'as' as possible, particularly
233
// in this crate where we are being really paranoid about offsets and making
234
// sure we don't panic on inputs that might be untrusted. This way, the 'as'
235
// casts become easier to audit if they're all in one place, even when some of
236
// them are actually okay 100% of the time.
237
238
pub(crate) trait Pointer {
239
    fn as_usize(self) -> usize;
240
}
241
242
impl<T> Pointer for *const T {
243
0
    fn as_usize(self) -> usize {
244
0
        self as usize
245
0
    }
246
}