Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/func/args/count.rs
6600 views
1
use crate::func::args::ArgCountOutOfBoundsError;
2
use core::fmt::{Debug, Formatter};
3
4
/// A container for zero or more argument counts for a function.
5
///
6
/// For most functions, this will contain a single count,
7
/// however, overloaded functions may contain more.
8
///
9
/// # Maximum Argument Count
10
///
11
/// The maximum number of arguments that can be represented by this struct is 63,
12
/// as given by [`ArgCount::MAX_COUNT`].
13
/// The reason for this is that all counts are stored internally as a single `u64`
14
/// with each bit representing a specific count based on its bit index.
15
///
16
/// This allows for a smaller memory footprint and faster lookups compared to a
17
/// `HashSet` or `Vec` of possible counts.
18
/// It's also more appropriate for representing the argument counts of a function
19
/// given that most functions will not have more than a few arguments.
20
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
21
pub struct ArgCount {
22
/// The bits representing the argument counts.
23
///
24
/// Each bit represents a specific count based on its bit index.
25
bits: u64,
26
/// The total number of argument counts.
27
len: u8,
28
}
29
30
impl ArgCount {
31
/// The maximum number of arguments that can be represented by this struct.
32
pub const MAX_COUNT: usize = u64::BITS as usize - 1;
33
34
/// Create a new [`ArgCount`] with the given count.
35
///
36
/// # Errors
37
///
38
/// Returns an error if the count is greater than [`Self::MAX_COUNT`].
39
pub fn new(count: usize) -> Result<Self, ArgCountOutOfBoundsError> {
40
Ok(Self {
41
bits: 1 << Self::try_to_u8(count)?,
42
len: 1,
43
})
44
}
45
46
/// Adds the given count to this [`ArgCount`].
47
///
48
/// # Panics
49
///
50
/// Panics if the count is greater than [`Self::MAX_COUNT`].
51
pub fn add(&mut self, count: usize) {
52
self.try_add(count).unwrap();
53
}
54
55
/// Attempts to add the given count to this [`ArgCount`].
56
///
57
/// # Errors
58
///
59
/// Returns an error if the count is greater than [`Self::MAX_COUNT`].
60
pub fn try_add(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {
61
let count = Self::try_to_u8(count)?;
62
63
if !self.contains_unchecked(count) {
64
self.len += 1;
65
self.bits |= 1 << count;
66
}
67
68
Ok(())
69
}
70
71
/// Removes the given count from this [`ArgCount`].
72
pub fn remove(&mut self, count: usize) {
73
self.try_remove(count).unwrap();
74
}
75
76
/// Attempts to remove the given count from this [`ArgCount`].
77
///
78
/// # Errors
79
///
80
/// Returns an error if the count is greater than [`Self::MAX_COUNT`].
81
pub fn try_remove(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {
82
let count = Self::try_to_u8(count)?;
83
84
if self.contains_unchecked(count) {
85
self.len -= 1;
86
self.bits &= !(1 << count);
87
}
88
89
Ok(())
90
}
91
92
/// Checks if this [`ArgCount`] contains the given count.
93
pub fn contains(&self, count: usize) -> bool {
94
count < usize::BITS as usize && (self.bits >> count) & 1 == 1
95
}
96
97
/// Returns the total number of argument counts that this [`ArgCount`] contains.
98
pub fn len(&self) -> usize {
99
self.len as usize
100
}
101
102
/// Returns true if this [`ArgCount`] contains no argument counts.
103
pub fn is_empty(&self) -> bool {
104
self.len == 0
105
}
106
107
/// Returns an iterator over the argument counts in this [`ArgCount`].
108
pub fn iter(&self) -> ArgCountIter {
109
ArgCountIter {
110
count: *self,
111
index: 0,
112
found: 0,
113
}
114
}
115
116
/// Checks if this [`ArgCount`] contains the given count without any bounds checking.
117
///
118
/// # Panics
119
///
120
/// Panics if the count is greater than [`Self::MAX_COUNT`].
121
fn contains_unchecked(&self, count: u8) -> bool {
122
(self.bits >> count) & 1 == 1
123
}
124
125
/// Attempts to convert the given count to a `u8` within the bounds of the [maximum count].
126
///
127
/// [maximum count]: Self::MAX_COUNT
128
fn try_to_u8(count: usize) -> Result<u8, ArgCountOutOfBoundsError> {
129
if count > Self::MAX_COUNT {
130
Err(ArgCountOutOfBoundsError(count))
131
} else {
132
Ok(count as u8)
133
}
134
}
135
}
136
137
/// Defaults this [`ArgCount`] to empty.
138
///
139
/// This means that it contains no argument counts, including zero.
140
impl Default for ArgCount {
141
fn default() -> Self {
142
Self { bits: 0, len: 0 }
143
}
144
}
145
146
impl Debug for ArgCount {
147
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
148
f.debug_set().entries(self.iter()).finish()
149
}
150
}
151
152
/// An iterator for the argument counts in an [`ArgCount`].
153
pub struct ArgCountIter {
154
count: ArgCount,
155
index: u8,
156
found: u8,
157
}
158
159
impl Iterator for ArgCountIter {
160
type Item = usize;
161
162
fn next(&mut self) -> Option<Self::Item> {
163
loop {
164
if self.index as usize > ArgCount::MAX_COUNT {
165
return None;
166
}
167
168
if self.found == self.count.len {
169
// All counts have been found
170
return None;
171
}
172
173
if self.count.contains_unchecked(self.index) {
174
self.index += 1;
175
self.found += 1;
176
return Some(self.index as usize - 1);
177
}
178
179
self.index += 1;
180
}
181
}
182
183
fn size_hint(&self) -> (usize, Option<usize>) {
184
(self.count.len(), Some(self.count.len()))
185
}
186
}
187
188
impl ExactSizeIterator for ArgCountIter {}
189
190
#[cfg(test)]
191
mod tests {
192
use super::*;
193
194
#[test]
195
fn should_default_to_empty() {
196
let count = ArgCount::default();
197
198
assert_eq!(count.len(), 0);
199
assert!(count.is_empty());
200
201
assert!(!count.contains(0));
202
}
203
204
#[test]
205
fn should_construct_with_count() {
206
let count = ArgCount::new(3).unwrap();
207
208
assert_eq!(count.len(), 1);
209
assert!(!count.is_empty());
210
211
assert!(count.contains(3));
212
}
213
214
#[test]
215
fn should_add_count() {
216
let mut count = ArgCount::default();
217
count.add(3);
218
219
assert_eq!(count.len(), 1);
220
221
assert!(count.contains(3));
222
}
223
224
#[test]
225
fn should_add_multiple_counts() {
226
let mut count = ArgCount::default();
227
count.add(3);
228
count.add(5);
229
count.add(7);
230
231
assert_eq!(count.len(), 3);
232
233
assert!(!count.contains(0));
234
assert!(!count.contains(1));
235
assert!(!count.contains(2));
236
237
assert!(count.contains(3));
238
assert!(count.contains(5));
239
assert!(count.contains(7));
240
}
241
242
#[test]
243
fn should_add_idempotently() {
244
let mut count = ArgCount::default();
245
count.add(3);
246
count.add(3);
247
248
assert_eq!(count.len(), 1);
249
assert!(count.contains(3));
250
}
251
252
#[test]
253
fn should_remove_count() {
254
let mut count = ArgCount::default();
255
count.add(3);
256
257
assert_eq!(count.len(), 1);
258
assert!(count.contains(3));
259
260
count.remove(3);
261
262
assert_eq!(count.len(), 0);
263
assert!(!count.contains(3));
264
}
265
266
#[test]
267
fn should_allow_removing_nonexistent_count() {
268
let mut count = ArgCount::default();
269
270
assert_eq!(count.len(), 0);
271
assert!(!count.contains(3));
272
273
count.remove(3);
274
275
assert_eq!(count.len(), 0);
276
assert!(!count.contains(3));
277
}
278
279
#[test]
280
fn should_iterate_over_counts() {
281
let mut count = ArgCount::default();
282
count.add(3);
283
count.add(5);
284
count.add(7);
285
286
let mut iter = count.iter();
287
288
assert_eq!(iter.len(), 3);
289
290
assert_eq!(iter.next(), Some(3));
291
assert_eq!(iter.next(), Some(5));
292
assert_eq!(iter.next(), Some(7));
293
assert_eq!(iter.next(), None);
294
}
295
296
#[test]
297
fn should_return_error_for_out_of_bounds_count() {
298
let count = ArgCount::new(64);
299
assert_eq!(count, Err(ArgCountOutOfBoundsError(64)));
300
301
let mut count = ArgCount::default();
302
assert_eq!(count.try_add(64), Err(ArgCountOutOfBoundsError(64)));
303
assert_eq!(count.try_remove(64), Err(ArgCountOutOfBoundsError(64)));
304
}
305
306
#[test]
307
fn should_return_false_for_out_of_bounds_contains() {
308
let count = ArgCount::default();
309
assert!(!count.contains(64));
310
}
311
}
312
313