1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

//! A library for creating debug-only tags to track and check where values are used.
//! 
//! Useful for asserting that two values stem from the same origin. For example, an operation on two
//! nodes may only be valid if the nodes are in the same graph. `DebugTag` can be used to check for
//! this in tests and when debugging.
//! 
//! See the `DebugTag` type for more details.
//! 
//! # Motivation
//! 
//! When developing Rust, we often use *indices* to refer to other values instead of using
//! *pointers*. Using indices allows us to "point" to a value without borrowing it and can basically
//! be seen as bypassing the borrow checker in a restricted way.
//! 
//! One of the issues of using indicies, is that it is not connected at compile time to the
//! container storing the value. An index used with an incorrect container might panic, or worse,
//! return garbage values.
//! 
//! We need a way to "tag" values such that we can determine if an index is used with the correct
//! container.
//! 
//! # Example
//! 
//! ```
//! use debug_tag::DebugTag;
//! 
//! /// An example vec-like structure that allows pushing values and getting values by index.
//! #[derive(Default)]
//! struct Slab<T>{
//!     items: Vec<T>,
//!     tag: DebugTag,
//! }
//! 
//! /// An index into a value on a slab.
//! #[derive(Copy, Clone)]
//! struct Index {
//!     index: usize,
//!     tag: DebugTag,
//! }
//! 
//! impl<T> Slab<T> {
//!     /// Pushes a new value onto the slab.
//!     fn push(&mut self, item: T) -> Index {
//!         let index = self.items.len();
//!         self.items.push(item);
//!         Index {
//!             index,
//!             tag: self.tag
//!         }
//!     }
//! 
//!     /// Gets a value in this `Slab`. If the index is not from this slab, either panics or
//!     /// returns an arbitrary value.
//!     fn get(&self, index: Index) -> &T {
//!         assert_eq!(self.tag, index.tag, "Index must stem from this slab");
//!         &self.items[index.index]
//!     }
//! }
//! 
//! let mut slab_a = Slab::default();
//! let ix = slab_a.push(42);
//! 
//! assert_eq!(slab_a.get(ix), &42);
//! 
//! let mut slab_b = Slab::default();
//! slab_b.push(1337);
//! 
//! // Panics due to the tags being checked:
//! //   assert_eq!(slab_b.get(ix), &1337);
//! ``` 
//! 
//! Without `DebugTag`s, the last line above would just return 1337 - a "garbage value" since `ix`
//! stems from a different `Slab`.

#[cfg(debug_assertions)]
mod checked {
    use std::cell::Cell;
    use std::sync::atomic::{AtomicU32, Ordering};

    // The increment to global every time we fetch a local tag offset. This is equal to
    // 2**32 * (1 - 1/(golden ratio)), which ends up distributing offsets well for an arbitrary
    // number of local threads.
    const INCREMENT: u32 = 1_640_531_527;

    static GLOBAL: AtomicU32 = AtomicU32::new(INCREMENT);

    thread_local! {
        static LOCAL: Cell<u32> = Cell::new(GLOBAL.fetch_add(INCREMENT, Ordering::SeqCst));
    }

    pub fn next() -> u32 {
        LOCAL.with(|local| {
            let old = local.get();
            local.set(old.wrapping_add(1));
            old
        })
    }
}

/// A value that guarentees that if two `DebugTag`s are not equal, then they are *not* clones.
/// 
/// This can be used to tag information during debug, such that the use of a value can be tracked
/// and checked. For example, you can use this to ensure (in debug) that a value returned by a data
/// structure is only used with the instance that returned the value.
/// 
/// This tagging is only done if `debug_assertions` is set. If `debug_assertions` is not set, then
/// all `DebugTags` are equal. Even if `debug_assertions` is set, two `DebugTag`s that are not
/// clones can still be equal. This is unlikely, however.
/// 
/// Therefore, functionality should not directly depend on the equality these tags but only use them
/// for additional sanity checks.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub struct DebugTag(
    #[cfg(debug_assertions)]
    u32,
);

impl Default for DebugTag {
    fn default() -> DebugTag {
        #[cfg(debug_assertions)]
        let tag = DebugTag(checked::next());

        #[cfg(not(debug_assertions))]
        let tag = DebugTag();

        tag
    }
}

impl DebugTag {
    /// Creates a new `DebugTag`
    pub fn new() -> DebugTag {
        DebugTag::default()
    }

    /// Create a new tag with the specified value.
    /// 
    /// Prefer using `new` instead, which will generate a value. Use this only in cases where that 
    /// is not possible, like when creating a const debug tag.
    /// 
    /// The tag value should be a randomly chosen constant.
    pub const fn from(_tag: u32) -> DebugTag {
        #[cfg(debug_assertions)]
        let tag = DebugTag(_tag);

        #[cfg(not(debug_assertions))]
        let tag = DebugTag();

        tag
    }
}

#[cfg(test)]
mod tests {
    use super::DebugTag;

    #[test]
    #[cfg(debug_assertions)]
    fn not_equal() {
        let a = DebugTag::new();
        let b = DebugTag::new();
        assert!(a != b); 
    }

    #[test]
    fn equal() {
        let a = DebugTag::new();
        let b = a.clone();
        assert!(a == b);
    }

    #[test]
    fn reflexive() {
        let a = DebugTag::new();
        assert!(a == a);
    }
}