1use core::{
4 cmp, fmt,
5 hash::{Hash, Hasher},
6 i16, i32, i64, i8, isize,
7 ops,
8 sync::atomic::*,
9 u16, u32, u64, u8, usize,
10};
11#[cfg(feature = "iterators")]
12use crate::iterators::*;
13use enumflags2::{BitFlags, _internal::RawBitFlags};
14use paste::paste;
15#[cfg(feature = "serde")]
16use serde::{ser::SerializeStruct, Deserialize, Serialize};
17
18pub mod behavior;
19pub use behavior::*;
20
21mod private {
22 use super::*;
23 pub trait As<T> {
25 fn as_ref(&self) -> &T;
26 fn as_mut(&mut self) -> &mut T;
27 }
28
29 #[derive(strum::AsRefStr)]
31 #[cfg_attr(feature = "serde", derive(Deserialize))]
32 #[non_exhaustive]
33 pub enum O {
34 AcqRel,
35 Acquire,
36 Relaxed,
37 Release,
38 SeqCst,
39 }
40 impl From<Ordering> for O {
41 fn from(value: Ordering) -> Self {
42 match value {
43 Ordering::AcqRel => Self::AcqRel,
44 Ordering::Acquire => Self::Acquire,
45 Ordering::Relaxed => Self::Relaxed,
46 Ordering::Release => Self::Release,
47 Ordering::SeqCst => Self::SeqCst,
48 _ => Self::SeqCst,
49 }
50 }
51 }
52 impl From<O> for Ordering {
53 fn from(value: O) -> Self {
54 #[allow(unreachable_patterns)]
55 match value {
56 O::AcqRel => Self::AcqRel,
57 O::Acquire => Self::Acquire,
58 O::Relaxed => Self::Relaxed,
59 O::Release => Self::Release,
60 O::SeqCst => Self::SeqCst,
61 _ => Self::SeqCst,
62 }
63 }
64 }
65 #[cfg(feature = "serde")]
66 impl Serialize for O {
67 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68 where
69 S: serde::Serializer,
70 {
71 macro_rules! serialize {
72 (@it $V:ident) => {{ serializer.serialize_unit_variant("O", Self::$V as u32, Self::$V.as_ref()) }};
73 ($($V:ident,)+) => {{
74 #[allow(unreachable_patterns)]
75 match self {
76 $(Self::$V => serialize!(@it $V),)+
77 _ => serialize!(@it SeqCst),
78 }
79 }}
80 }
81 serialize! { AcqRel, Acquire, Relaxed, Release, SeqCst, }
82 }
83 }
84}
85use private::*;
86
87
88macro_rules! make_counter {
90 (@Main $Prefix:ident => $Unit:ident | $Atomic:ident ) => {
91 paste!{
92 #[doc = r#"An atomic counter using ["# $Atomic r#"] / ([core::"# $Unit r#"])."#]
93 #[doc = ""]
94 #[doc = r"### Behavior "]
95 #[doc = "1. The default ordering is [Sequentially Consistent](Ordering::SeqCst). "]
96 #[doc = "2. The ordering used for atomic operations is customizable for operations ending in `with_ordering`. "]
97 #[doc = "3. The choice of ordering *intentionally* impacts **ALMOST EVERYTHING** about how this counter works, including de/serialization, incrementing, decrementing, equality comparisons, partial ordering comparisons, etc. "]
98 #[doc = "4. Total (non-partial) ordering comparisons always use the default ordering. "]
99 #[doc = "5. Unlike the underlying [" $Atomic "], this will not wrap on overflow unless the cyclic behavior mode is set. "]
100 #[doc = "6. The default behavior is non-monotonic, so the counter can increment and decrement. "]
101 #[doc = r"### Ordering "]
102 #[doc = "- PartialEq is implemented such that counters with differing orderings are never equal. "]
103 #[doc = "- PartialOrd is implemented such that counters with differing [(atomic) orderings](Ordering) produce no [(comparison) ordering](core::cmp::Ordering). "]
104 #[doc = "- **(Saturating) arithmetic operations are implemented such that differing atomic orderings between the operands are ignored!**"]
105 #[doc = r"### Miscellaneous"]
106 #[doc = r"You can use the [to_x](Self::to_x) method to convert to any type that implements From\<" $Unit r"\>"]
107 #[derive(Debug)]
108 pub struct [<$Prefix $Unit:camel>] {
109 inner: $Atomic,
111 pub (crate) ordering: Ordering,
113 pub (crate) counting_behavior: BitFlags<CountingBehavior>,
115 conflicts: AllCountingBehaviorConflicts,
117 }
118
119 #[cfg(feature = "serde")]
120 #[allow(non_snake_case)]
121 mod [<serde_impls_ $Prefix $Unit:camel >] {
122 use super::*;
123 impl Serialize for [<$Prefix $Unit:camel>] {
124 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
125 where
126 S: serde::Serializer
127 {
128 let name_str = stringify!([<$Prefix $Unit:camel>]);
129 let mut counter = serializer.serialize_struct( name_str, 2 )?;
130 counter.serialize_field("ordering", &O::from(self.ordering))?;
131 counter.serialize_field("inner", &self.get())?;
132 counter.serialize_field("counting_behavior", &self.counting_behavior)?;
133 counter.end()
134 }
135 }
136
137 impl<'de> Deserialize<'de> for [<$Prefix $Unit:camel>] {
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: serde::Deserializer<'de>
141 {
142 let name_str = stringify!([<$Prefix $Unit:camel>]);
143
144 #[derive(Deserialize)]
145 #[serde(field_identifier, rename_all = "snake_case")]
146 enum Field { Inner, Ordering, CountingBehavior }
147
148 struct [<$Prefix $Unit:camel Visitor>];
149 impl<'de> serde::de::Visitor<'de> for [<$Prefix $Unit:camel Visitor>] {
150 type Value = [<$Prefix $Unit:camel >];
151
152 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
153 let expecting_str = stringify!(struct [<$Prefix $Unit:camel>]);
154 formatter.write_str(expecting_str)
155 }
156
157 fn visit_map<V>(self, mut map: V) -> Result< [<$Prefix $Unit:camel >], V::Error>
158 where V: serde::de::MapAccess<'de>
159 {
160 let mut inner = None;
161 let mut ordering = None;
162 let mut counting_behavior = None;
163 while let Some(key) = map.next_key()? {
164 match key {
165 Field::Inner => {
166 if inner.is_some() {
167 return Err(serde::de::Error::duplicate_field("inner"));
168 }
169 let val: $Unit = map.next_value()?;
170 inner = Some(val.into())
171 },
172 Field::Ordering => {
173 if ordering.is_some() {
174 return Err(serde::de::Error::duplicate_field("ordering"));
175 }
176 let val: O = map.next_value()?;
177 ordering = Some(val.into())
178 },
179 Field::CountingBehavior => {
180 if counting_behavior.is_some() {
181 return Err(serde::de::Error::duplicate_field("counting_behavior"));
182 }
183 let val: BitFlags<CountingBehavior> = map.next_value()?;
184 counting_behavior = Some(val.into())
185 },
186 }
187 }
188 let ordering = ordering.ok_or_else(|| serde::de::Error::missing_field("ordering"))?;
189 let inner = inner.ok_or_else(|| serde::de::Error::missing_field("inner"))?;
190 let counting_behavior: BitFlags<CountingBehavior> = counting_behavior
191 .ok_or_else(|| serde::de::Error::missing_field("inner"))?;
192 let conflicts = counting_behavior.get_behavior_conflicts();
193 Ok([<$Prefix $Unit:camel >]{
194 ordering,
195 inner,
196 counting_behavior,
197 conflicts
198 })
199 }
200 }
201 const FIELDS: &'static [&'static str] = &["ordering", "inner"];
202 deserializer.deserialize_struct(name_str, FIELDS, [<$Prefix $Unit:camel Visitor>])
203 }
204 }
205
206 #[cfg(test)]
207 mod test_serde{
208 use super::*;
209 #[test]
210 fn serialize_and_deserialize() {
211 use [<$Prefix $Unit:camel >] as C;
212 let c = C::new_from_offset(21);
214 let ron_c = ron::to_string(&c).expect(stringify!(Must serialize [<$Prefix $Unit:camel >]));
215 let d = ron::from_str(&ron_c).expect(stringify!(Must deserialize [<$Prefix $Unit:camel >]));
216 assert_eq!(c, d, "Counter deserialization equals original serialized counter");
217 }
219 }
220 }
221
222 #[cfg(feature = "iterators")]
223 impl IntoIterator for [<$Prefix $Unit:camel>] {
224 type Item = $Unit;
225 type IntoIter = CounterIterator<Self>;
226 fn into_iter(self) -> Self::IntoIter {
227 CounterIterator::new(self)
228 }
229 }
230
231 #[doc = "PartialOrd only produces [cmp ordering](cmp::Ordering) when [atomic orderings](Ordering) are equal"]
232 #[doc = r"```"]
233 #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
234 #[doc = "use core::sync::atomic::Ordering;"]
235 #[doc = r#"let a = C::new_from_offset_with_ordering(32, Ordering::Relaxed);"# ]
236 #[doc = r#"let b = C::new_from_offset_with_ordering(33, Ordering::Relaxed);"# ]
237 #[doc = r#"assert!(a < b, "same-cmp::ordering counters must be ordered by when counts");"# ]
238 #[doc = r#"assert!(b > a, "same-cmp::ordering counters must be ordered by when counts");"# ]
239 #[doc = r#"let m = 20;"# ]
240 #[doc = r#"(0..m).for_each(|_| { a.inc_one(); b.inc_one(); });"# ]
241 #[doc = r#"assert!(a < b, "cmp::ordering preserved after counting same amount");"# ]
242 #[doc = r#"assert!(b > a, "cmp::ordering preserved after counting same amount");"# ]
243 impl cmp::PartialOrd for [<$Prefix $Unit:camel >] {
244 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
245 self.ordering
246 .eq(&other.ordering)
247 .then(|| ())
248 .and_then(|()| self.get().partial_cmp(&other.get()))
249 }
250 }
251
252 impl cmp::Ord for [<$Prefix $Unit:camel >] {
253 fn cmp(&self, other:&Self) -> cmp::Ordering {
254 self.get_with_ordering(Self::DEFAULT_ORDERING)
255 .cmp(&other.get_with_ordering(Self::DEFAULT_ORDERING))
256 }
257 }
258
259 impl fmt::Display for [<$Prefix $Unit:camel>] {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 write!(f, "{}", self.get())
262 }
263 }
264
265 impl As<$Atomic> for [<$Prefix $Unit:camel>] {
266 fn as_ref(&self) -> &$Atomic { &self.inner}
267 fn as_mut(&mut self) -> &mut $Atomic { &mut self.inner}
268 }
269 impl As<Ordering> for [<$Prefix $Unit:camel>] {
270 fn as_ref(&self) -> &Ordering { &self.ordering}
271 fn as_mut(&mut self) -> &mut Ordering { &mut self.ordering}
272 }
273 impl AsRef<Ordering> for [<$Prefix $Unit:camel>] {
274 fn as_ref(&self) -> &Ordering { <Self as As<Ordering>>::as_ref(self) }
275 }
276
277 impl Clone for [<$Prefix $Unit:camel>] {
278 fn clone(&self) -> Self {
279 Self::new_from_offset_with_ordering(self.get(), self.ordering)
280 }
281 }
282
283 impl Default for [<$Prefix $Unit:camel>] {
284 fn default() -> Self { Self::new() }
285 }
286
287 #[allow(non_snake_case)]
288 mod [<eq_partial_eq_hash_ $Prefix $Unit:camel >] {
289 use super::*;
290 impl Eq for [<$Prefix $Unit:camel>] {}
291 #[doc = "PartialEq is only equal when orderings and counting behaviors are equal"]
292 #[doc = r"```"]
293 #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
294 #[doc = "use core::sync::atomic::Ordering;"]
295 #[doc = r#"let a = C::new_from_offset_with_ordering(33, Ordering::Relaxed);"# ]
296 #[doc = r#"let b = C::new_from_offset_with_ordering(33, Ordering::Relaxed);"# ]
297 #[doc = r#"assert_eq!(a, b, "counters must be equal when counts and orderings are equal");"# ]
298 #[doc = r#"assert_eq!(a, b, "counters must be equal when counts and orderings are equal");"# ]
299 #[doc = r#"let m = 20;"# ]
300 #[doc = r#"(0..m).for_each(|_| { a.inc_one(); b.inc_one(); });"# ]
301 #[doc = r#"assert_eq!(a, b, "counters must be equal after counting same amount");"# ]
302 #[doc = r#"assert_eq!(a, b, "counters must be equal after counting same amount");"# ]
303 #[doc = r#"a.inc_one();"# ]
304 #[doc = r#"assert_ne!(a, b, "counters must not be equal after counting different amounts");"# ]
305 #[doc = r#"let c = C::new_from_offset_with_ordering(44, Ordering::Relaxed);"# ]
306 #[doc = r#"let d = C::new_from_offset_with_ordering(44, Ordering::Release);"# ]
307 #[doc = r#"assert_ne!(c, d, "ordering-inequal counters must not be equal with same count");"# ]
308 impl PartialEq for [<$Prefix $Unit:camel>] {
309 fn eq(&self, rhs: &Self) -> bool {
310 self.ordering.eq(&rhs.ordering)
311 && self.counting_behavior.eq(&rhs.counting_behavior)
312 && self.get().eq(&rhs.get())
313 }
314 }
315
316 impl Hash for [<$Prefix $Unit:camel >] {
317 fn hash<H: Hasher>(&self, state: &mut H) {
318 self.ordering.hash(state);
319 self.counting_behavior.hash(state);
320 self.get().hash(state);
321 }
322 }
323
324 #[cfg(test)]
325 mod hash_and_eq {
326 extern crate std;
327 use super::*;
328 use std::collections::hash_map::DefaultHasher;
329 #[test]
330 fn hash_and_eq_property() {
331 use [<$Prefix $Unit:camel >] as C;
332 let c = C::new_from_offset(21);
334 let d = C::new_from_offset(21);
335 assert_eq!(c, d, "Test counters must equal");
336 let hasher_c = &mut DefaultHasher::new();
337 c.hash(hasher_c);
338 let hash_c = hasher_c.finish();
339 let hasher_d = &mut DefaultHasher::new();
340 d.hash(hasher_d);
341 let hash_d = hasher_d.finish();
342 assert_eq!(hash_c, hash_d, "When impelementing Hash and Eq, the property k1 == k2 -> hash(k1) == hash(k2) must hold");
343 }
344 }
345 }
346
347 impl From<$Unit> for [<$Prefix $Unit:camel>] {
348 fn from(x: $Unit) -> Self {
349 let counting_behavior = Self::DEFAULT_COUNTING_BEHAVIOR;
350 let conflicts = counting_behavior.get_behavior_conflicts();
351 Self{
352 inner: $Atomic::new(x),
353 ordering: Self::DEFAULT_ORDERING,
354 counting_behavior,
355 conflicts
356 }
357 }
358 }
359
360 impl From<&[<$Prefix $Unit:camel>]> for $Unit {
361 fn from(counter: &[<$Prefix $Unit:camel>]) -> Self { counter.get() }
362 }
363
364 impl HasCountingBehavior for [<$Prefix $Unit:camel>] {
365 fn get_behavior_ref(&self) -> &BitFlags<CountingBehavior> { &self.counting_behavior }
366 fn get_behavior_conflicts(&self) -> AllCountingBehaviorConflicts { self.conflicts }
367 }
368 impl IsCounter for [<$Prefix $Unit:camel >] {
369 type Unit = $Unit;
370 fn get_ordering_ref(&self) -> &Ordering { &self.ordering }
371 fn get_current(&self) -> Self::Unit { self.get() }
372 }
373
374 impl [<$Prefix $Unit:camel>] {
375 #[doc = "Largest [representable value](" $Unit "::MAX)"]
376 pub const MAX: $Unit = $Unit::MAX;
377 #[doc = "Smallest [representable value](" $Unit "::MIN)"]
378 pub const MIN: $Unit = $Unit::MIN;
379 pub const DEFAULT_ORDERING: Ordering = Ordering::SeqCst;
381 pub const DEFAULT_COUNTING_BEHAVIOR: BitFlags<CountingBehavior> = BitFlags::<CountingBehavior>::from_bits_truncate_c(
383 CountingBehavior::DEFAULT,
384 BitFlags::CONST_TOKEN
385 );
386 pub fn new() -> Self {
388 let counting_behavior = Self::DEFAULT_COUNTING_BEHAVIOR;
389 let conflicts = counting_behavior.get_behavior_conflicts();
390 Self {
391 inner: $Atomic::new(0),
392 ordering: Self::DEFAULT_ORDERING,
393 counting_behavior,
394 conflicts
395 }
396 }
397 pub fn new_with_ordering(ordering: Ordering) -> Self {
399 let mut s = Self::new();
400 s.ordering = ordering;
401 s
402 }
403 pub fn new_from_offset(offset: $Unit) -> Self {
405 let mut s = Self::new();
406 s.inner = $Atomic::new(offset);
407 s
408 }
409 pub fn new_from_offset_with_ordering(offset: $Unit, ordering: Ordering) -> Self {
411 let mut s = Self::new_from_offset(offset);
412 s.ordering = ordering;
413 s
414 }
415 pub fn set_counting_behavior<B: Into<BitFlags<CountingBehavior>>>(
417 &mut self,
418 counting_behavior: B
419 ) {
420 self.counting_behavior = counting_behavior.into();
421 self.conflicts = self.counting_behavior.get_behavior_conflicts();
422 }
423 pub fn new_with_counting_behavior<B: Into<BitFlags<CountingBehavior>>>(
425 counting_behavior: B
426 ) -> Self {
427 let mut s = Self::new();
428 s.set_counting_behavior(counting_behavior);
429 s
430 }
431 pub fn new_from_offset_with_counting_behavior<B: Into<BitFlags<CountingBehavior>>>(
433 offset: $Unit,
434 counting_behavior: B
435 ) -> Self {
436 let mut s = Self::new_from_offset(offset);
437 s.set_counting_behavior(counting_behavior);
438 s
439 }
440
441 #[doc = "Get current value with the default [ordering](Ordering)"]
442 #[doc = r"```"]
443 #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
444 #[doc = "use " $Unit " as U;"]
445 #[doc = r#"let c = C::new();"# ]
446 #[doc = r#"assert_eq!(c.get(), 0, "get returns initial value");"# ]
447 #[doc = r#"c.inc_one();"# ]
448 #[doc = r#"c.inc_one();"# ]
449 #[doc = r#"c.inc_one();"# ]
450 #[doc = r#"assert_eq!(c.get(), 3, "get returns post-increment value");"# ]
451 pub fn get(&self) -> $Unit { self.inner.load(self.ordering) }
452 pub fn get_with_ordering(&self, ordering: Ordering) -> $Unit { self.inner.load(ordering) }
454 pub fn get_i128(&self) -> i128 { self.get() as i128 }
456 #[doc = r#"Convert to some type that impls [From] "# $Unit r#"."#]
457 pub fn to_x<X: From<$Unit>> (&self) -> X { X::from(self.get()) }
458 }
459
460 }
461 };
462 (@CountsNonMonotonically $Prefix:ident => $Unit:ident | $Atomic:ident =>
463 $(
464 {$CountingDesc:ident ($pro_op:ident/$anti_op:ident)
465 as $counting_prefix:ident using $counting_op:ident until $test_limit:ident
466 or $cyclic_counting_op:ident};
467 )+) => {
468 paste!{
469 impl CountsNonmotonically for [<$Prefix $Unit:camel>] {
470 $(
471 #[doc = $CountingDesc " by one "]
472 #[doc = r"```"]
473 #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
474 #[doc = "use core::ops::*; "]
475 #[doc = "use enumflags2::{make_bitflags};"]
476 #[doc = "use " $Unit " as U;"]
477 #[doc = r#"let offset = U::MAX/2;"# ]
478 #[doc = r#"let c = C::new_from_offset(offset);"# ]
479 #[doc = r#"let m = 20;"# ]
480 #[doc = r#"(0..m).for_each(|_| { c."# [<$counting_prefix _one>] r#"(); });"# ]
481 #[doc = r#"assert_eq!(c.get(), (offset)."# $pro_op r#"(20), "counter must "# $CountingDesc r#"/"# $pro_op r#" number of times given as per sequential ordering");"# ]
482 #[doc = r#"let d = C::new_from_offset(U::"# $test_limit r#");"# ]
483 #[doc = r#"d."# [<$counting_prefix _one>] r#"();"# ]
484 #[doc = r#"d."# [<$counting_prefix _one>] r#"();"# ]
485 #[doc = r#"assert_eq!(d.get(), U::"# $test_limit r#", "counter must stop at "# $test_limit r#" ");"# ]
486 fn [<$counting_prefix _one>](&self) { self.[<$counting_prefix _by_with_ordering>](1, self.ordering) }
487
488 #[doc = "Can the counter " $CountingDesc:lower " any further?"]
489 #[doc = ""]
490 #[doc = "- It halts " $CountingDesc:lower " ing at Self::"$test_limit ]
491 #[doc = ""]
492 #[doc = r"```"]
493 #[doc = "use width_counters::{*, " [<$Prefix $Unit:camel>] " as C, CountingBehavior as B };"]
494 #[doc = "use " $Unit " as U;"]
495 #[doc = "use core::ops::*; "]
496 #[doc = r#"let m = 3"# $Unit r#";"# ]
497 #[doc = r#"let offset = C::MAX/2;"# ]
498 #[doc = r#"let d = C::new_from_offset_with_counting_behavior(offset, B::"# $CountingDesc r#");"# ]
499 #[doc = r#"assert_eq!(d."# [< can_ $counting_prefix >] r#"(), true, "counter must detect when it can "# $CountingDesc r#"");"# ]
500 #[doc = r#"let offset = C::"# $test_limit r#";"# ]
501 #[doc = r#"let d = C::new_from_offset_with_counting_behavior(offset, B::"# $CountingDesc r#");"# ]
502 #[doc = r#"assert_eq!(d."# [< can_ $counting_prefix >] r#"(), false, "counter must detect when it can no longer "# $CountingDesc r#"");"# ]
503 fn [< can_ $counting_prefix >](&self) -> bool {
504 self.[<is_ $counting_prefix rementable>]()
505 && self.[<is_within_ $counting_prefix rement_bound >]()
506 }
507
508 #[doc = "Is the current index advanceable with the given operation type"]
509 fn [<is_ $counting_prefix rementable>](&self) -> bool {
510 !self.counting_behavior.is_empty()
511 && !self.conflicts.contains(&CountingBehaviorConflict::Always)
512 && (
513 self.counting_behavior.contains(CountingBehavior::$CountingDesc)
514 || self.counting_behavior.contains(CountingBehavior::Nonmonotonic)
515 )
516 }
517
518 #[doc = "Is it within(inclusive) the " $CountingDesc:lower " bound"]
519 #[doc = ""]
520 #[doc = "- **The bound is:** [Self::"$test_limit "]"]
521 #[doc = "- The bound never applies in:** [acyclic mode](CountingBehavior)" ]
522 fn [<is_within_ $counting_prefix rement_bound >](&self) -> bool {
523 self.get() != Self::$test_limit
524 || self.counting_behavior.contains(CountingBehavior::Cyclic)
525 }
528
529 #[doc = "Is it at the " $CountingDesc:lower " bound"]
530 #[doc = ""]
531 #[doc = "- **The bound is:** [Self::"$test_limit "]"]
532 fn [<is_at_ $counting_prefix rement_bound >](&self) -> bool {
533 self.get() == Self::$test_limit
534 }
535
536 )+
537 }
538 }
539 };
540 (@Counting $Prefix:ident =>
541 $Unit:ident | $Atomic:ident =>
542 $CountingDesc:ident ($pro_op:ident/$anti_op:ident)
543 as $counting_prefix:ident using $counting_op:ident until $test_limit:ident
544 or $cyclic_counting_op:ident
545 ) => {
546 paste!{
547 impl [<$Prefix $Unit:camel>] {
548 #[doc = $CountingDesc " by one with ordering"]
549 pub fn [<$counting_prefix _one_with_ordering>](&self, ordering: Ordering) { self.[<$counting_prefix _by_with_ordering>](1, ordering) }
550
551 #[doc = $CountingDesc " by specified amount"]
552 #[doc = r"```"]
553 #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
554 #[doc = "use core::ops::*; "]
555 #[doc = "use " $Unit " as U;"]
556 #[doc = r#"let offset = U::MAX/2;"# ]
557 #[doc = r#"let c = C::new_from_offset(offset);"# ]
558 #[doc = r#"let m = 20;"# ]
559 #[doc = r#"(0..m).for_each(|_| { c."# [<$counting_prefix _by>] r#"(2); });"# ]
560 #[doc = r#"assert_eq!((c.get() as i128)."# $anti_op r#"((20*2) as i128), ((offset) as i128), "counter must "# $CountingDesc r#" by specified amount");"# ]
561 pub fn [<$counting_prefix _by>](&self, amount: $Unit) { self.[<$counting_prefix _by_with_ordering>](amount, self.ordering); }
562
563 #[doc = $CountingDesc " by specified amount with ordering"]
564 #[doc = r"```"]
565 #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
566 #[doc = "use " $Unit " as U;"]
567 #[doc = "use core::ops::*; "]
568 #[doc = r#"let m = 3"# $Unit r#";"# ]
569 #[doc = r#"let d = C::new_from_offset(U::"# $test_limit r#"."# $anti_op r#"(m * 2));"# ]
570 #[doc = r#"d."# [<$counting_prefix _by>] r#"(m);"# ]
571 #[doc = r#"d."# [<$counting_prefix _by>] r#"(m);"# ]
572 #[doc = r#"d."# [<$counting_prefix _by>] r#"(m);"# ]
573 #[doc = r#"assert_eq!(d.get(), U::"# $test_limit r#", "counter must stop at "# $test_limit r#"");"# ]
574 pub fn [<$counting_prefix _by_with_ordering>](&self, amount: $Unit, ordering: Ordering) {
575 if self.[<is_ $counting_prefix rementable>]() {
576 let current = self.get_with_ordering(ordering);
577 if self.counting_behavior.contains(CountingBehavior::Cyclic) {
578 let _ = self.inner.swap(current.$cyclic_counting_op(amount), ordering);
579 } else {
580 let _ = self.inner.swap(current.$counting_op(amount), ordering);
581 }
582 }
583 }
584
585 #[doc = "Combine the " $CountingDesc:lower " (by one) and get operations" ]
586 #[doc = ""]
587 #[doc = "Returns the value **before** the " $CountingDesc:lower " operation"]
588 pub fn [<get_and_ $counting_prefix _one>](&self,) -> $Unit {
589 self.[<get_and_ $counting_prefix _by>](1)
590 }
591
592 #[doc = "Combine the " $CountingDesc:lower " (by the given amount) and get operations" ]
593 #[doc = ""]
594 #[doc = "Returns the value **before** the " $CountingDesc:lower "operation"]
595 pub fn [<get_and_ $counting_prefix _by>](&self, amount: $Unit) -> $Unit {
596 self.[<get_and_ $counting_prefix _by_with_ordering>](amount, self.ordering)
597 }
598
599 #[doc = "Combine the " $CountingDesc:lower " (by the given amount) and get operations" ]
600 #[doc = ""]
601 #[doc = "Returns the value **before** the " $CountingDesc:lower " operation"]
602 pub fn [<get_and_ $counting_prefix _by_with_ordering>](&self, amount: $Unit, ordering: Ordering) -> $Unit {
603 let u = self.get();
604 self.[<$counting_prefix _by_with_ordering>](amount, ordering);
605 u
606 }
607 }
608 }
609 };
610 (@Op $Prefix:ident => $Unit:ident op $Op:ty : $op_fn:ident ) => {
611 paste!{
612 impl $Op for [<$Prefix $Unit:camel>] {
613 type Output = Self;
614 #[doc = r" - This operation is implemented with saturating arithmetic"]
615 #[doc = r" - **This operation IGNORES dissimilar [atomic orderings](Ordering)!**"]
616 fn $op_fn(self, rhs: Self) -> Self::Output {
617 Self::new_from_offset(self.get().[<saturating_ $op_fn>](rhs.get()))
618 }
619 }
620 }
621 };
622 (@Ops $Prefix:ident => $Unit:ident => [
623 $($Op:ty : $op_fn:ident;)+
624 ] ) => {
625 $(make_counter!{@Op $Prefix => $Unit op $Op : $op_fn})+
626 };
627 ($Prefix:ident => [$($Unit:ident | $Atomic:ident, )+]) => {
628 $(make_counter!{@Main $Prefix => $Unit | $Atomic})+
629 $(make_counter!{@Counting $Prefix => $Unit | $Atomic => Increment (add/sub) as inc using saturating_add until MAX or wrapping_add })+
630 $(make_counter!{@Counting $Prefix => $Unit | $Atomic => Decrement (sub/add) as dec using saturating_sub until MIN or wrapping_sub })+
631 $(make_counter!{@CountsNonMonotonically $Prefix => $Unit | $Atomic =>
632 {Increment (add/sub) as inc using saturating_add until MAX or wrapping_add};
633 {Decrement (sub/add) as dec using saturating_sub until MIN or wrapping_sub};
634 })+
635 $(make_counter!{@Ops $Prefix => $Unit => [
636 ops::Add : add;
637 ops::Sub : sub;
638 ops::Mul : mul;
639 ops::Div : div;
640 ]})+
641 };
642}
643
644make_counter! {Counter => [
645 u8 | AtomicU8,
646 u16 | AtomicU16,
647 u32 | AtomicU32,
648 u64 | AtomicU64,
649 usize | AtomicUsize,
651
652 i8 | AtomicI8,
653 i16 | AtomicI16,
654 i32 | AtomicI32,
655 i64 | AtomicI64,
656 isize | AtomicIsize,
658]}
659
660
661#[cfg(test)]
662mod general{
663 use super::*;
664 use enumflags2::make_bitflags;
665
666 #[test]
667 fn nonmonotonic() {
668 let c = CounterI8::new_with_counting_behavior(make_bitflags!(CountingBehavior::{Nonmonotonic}));
670 (0..100).for_each(|_| c.inc_one() );
671 assert_eq!(c.get(), 100);
672 (0..100).for_each(|_| c.dec_one() );
673 assert_eq!(c.get(), 0);
674 (0..100).for_each(|_| c.inc_by(2) );
675 assert_eq!(c.get(), i8::MAX);
676 (0..100).for_each(|_| c.dec_by(5) );
677 assert_eq!(c.get(), i8::MIN);
678 }
679
680 #[test]
681 fn get_and() {
682 let c = CounterI32::new_from_offset_with_counting_behavior(33, make_bitflags!(CountingBehavior::{Decrement}));
683 assert_eq!(c.get_and_dec_by(34), 33, "get_and...method must return starting value");
684 assert_eq!(c.get(), -1, "counter must be set to new value following get_and... method")
685 }
686
687 #[test]
688 fn conflicting_behaviors() {
689 let c0 = CounterI16::new_with_counting_behavior(
690 CountingBehavior::make_behavior_flags(&[
691 CountingBehavior::Nonmonotonic,
692 CountingBehavior::Monotonic,
693 ])
694 );
696 assert!(
697 c0.get_behavior_conflicts().contains(&CountingBehaviorConflict::Always),
698 "Must detect conflicting monotonic/nonmonotonic counting behaviors"
699 );
700 let c1 = CounterI16::new_with_counting_behavior(make_bitflags!(CountingBehavior::{Increment | Decrement}));
702 assert!(
703 c1.get_behavior_conflicts().contains(&CountingBehaviorConflict::Always),
704 "Must detect conflicting counting increment/decrement behaviors"
705 );
706
707 let c2 = CounterI16::new_with_counting_behavior(
708 make_bitflags!(CountingBehavior::{Acyclic | Cyclic})
709 );
710 assert!(
711 c2.get_behavior_conflicts().contains(&CountingBehaviorConflict::Overflowing),
712 "Must detect conflicting counting acyclic/cyclic behaviors"
713 );
714
715 let c3 = CounterI16::new_with_counting_behavior(make_bitflags!(CountingBehavior::{Monotonic | Increment}));
716 assert!(
717 !c3.get_behavior_conflicts().contains(&CountingBehaviorConflict::Always),
718 "Must ignore nonconflicting counting behaviors"
719 );
720 }
721
722
723 #[test]
724 fn cyclic() {
725 let c = CounterU8::new_with_counting_behavior(
727 make_bitflags!(
728 CountingBehavior::{Monotonic | Increment | Cyclic}
729 )
730 );
731 let m = 100u8;
732 (0..m).for_each(|_| c.inc_one() );
733 assert_eq!(c.get(), m, "must increment specified amount");
734 (0..m).for_each(|_| c.dec_one() );
735 assert_eq!(c.get(), m, "must remain monotonic non-decreasing");
736 (0..(u8::MAX-m)).for_each(|_| c.inc_by(1) );
737 assert_eq!(c.get(), u8::MAX, "must increment to maximum");
738 (0..(m+1)).for_each(|_| c.inc_one() );
739 assert_eq!(c.get(), m, "must cycle around");
740 }
741
742}