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
//!# Lazy non-const Statics
//!This crate provides a simple global allocation item to store items as statics that arent
//!necessarily constant. "Global" is a bit of a misnomer, as these are more accurately described as
//!immutable non-constant statics.
//!
//![Global] dereferences to `T`, so it can be treated as a wrapper that allows any type to be static.
//!# Usage
//![Global] stores a function pointer that produces `T`. On the first deref call, this value will be
//!produced and allocated on the heap for the lifetime of the program. Subsequent calls will return
//!this cached value.
//!```rust
//!use global::Global;
//!
//!static MY_NUM: Global<i32> = Global::new(|| 5);
//!
//!fn main() {
//!    assert_eq!(*MY_NUM + 5, 10);
//!}
//!```
//!# Limitations
//!The biggest limitation is the double-pointer indirection that arises from storing a type that
//!itself allocates memory, such as [Vec] or [Box]. It also isn't possible to store DSTs, as the
//!data needs to be returned from a function on the stack. This could be fixed with a type that
//!offsets its allocation onto the producing closure, however types like [Vec] that require extra
//!information would still not work with this system.
//!
//!You may also want statics to simply be initalized on program startup,
//!which can be done with crates like ctor.
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::{ops::Deref, sync::OnceLock};


#[cfg_attr(docsrs, doc(cfg(feature = "ctor")))]
#[cfg(feature = "ctor")]
pub use ctor;

#[cfg_attr(docsrs, doc(cfg(feature = "ctor")))]
#[cfg(feature = "ctor")]
#[macro_export]
/// Generate a static with a ctor procedure.
/// 
///```rust
///use global::ctor_static;
///
///ctor_static! {
///    static MY_NUM: i32 = { 5 };
///    static MY_OTHER_NUM: i32 = { *MY_NUM * 2 };
///};
///```
macro_rules! ctor_static {
    ($($name:ident: $type: ty = $init:block);*;) => {
        $(static $name: Global<$type> = Global::new(|| $init););*
        
        #[crate::ctor::ctor]
        fn _global_init() {
            $($name.init());*
        }
    };
}



///Lazily evaluated static allocation.
pub struct Global<T> {
    f: fn() -> T,
    data: OnceLock<SendPtr<T>>
}

struct SendPtr<T>(pub *const T);
unsafe impl<T> Send for SendPtr<T> {}
unsafe impl<T> Sync for SendPtr<T> {}

impl<T> Deref for SendPtr<T> {
    type Target = *const T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}


impl<T> Global<T> {
    ///Constructs a new global.
    ///Rather than a value, this function takes a closure that produces a value.
    ///```rust
    ///use global::Global;
    ///
    ///static MY_TABLE: Global<Vec<&str>> = Global::new(|| vec!["a", "b", "c"]);
    pub const fn new(f: fn() -> T) -> Self {
        Self { f, data: OnceLock::new() }
    }

    ///Initializes the contents of a global. Does nothing if already initialized.
    pub fn init(&self) {
        if let None = self.data.get() { 
            let _ = unsafe { self.alloc() }; 
        }
    }

    ///Retrieves a reference to the value inside the global without allocating.
    ///This function will return `None` if the global has not been allocated.
    pub fn get(&self) -> Option<&T> {
        self.data.get().map(|ptr| {unsafe { &***ptr }})
    }

    ///Retrieves a reference to the value inside the global without allocating. Calling this function on
    ///an unallocated global is undefined behavior.
    pub unsafe fn get_unchecked(&self) -> &T {
        //lol
        &***self.data.get().unwrap_unchecked()
    } 
    
    ///Caller must ensure cell has not been already allocated
    unsafe fn alloc(&self) -> *const T {
        //box will panic if it cannot allocate
        let ptr = Box::leak(
            Box::new((self.f)())
            ) as *const T;
        self.data.set(SendPtr(ptr)).unwrap_unchecked();
        **self.data.get().unwrap_unchecked()
    }
}


impl<T> Deref for Global<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self.data.get() {
            Some(v) => unsafe { &***v },
            None => unsafe { &*self.alloc() },
        }
    }
}

static TEST: Global<u8> = Global::new(|| 5);

#[cfg(test)]
mod tests {
    use std::ops::Add;

    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(TEST.add(1), 6);
        assert_eq!(*TEST, 5);
    }

    #[test]
    #[cfg(feature = "ctor")]
    fn ctor_test() {
        ctor_static! { 
            THING: u32 = { 5 };
        };

        assert_eq!(THING.add(1), 6);
        assert_eq!(*THING, 5);
    } 
}