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
// Copyright (C) 2017 Pietro Albini
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Fisher's global state.
//!
//! This module contains the code that keeps the Fisher global state. This is
//! used for example to generate unique IDs across the codebase. The main
//! [`State`](struct.State.html) struct is also marked as Sync and Send, so
//! it can be used across threads without locking.

use std::sync::atomic::{AtomicUsize, Ordering};
use std::cmp::PartialOrd;
use std::cmp::Ordering as CmpOrdering;


/// This enum represents a kind of ID.
///
/// You should use this to specify which ID you do want.

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum IdKind {
    /// This kind should be used to identify hooks.
    HookId,

    /// This kind should be used to identify threads.
    ThreadId,

    #[doc(hidden)]
    __NonExaustiveMatch,
}


/// This struct contains an unique ID.
///
/// The struct is intentionally opaque, so you won't be able to get the actual
/// value of the ID, but you can compare multiple IDs to get which one is
/// greater, and check if multiple IDs are equal. This is done to be able to
/// swap the inner implementation without breaking any code.

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct UniqueId {
    id: usize,
    kind: IdKind,
}

impl PartialOrd for UniqueId {

    fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
        if self.kind == other.kind {
            self.id.partial_cmp(&other.id)
        } else {
            None
        }
    }
}


/// This struct keeps the global state of Fisher.

#[derive(Debug)]
pub struct State {
    counter: AtomicUsize,
}

impl State {

    /// Create a new instance of the struct.
    pub fn new() -> Self {
        State {
            counter: AtomicUsize::new(0),
        }
    }

    /// Get the next ID for a specific [`IdKind`](enum.IdKind.html). The ID is
    /// guaranteed to be unique and greater than the last ID.
    pub fn next_id(&self, kind: IdKind) -> UniqueId {
        UniqueId {
            id: self.counter.fetch_add(1, Ordering::SeqCst),
            kind: kind,
        }
    }
}


#[cfg(test)]
mod tests {
    use super::{State, IdKind};


    #[test]
    fn test_next_id() {
        // State must always increment
        let state = State::new();
        let id1 = state.next_id(IdKind::HookId);
        let id2 = state.next_id(IdKind::HookId);
        let id3 = state.next_id(IdKind::ThreadId);

        assert!(id1 < id2);
        assert!(id1 == id1);
        assert!(id1 != id2);
        assert!(id1 != id3);
    }
}