Path: blob/main/crates/bevy_reflect/src/func/args/count.rs
6600 views
use crate::func::args::ArgCountOutOfBoundsError;1use core::fmt::{Debug, Formatter};23/// A container for zero or more argument counts for a function.4///5/// For most functions, this will contain a single count,6/// however, overloaded functions may contain more.7///8/// # Maximum Argument Count9///10/// The maximum number of arguments that can be represented by this struct is 63,11/// as given by [`ArgCount::MAX_COUNT`].12/// The reason for this is that all counts are stored internally as a single `u64`13/// with each bit representing a specific count based on its bit index.14///15/// This allows for a smaller memory footprint and faster lookups compared to a16/// `HashSet` or `Vec` of possible counts.17/// It's also more appropriate for representing the argument counts of a function18/// given that most functions will not have more than a few arguments.19#[derive(Copy, Clone, PartialEq, Eq, Hash)]20pub struct ArgCount {21/// The bits representing the argument counts.22///23/// Each bit represents a specific count based on its bit index.24bits: u64,25/// The total number of argument counts.26len: u8,27}2829impl ArgCount {30/// The maximum number of arguments that can be represented by this struct.31pub const MAX_COUNT: usize = u64::BITS as usize - 1;3233/// Create a new [`ArgCount`] with the given count.34///35/// # Errors36///37/// Returns an error if the count is greater than [`Self::MAX_COUNT`].38pub fn new(count: usize) -> Result<Self, ArgCountOutOfBoundsError> {39Ok(Self {40bits: 1 << Self::try_to_u8(count)?,41len: 1,42})43}4445/// Adds the given count to this [`ArgCount`].46///47/// # Panics48///49/// Panics if the count is greater than [`Self::MAX_COUNT`].50pub fn add(&mut self, count: usize) {51self.try_add(count).unwrap();52}5354/// Attempts to add the given count to this [`ArgCount`].55///56/// # Errors57///58/// Returns an error if the count is greater than [`Self::MAX_COUNT`].59pub fn try_add(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {60let count = Self::try_to_u8(count)?;6162if !self.contains_unchecked(count) {63self.len += 1;64self.bits |= 1 << count;65}6667Ok(())68}6970/// Removes the given count from this [`ArgCount`].71pub fn remove(&mut self, count: usize) {72self.try_remove(count).unwrap();73}7475/// Attempts to remove the given count from this [`ArgCount`].76///77/// # Errors78///79/// Returns an error if the count is greater than [`Self::MAX_COUNT`].80pub fn try_remove(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {81let count = Self::try_to_u8(count)?;8283if self.contains_unchecked(count) {84self.len -= 1;85self.bits &= !(1 << count);86}8788Ok(())89}9091/// Checks if this [`ArgCount`] contains the given count.92pub fn contains(&self, count: usize) -> bool {93count < usize::BITS as usize && (self.bits >> count) & 1 == 194}9596/// Returns the total number of argument counts that this [`ArgCount`] contains.97pub fn len(&self) -> usize {98self.len as usize99}100101/// Returns true if this [`ArgCount`] contains no argument counts.102pub fn is_empty(&self) -> bool {103self.len == 0104}105106/// Returns an iterator over the argument counts in this [`ArgCount`].107pub fn iter(&self) -> ArgCountIter {108ArgCountIter {109count: *self,110index: 0,111found: 0,112}113}114115/// Checks if this [`ArgCount`] contains the given count without any bounds checking.116///117/// # Panics118///119/// Panics if the count is greater than [`Self::MAX_COUNT`].120fn contains_unchecked(&self, count: u8) -> bool {121(self.bits >> count) & 1 == 1122}123124/// Attempts to convert the given count to a `u8` within the bounds of the [maximum count].125///126/// [maximum count]: Self::MAX_COUNT127fn try_to_u8(count: usize) -> Result<u8, ArgCountOutOfBoundsError> {128if count > Self::MAX_COUNT {129Err(ArgCountOutOfBoundsError(count))130} else {131Ok(count as u8)132}133}134}135136/// Defaults this [`ArgCount`] to empty.137///138/// This means that it contains no argument counts, including zero.139impl Default for ArgCount {140fn default() -> Self {141Self { bits: 0, len: 0 }142}143}144145impl Debug for ArgCount {146fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {147f.debug_set().entries(self.iter()).finish()148}149}150151/// An iterator for the argument counts in an [`ArgCount`].152pub struct ArgCountIter {153count: ArgCount,154index: u8,155found: u8,156}157158impl Iterator for ArgCountIter {159type Item = usize;160161fn next(&mut self) -> Option<Self::Item> {162loop {163if self.index as usize > ArgCount::MAX_COUNT {164return None;165}166167if self.found == self.count.len {168// All counts have been found169return None;170}171172if self.count.contains_unchecked(self.index) {173self.index += 1;174self.found += 1;175return Some(self.index as usize - 1);176}177178self.index += 1;179}180}181182fn size_hint(&self) -> (usize, Option<usize>) {183(self.count.len(), Some(self.count.len()))184}185}186187impl ExactSizeIterator for ArgCountIter {}188189#[cfg(test)]190mod tests {191use super::*;192193#[test]194fn should_default_to_empty() {195let count = ArgCount::default();196197assert_eq!(count.len(), 0);198assert!(count.is_empty());199200assert!(!count.contains(0));201}202203#[test]204fn should_construct_with_count() {205let count = ArgCount::new(3).unwrap();206207assert_eq!(count.len(), 1);208assert!(!count.is_empty());209210assert!(count.contains(3));211}212213#[test]214fn should_add_count() {215let mut count = ArgCount::default();216count.add(3);217218assert_eq!(count.len(), 1);219220assert!(count.contains(3));221}222223#[test]224fn should_add_multiple_counts() {225let mut count = ArgCount::default();226count.add(3);227count.add(5);228count.add(7);229230assert_eq!(count.len(), 3);231232assert!(!count.contains(0));233assert!(!count.contains(1));234assert!(!count.contains(2));235236assert!(count.contains(3));237assert!(count.contains(5));238assert!(count.contains(7));239}240241#[test]242fn should_add_idempotently() {243let mut count = ArgCount::default();244count.add(3);245count.add(3);246247assert_eq!(count.len(), 1);248assert!(count.contains(3));249}250251#[test]252fn should_remove_count() {253let mut count = ArgCount::default();254count.add(3);255256assert_eq!(count.len(), 1);257assert!(count.contains(3));258259count.remove(3);260261assert_eq!(count.len(), 0);262assert!(!count.contains(3));263}264265#[test]266fn should_allow_removing_nonexistent_count() {267let mut count = ArgCount::default();268269assert_eq!(count.len(), 0);270assert!(!count.contains(3));271272count.remove(3);273274assert_eq!(count.len(), 0);275assert!(!count.contains(3));276}277278#[test]279fn should_iterate_over_counts() {280let mut count = ArgCount::default();281count.add(3);282count.add(5);283count.add(7);284285let mut iter = count.iter();286287assert_eq!(iter.len(), 3);288289assert_eq!(iter.next(), Some(3));290assert_eq!(iter.next(), Some(5));291assert_eq!(iter.next(), Some(7));292assert_eq!(iter.next(), None);293}294295#[test]296fn should_return_error_for_out_of_bounds_count() {297let count = ArgCount::new(64);298assert_eq!(count, Err(ArgCountOutOfBoundsError(64)));299300let mut count = ArgCount::default();301assert_eq!(count.try_add(64), Err(ArgCountOutOfBoundsError(64)));302assert_eq!(count.try_remove(64), Err(ArgCountOutOfBoundsError(64)));303}304305#[test]306fn should_return_false_for_out_of_bounds_contains() {307let count = ArgCount::default();308assert!(!count.contains(64));309}310}311312313