Skip to main content

fbl/
size_tracker.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::marker::PhantomData;
6
7/// Trait for tracking the size of the list.
8pub trait SizeTracker: Clone {
9    /// The initial value for the tracker.
10    const INIT: Self;
11    /// True if this tracker actually tracks the size.
12    const IS_TRACKING: bool;
13    /// Increments the size count.
14    fn increment(&mut self);
15    /// Decrements the size count.
16    fn decrement(&mut self);
17    /// Returns the current size.
18    fn get(&self) -> usize;
19    /// Sets the size count.
20    fn set(&mut self, size: usize);
21    /// Swaps the size count with another tracker.
22    fn swap(&mut self, other: &mut Self);
23}
24
25/// A size tracker that does not actually track the size (zero overhead).
26///
27/// Carries a `PhantomData` instead of being a unit struct so that containers using it stay
28/// FFI-safe when embedded in `#[repr(C)]` structs named in `extern` signatures:
29/// `improper_ctypes` rejects fieldless structs but ignores `PhantomData`.
30#[derive(Clone, Copy)]
31#[repr(transparent)]
32pub struct NonTrackingSize(PhantomData<()>);
33impl SizeTracker for NonTrackingSize {
34    const INIT: Self = NonTrackingSize(PhantomData);
35    const IS_TRACKING: bool = false;
36    #[inline]
37    fn increment(&mut self) {}
38    #[inline]
39    fn decrement(&mut self) {}
40    #[inline]
41    fn get(&self) -> usize {
42        panic!("Cannot get the size if we are not tracking the size.")
43    }
44    #[inline]
45    fn set(&mut self, _size: usize) {}
46    #[inline]
47    fn swap(&mut self, _other: &mut Self) {}
48}
49
50/// A size tracker that maintains the count of elements in the list.
51///
52/// `repr(transparent)` keeps containers using this tracker FFI-safe when embedded in `#[repr(C)]`
53/// structs named in `extern` signatures.
54#[derive(Clone, Copy)]
55#[repr(transparent)]
56pub struct TrackingSize(usize);
57impl SizeTracker for TrackingSize {
58    const INIT: Self = TrackingSize(0);
59    const IS_TRACKING: bool = true;
60    #[inline]
61    fn increment(&mut self) {
62        self.0 += 1;
63    }
64    #[inline]
65    fn decrement(&mut self) {
66        self.0 -= 1;
67    }
68    #[inline]
69    fn get(&self) -> usize {
70        self.0
71    }
72    #[inline]
73    fn set(&mut self, size: usize) {
74        self.0 = size;
75    }
76    #[inline]
77    fn swap(&mut self, other: &mut Self) {
78        core::mem::swap(&mut self.0, &mut other.0);
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    #[should_panic(expected = "Cannot get the size if we are not tracking the size.")]
88    fn test_non_tracking_size_get_panics() {
89        let tracker = NonTrackingSize::INIT;
90        let _ = tracker.get();
91    }
92
93    #[test]
94    fn test_non_tracking_size_set_swap() {
95        let mut tracker1 = NonTrackingSize::INIT;
96        let mut tracker2 = NonTrackingSize::INIT;
97        tracker1.increment();
98        tracker1.decrement();
99        tracker1.set(10);
100        tracker1.swap(&mut tracker2);
101        assert!(!NonTrackingSize::IS_TRACKING);
102    }
103
104    #[test]
105    fn test_tracking_size_increment_decrement() {
106        let mut tracker = TrackingSize::INIT;
107        assert!(TrackingSize::IS_TRACKING);
108        assert_eq!(tracker.get(), 0);
109        tracker.increment();
110        assert_eq!(tracker.get(), 1);
111        tracker.increment();
112        assert_eq!(tracker.get(), 2);
113        tracker.decrement();
114        assert_eq!(tracker.get(), 1);
115    }
116
117    #[test]
118    fn test_tracking_size_set() {
119        let mut tracker = TrackingSize::INIT;
120        tracker.set(100);
121        assert_eq!(tracker.get(), 100);
122    }
123
124    #[test]
125    fn test_tracking_size_swap() {
126        let mut tracker1 = TrackingSize::INIT;
127        tracker1.set(100);
128        let mut tracker2 = TrackingSize::INIT;
129        tracker2.set(50);
130
131        tracker1.swap(&mut tracker2);
132        assert_eq!(tracker1.get(), 50);
133        assert_eq!(tracker2.get(), 100);
134    }
135}