ic_stable_memory/
lib.rs

1#![warn(missing_docs)]
2
3//! This crate provides a number of "stable" data structures - collections that use canister's stable
4//! memory for storage, as well as other primitives that allow storing all data of your canister in stable memory.
5//! This crate also provides the [`SCertifiedBTreeMap`](collections::SCertifiedBTreeMap) - a Merkle-tree based collection that can
6//! be used to include custom data into canister's certified state tree.
7//!
8//! This documentation only covers API and some implementation details. For more useful info and
9//! tutorials, please visit [project's Github page](https://github.com/seniorjoinu/ic-stable-memory).
10//!
11//! # Features
12//! 1. Stable data structures release their memory automatically, following Rust's ownership rules.
13//! 2. Stable data structures also obey and enforce Rust's borrowing and lifetime rules.
14//! 3. Each data structure is aware of the limited nature of memory in IC and allows programmatic
15//! reaction for situations when your canister is out of stable memory.
16//! 3. Each data structure's performance is reasonably close to its std's analog.
17//! 4. Supported stable data structures: box, vec, log, hash-map, hash-set, btree-map, btree-set, certified-map.
18//! 5. In addition to these data structures, this crate provides you with a fully featured toolset
19//! to build your own data structure, if you need something more domain-specific.
20use crate::mem::allocator::StableMemoryAllocator;
21use mem::s_slice::SSlice;
22use std::cell::RefCell;
23
24mod benches;
25/// All collections provided by this crate
26pub mod collections;
27/// Traits and algorithms for internal data encoding
28pub mod encoding;
29/// Stable memory allocator and related structs
30pub mod mem;
31/// Stable memory smart-pointers
32pub mod primitive;
33/// Various utilities: certification, stable memory API wrapper etc.
34pub mod utils;
35
36pub use ic_stable_memory_derive as derive;
37
38use crate::utils::isoprint;
39pub use crate::utils::mem_context::{stable, OutOfMemory, PAGE_SIZE_BYTES};
40pub use encoding::{AsDynSizeBytes, AsFixedSizeBytes, Buffer};
41pub use primitive::s_box::SBox;
42pub use primitive::StableType;
43pub use utils::certification::{
44    empty, empty_hash, fork, fork_hash, labeled, labeled_hash, leaf, leaf_hash, AsHashTree,
45    AsHashableBytes,
46};
47
48thread_local! {
49    static STABLE_MEMORY_ALLOCATOR: RefCell<Option<StableMemoryAllocator>> = RefCell::new(None);
50}
51
52/// Initializes the [memory allocator](mem::allocator::StableMemoryAllocator).
53///
54/// This function should be called *ONLY ONCE* during the lifetime of a canister. For canisters,
55/// that are being build using this crate from scratch, the most apropriate place to call it is the
56/// `#[init]` canister method. For canisters which are migrating from standard data structures thic
57/// function should be added as a first line of `#[post_upgrade]` canister method and later (right after
58/// this canister upgrade happens) in the next code revision it should be replaced with [stable_memory_post_upgrade()].
59///
60/// Stable memory allocator is stored inside a `thread_local!` static variable at runtime.
61///
62/// Works the same way as [init_allocator(0)].
63///
64/// # Panics
65/// Panics if the allocator is already initialized.
66///
67/// # Examples
68/// For new canisters:
69/// ```rust
70/// # use ic_stable_memory::stable_memory_init;
71/// #[ic_cdk_macros::init]
72/// fn init() {
73///     stable_memory_init();
74///
75///     // the rest of the initialization
76/// }
77/// ```
78///
79/// For migrating canisters:
80/// ```rust
81/// // canister version N
82/// # use ic_stable_memory::stable_memory_init;
83/// #[ic_cdk_macros::post_upgrade]
84/// fn post_upgrade() {
85///     stable_memory_init();
86///
87///     // move data from standard collections into "stable" ones
88/// }
89/// ```
90/// ```rust
91/// // canister version N+1
92/// # use ic_stable_memory::stable_memory_post_upgrade;
93/// #[ic_cdk_macros::post_upgrade]
94/// fn post_upgrade() {
95///     stable_memory_post_upgrade();
96///
97///     // the rest of canister's reinitialization
98/// }
99/// ```
100#[inline]
101pub fn stable_memory_init() {
102    init_allocator(0);
103}
104
105/// Persists the memory allocator into stable memory between canister upgrades.
106///
107/// See also [stable_memory_post_upgrade].
108///
109/// This function should be called as the last step of the `#[pre_ugrade]` canister method.
110///
111/// It works by first writing the allocator to an `SBox` and then writing a pointer to that `SBox` into
112/// frist 8 bytes of stable memory (offsets [0..8)). `thread_local!` static variable that stores the
113/// allocator also gets cleared, if this function is executed successfully.
114///
115/// If it was impossible to allocate a memory block of required size, this function returns an [OutOfMemory]
116/// error. For tips on possible ways of resolving an [OutOfMemory] error visit [this page](https://github.com/seniorjoinu/ic-stable-memory/docs/out-of-memory-error-handling.md).
117///
118/// This function is an alias for [deinit_allocator()].
119///
120/// # Example
121/// ```rust
122/// # use ic_stable_memory::stable_memory_pre_upgrade;
123/// #[ic_cdk_macros::pre_upgrade]
124/// fn pre_upgrade() {
125///     // other pre-upgrade routine
126///
127///     if stable_memory_pre_upgrade().is_err() {
128///         panic!("Out of stable memory")
129///     }
130/// }
131/// ```
132///
133/// # Panics
134/// Panics if there is no initialized stable memory allocator.
135#[inline]
136pub fn stable_memory_pre_upgrade() -> Result<(), OutOfMemory> {
137    deinit_allocator()
138}
139
140/// Retrieves the memory allocator from stable memory.
141///
142/// See also [stable_memory_pre_upgrade].
143///
144/// This function should be called as the first step of the `#[post_upgrade]` canister method.
145///
146/// The process is exactly the same as in `stable_memory_pre_upgrade`, but in reverse order. It reads
147/// first 8 bytes of stable memory to get a pointer. Then the `SBox` located at that pointer is read
148/// and "unboxed" into the allocator. Then the allocator is assigned back to the `thread_local!` variable.
149///
150/// This function is an alias for [reinit_allocator()].
151///
152/// # Example
153/// ```rust
154/// use ic_stable_memory::stable_memory_post_upgrade;
155/// #[ic_cdk_macros::post_upgrade]
156/// fn post_upgrade() {
157///     stable_memory_post_upgrade();
158///
159///     // other post-upgrade routine
160/// }
161/// ```
162///
163/// # Panics
164/// This function will panic if:
165/// 1. there is no valid pointer stored at first 8 bytes of stable memory,
166/// 2. there is no valid `SBox` was found at that location,
167/// 3. deserialization step during `SBox`'s "unboxing" failed due to invalid data stored inside this `SBox`,
168/// 4. if there was an already initialized stable memory allocator.
169#[inline]
170pub fn stable_memory_post_upgrade() {
171    reinit_allocator();
172}
173
174/// An alias for [stable_memory_init], but allows limiting the maximum number of stable memory pages
175/// that the allocator can grow. [init_allocator(0)] works exactly the same as [stable_memory_init()].
176///
177/// This function is useful for testing, when one wants to see how a canister behaves when there is
178/// only a little of stable memory available.
179///
180/// If this function is invoked in a canister that already grown more stable memory pages than the
181/// argument states (this can happen for canisters that migrate from standard collections to "stable" ones),
182/// then the actual number of already grown pages is used as a maximum number of pages
183/// instead of what is passed as an argument.
184///
185/// Passing a `0` as an argument has a special "infinite" meaning, which means "grow as many pages
186/// as needed, while it is possible".
187///
188/// Internally calls [StableMemoryAllocator::init](mem::allocator::StableMemoryAllocator::init).
189#[inline]
190pub fn init_allocator(max_pages: u64) {
191    STABLE_MEMORY_ALLOCATOR.with(|it| {
192        if it.borrow().is_none() {
193            let allocator = StableMemoryAllocator::init(max_pages);
194
195            *it.borrow_mut() = Some(allocator);
196        } else {
197            unreachable!("StableMemoryAllocator can only be initialized once");
198        }
199    })
200}
201
202/// An alias for [stable_memory_pre_upgrade].
203///
204/// Internally calls [StableMemoryAllocator::store](mem::allocator::StableMemoryAllocator::store).
205///
206/// # Panics
207/// Panics if there is no initialized stable memory allocator.
208#[inline]
209pub fn deinit_allocator() -> Result<(), OutOfMemory> {
210    STABLE_MEMORY_ALLOCATOR.with(|it: &RefCell<Option<StableMemoryAllocator>>| {
211        if let Some(mut alloc) = it.take() {
212            let res = alloc.store();
213            if res.is_err() {
214                *it.borrow_mut() = Some(alloc);
215            }
216
217            res
218        } else {
219            unreachable!("StableMemoryAllocator is not initialized");
220        }
221    })
222}
223
224/// An alias for [stable_memory_post_upgrade].
225///
226/// Internally calls [StableMemoryAllocator::retrieve](mem::allocator::StableMemoryAllocator::retrieve).
227///
228/// # Panics
229/// Panics if there is no initialized stable memory allocator.
230#[inline]
231pub fn reinit_allocator() {
232    STABLE_MEMORY_ALLOCATOR.with(|it| {
233        if it.borrow().is_none() {
234            let allocator = StableMemoryAllocator::retrieve();
235
236            *it.borrow_mut() = Some(allocator);
237        } else {
238            unreachable!("StableMemoryAllocator can only be initialized once");
239        }
240    });
241}
242
243/// Persists a pointer to an [SBox] between canister upgrades mapped to some unique [usize] key.
244///
245/// See also [retrieve_custom_data].
246///
247/// Despite the fact that stable memory data structures from this crate store all data completely on
248/// stable memory, they themselves are stored on stack. Exactly how standard data structures ([Vec],
249/// for example) store their data on heap, but themselves are stored on stack. This means that in order
250/// to persist this data structures between canister upgrades, we have to temporary store them on
251/// stable memory aswell.
252///
253/// This function allows one to do that, by assigning a unique [usize] index to the stored data, which was
254/// previously stored in [SBox].
255///
256/// This function should be used in the `#[pre_upgrade]` canister method. Right before
257/// [stable_memory_pre_upgrade()] invocation. This function can be used multiple times, but one should
258/// make sure they always keep track of keys they are assigning custom data to. An attempt to assign
259/// two values to a single key will lead to losing the data that was assigned first. *Be careful!*
260///
261/// Internally calls [StableMemoryAllocator::store_custom_data](mem::allocator::StableMemoryAllocator::store_custom_data).
262///
263/// # Example
264/// ```rust
265/// # use ic_stable_memory::collections::SHashMap;
266/// # use ic_stable_memory::{retrieve_custom_data, SBox, stable_memory_init, stable_memory_post_upgrade, stable_memory_pre_upgrade, store_custom_data};
267/// # unsafe { ic_stable_memory::mem::clear(); }
268/// # stable_memory_init();
269/// static mut STATE: Option<SHashMap<u64, u64>> = None;
270///
271/// #[ic_cdk_macros::pre_upgrade]
272/// fn pre_upgrade() {
273///     let state = unsafe { STATE.take().unwrap() };
274///     let boxed_state = SBox::new(state).expect("Out of memory");
275///
276///     store_custom_data(1, boxed_state);
277///
278///     // always as a last expression of "pre_upgrade"
279///     stable_memory_pre_upgrade();
280/// }
281///
282/// #[ic_cdk_macros::post_upgrade]
283/// fn post_upgrade() {
284///     // always as a first expression of "post_upgrade"
285///     stable_memory_post_upgrade();
286///
287///     let boxed_state = retrieve_custom_data::<SHashMap<u64, u64>>(1)
288///         .expect("Key not found");
289///
290///     unsafe { STATE = Some(boxed_state.into_inner()); }
291/// }
292/// ```
293///
294/// One can also persist other data this way
295/// ```rust
296/// # use ic_stable_memory::{retrieve_custom_data, SBox, stable_memory_post_upgrade, stable_memory_pre_upgrade, store_custom_data};
297///
298/// #[ic_cdk_macros::pre_upgrade]
299/// fn pre_upgrade() {
300///     let very_important_string = String::from("THE PASSWORD IS 42");
301///     let boxed_string = SBox::new(very_important_string)
302///         .expect("Out of memory");
303///     
304///     store_custom_data(2, boxed_string);
305///
306///     // always as a last expression of "pre_upgrade"
307///     stable_memory_pre_upgrade();
308/// }
309///
310/// #[ic_cdk_macros::post_upgrade]
311/// fn post_upgrade() {
312///     // always as a first expression of "post_upgrade"
313///     stable_memory_post_upgrade();
314///
315///     let boxed_string = retrieve_custom_data::<String>(2)
316///         .expect("Key not found");
317///
318///     let very_important_string = boxed_string.into_inner();
319/// }
320/// ```
321///
322/// # Panics
323/// Panics if there is no initialized stable memory allocator.
324#[inline]
325pub fn store_custom_data<T: StableType + AsDynSizeBytes>(idx: usize, data: SBox<T>) {
326    STABLE_MEMORY_ALLOCATOR.with(|it| {
327        if let Some(alloc) = &mut *it.borrow_mut() {
328            alloc.store_custom_data(idx, data)
329        } else {
330            unreachable!("StableMemoryAllocator is not initialized");
331        }
332    })
333}
334
335/// Retrieves a pointer to some [SBox] stored previously.
336///
337/// See also [store_custom_data].
338///
339/// This function is intended to be invoked inside the `#[post_upgrade]` canister method. Right after
340/// [stable_memory_post_upgrade()] invocation. After retrieval, the key gets "forgotten", allowing
341/// reusing it again for other data.
342///
343/// Any panic in the `#[post_upgrade]` canister method results in broken canister.
344/// Please, *be careful*.
345///
346/// Internally calls [StableMemoryAllocator::retrieve_custom_data](mem::allocator::StableMemoryAllocator::retrieve_custom_data).
347///
348/// # Examples
349/// See examples of [store_custom_data].
350///
351/// # Panics
352/// Panics if there is no initialized stable memory allocator.
353#[inline]
354pub fn retrieve_custom_data<T: StableType + AsDynSizeBytes>(idx: usize) -> Option<SBox<T>> {
355    STABLE_MEMORY_ALLOCATOR.with(|it| {
356        if let Some(alloc) = &mut *it.borrow_mut() {
357            alloc.retrieve_custom_data(idx)
358        } else {
359            unreachable!("StableMemoryAllocator is not initialized");
360        }
361    })
362}
363
364/// Attempts to allocate a new [SSlice] of at least the required size or returns an [OutOfMemory] error
365/// if there is no continuous stable memory memory block of that size can be allocated.
366///
367/// Memory block that is returned *can be bigger* than requested. This happens because:
368/// 1. Sizes for allocation are always getting rounded up to the next multiple of 8 bytes. For example,
369/// if requested 100 bytes to allocate requested, the resulting memory block can't be smaller than 104 bytes.
370/// 2. Minimum memory block size is 16 bytes. To find out more see documentation for [the allocator](mem::allocator::StableMemoryAllocator).
371/// 3. If the allocator has a free block of size less than `requested size + minimum block size`,
372/// this block won't be split and will be returned as is.
373///
374/// If the allocator only has a memory block which is bigger than `requested size + minimum block size`,
375/// that block gets split it two. The first half is returned as the result of this function, and the other
376/// half goes back to the free list.
377///
378/// If the allocator has no apropriate free memory block to allocate, it will try to grow stable memory
379/// by the number of pages enough to allocate a block of that size. If it can't grow due to lack of
380/// stable memory in a subnet or due to reaching `max_pages` limit set earlier - it will return an
381/// [OutOfMemory] error.
382///
383/// Internally calls [StableMemoryAllocator::allocate](mem::allocator::StableMemoryAllocator::allocate).
384///
385/// # Example
386/// ```rust
387/// // slice size is in [104..136) bytes range, despite requesting for only 100 bytes
388/// # use ic_stable_memory::{allocate, stable_memory_init};
389/// # unsafe { ic_stable_memory::mem::clear(); }
390/// # stable_memory_init();
391/// # unsafe {
392/// let slice = allocate(100).expect("Not enough stable memory");
393/// # }
394/// ```
395///
396/// # Panics
397/// Panics if there is no initialized stable memory allocator.
398///
399/// # Safety
400/// Don't forget to [deallocate] the memory block, when you're done!
401#[inline]
402pub unsafe fn allocate(size: u64) -> Result<SSlice, OutOfMemory> {
403    STABLE_MEMORY_ALLOCATOR.with(|it| {
404        if let Some(alloc) = &mut *it.borrow_mut() {
405            alloc.allocate(size)
406        } else {
407            unreachable!("StableMemoryAllocator is not initialized");
408        }
409    })
410}
411
412/// Deallocates an already allocated [SSlice] freeing it's memory.
413///
414/// Supplied [SSlice] get's transformed into [FreeBlock](mem::free_block::FreeBlock) and then an
415/// attempt to merge it with neighboring (physically) free blocks is performed.
416///
417/// Internally calls [StableMemoryAllocator::deallocate](mem::allocator::StableMemoryAllocator::deallocate).
418///
419/// # Example
420/// ```rust
421/// # use ic_stable_memory::{allocate, deallocate, stable_memory_init};
422/// # unsafe { ic_stable_memory::mem::clear(); }
423/// # stable_memory_init();
424/// # unsafe {
425/// let slice = allocate(100).expect("Out of memory");
426/// deallocate(slice);
427/// # }
428/// ```
429///
430/// # Panics
431/// Panics if there is no initialized stable memory allocator.
432#[inline]
433pub fn deallocate(slice: SSlice) {
434    STABLE_MEMORY_ALLOCATOR.with(|it| {
435        if let Some(alloc) = &mut *it.borrow_mut() {
436            alloc.deallocate(slice)
437        } else {
438            unreachable!("StableMemoryAllocator is not initialized");
439        }
440    })
441}
442
443/// Attempts to reallocate a memory block growing its size and possibly moving its content to a new
444/// location.
445///
446/// At first it tries to perform an `inplace reallocation` - check if the next neighboring (physically)
447/// memory block is also free. If that is so, this neighboring (or only a chunk of it, if it's too big)
448/// free block gets merged with the one passed as an argument to this function and returned as a result.
449/// This process does not move the data.
450///
451/// If there is no neighboring free block, than a sequence of operations is performed:
452/// 1. Copy the data to a heap-allocated byte buffer.
453/// 2. Deallocate the [SSlice] passed as an argument to this function.
454/// 3. Allocate a new [SSlice] of the requested size, possibly returning an [OutOfMemory] error.
455/// 4. Copy data from the byte buffer to this new [SSlice].
456/// 5. Return it as a result.
457/// This process moves the data.
458///
459/// If the requested new size is less than the actual size of the [SSlice] passed as an argument,
460/// the function does nothing and returns this [SSlice] as a result back.
461///
462/// Internally calls [StableMemoryAllocator::reallocate](mem::allocator::StableMemoryAllocator::reallocate).
463///
464/// # Example
465/// ```rust
466/// # use ic_stable_memory::{allocate, stable_memory_init, reallocate};
467/// # unsafe { ic_stable_memory::mem::clear(); }
468/// # stable_memory_init();
469/// # unsafe {
470/// let slice = allocate(100).expect("Out of memory");
471/// let bigger_slice = reallocate(slice, 200).expect("Out of memory");
472/// # }
473/// ```
474///
475/// # Panics
476/// Panics if there is no initialized stable memory allocator.
477/// Reallocating [SSlice]s bigger than [u32::MAX] bytes will also panic.
478///
479/// # Safety
480/// Don't forget to [deallocate] the memory block, when you're done!
481#[inline]
482pub unsafe fn reallocate(slice: SSlice, new_size: u64) -> Result<SSlice, OutOfMemory> {
483    STABLE_MEMORY_ALLOCATOR.with(|it| {
484        if let Some(alloc) = &mut *it.borrow_mut() {
485            alloc.reallocate(slice, new_size)
486        } else {
487            unreachable!("StableMemoryAllocator is not initialized");
488        }
489    })
490}
491
492/// Checks if it would be possible to allocate a block of stable memory of the provided size right now.
493///
494/// The allocator will check its free list for a block of appropriate size. If there is no such free
495/// block, it will try to grow stable memory by the number of pages enough to fit this size.
496///
497/// Returns `true` if a block was found. Returns `false` if an attempt to grow stable memory resulted in
498/// an [OutOfMemory] error.
499///
500/// Internally calls [StableMemoryAllocator::make_sure_can_allocate](mem::allocator::StableMemoryAllocator::make_sure_can_allocate).
501///
502/// # Example
503/// ```rust
504/// # use ic_stable_memory::{make_sure_can_allocate, stable_memory_init};
505/// # unsafe { ic_stable_memory::mem::clear(); }
506/// # stable_memory_init();
507/// if make_sure_can_allocate(1_000_000) {
508///     println!("It is possible to allocate a million bytes of stable memory");
509/// }
510/// ```
511///
512/// # Panics
513/// Panics if there is no initialized stable memory allocator.
514#[inline]
515pub fn make_sure_can_allocate(size: u64) -> bool {
516    STABLE_MEMORY_ALLOCATOR.with(|it| {
517        if let Some(alloc) = &mut *it.borrow_mut() {
518            alloc.make_sure_can_allocate(size)
519        } else {
520            unreachable!("StableMemoryAllocator is not initialized");
521        }
522    })
523}
524
525/// Returns the amount of stable memory in bytes which is under the allocator's management.
526///
527/// Always equals to [stable64_size()](ic_cdk::api::stable::stable64_size) - `8`.
528///
529/// Internally calls [StableMemoryAllocator::get_available_size](mem::allocator::StableMemoryAllocator::get_available_size).
530///
531/// # Panics
532/// Panics if there is no initialized stable memory allocator.
533#[inline]
534pub fn get_available_size() -> u64 {
535    STABLE_MEMORY_ALLOCATOR.with(|it| {
536        if let Some(alloc) = &*it.borrow() {
537            alloc.get_available_size()
538        } else {
539            unreachable!("StableMemoryAllocator is not initialized");
540        }
541    })
542}
543
544/// Returns the amount of free stable memory in bytes.
545///
546/// Internally calls [StableMemoryAllocator::get_free_size](mem::allocator::StableMemoryAllocator::get_free_size).
547///
548/// # Panics
549/// Panics if there is no initialized stable memory allocator.
550#[inline]
551pub fn get_free_size() -> u64 {
552    STABLE_MEMORY_ALLOCATOR.with(|it| {
553        if let Some(alloc) = &*it.borrow() {
554            alloc.get_free_size()
555        } else {
556            unreachable!("StableMemoryAllocator is not initialized");
557        }
558    })
559}
560
561/// Returns the amount of allocated stable memory in bytes.
562///
563/// Always equal to [get_available_size()] - [get_free_size()].
564///
565/// Internally calls [StableMemoryAllocator::get_allocated_size](mem::allocator::StableMemoryAllocator::get_allocated_size).
566///
567/// # Panics
568/// Panics if there is no initialized stable memory allocator.
569#[inline]
570pub fn get_allocated_size() -> u64 {
571    STABLE_MEMORY_ALLOCATOR.with(|it| {
572        if let Some(alloc) = &*it.borrow() {
573            alloc.get_allocated_size()
574        } else {
575            unreachable!("StableMemoryAllocator is not initialized");
576        }
577    })
578}
579
580/// Returns `max_pages` parameter.
581///
582/// See [init_allocator] for more details.
583///
584/// # Panics
585/// Panics if there is no initialized stable memory allocator.
586#[inline]
587pub fn get_max_pages() -> u64 {
588    STABLE_MEMORY_ALLOCATOR.with(|it| {
589        if let Some(alloc) = &*it.borrow() {
590            alloc.get_max_pages()
591        } else {
592            unreachable!("StableMemoryAllocator is not initialized");
593        }
594    })
595}
596
597#[inline]
598pub fn _debug_validate_allocator() {
599    STABLE_MEMORY_ALLOCATOR.with(|it: &RefCell<Option<StableMemoryAllocator>>| {
600        if let Some(alloc) = &*it.borrow() {
601            alloc.debug_validate_free_blocks();
602        } else {
603            unreachable!("StableMemoryAllocator is not initialized");
604        }
605    })
606}
607
608#[inline]
609pub fn _debug_print_allocator() {
610    STABLE_MEMORY_ALLOCATOR.with(|it| {
611        if let Some(alloc) = &*it.borrow_mut() {
612            isoprint(format!("{alloc:?}").as_str());
613        } else {
614            unreachable!("StableMemoryAllocator is not initialized");
615        }
616    })
617}
618
619#[cfg(test)]
620mod tests {
621    use crate::{
622        _debug_print_allocator, allocate, deallocate, get_allocated_size, get_free_size,
623        init_allocator, reallocate, retrieve_custom_data, stable_memory_init,
624        stable_memory_post_upgrade, stable_memory_pre_upgrade, store_custom_data, SBox,
625    };
626    use crate::{deinit_allocator, reinit_allocator, SSlice};
627
628    #[test]
629    fn basic_flow_works_fine() {
630        stable_memory_init();
631        stable_memory_pre_upgrade();
632        stable_memory_post_upgrade();
633
634        let b = unsafe { allocate(100).unwrap() };
635        let b = unsafe { reallocate(b, 200).unwrap() };
636        deallocate(b);
637
638        assert_eq!(get_allocated_size(), 0);
639        assert!(get_free_size() > 0);
640
641        _debug_print_allocator();
642
643        assert_eq!(retrieve_custom_data::<u64>(1), None);
644        store_custom_data(1, SBox::new(100u64).unwrap());
645        assert_eq!(retrieve_custom_data::<u64>(1).unwrap().into_inner(), 100);
646
647        _debug_print_allocator();
648    }
649
650    #[test]
651    #[should_panic]
652    fn init_allocator_twice_should_panic() {
653        init_allocator(0);
654        init_allocator(0);
655    }
656
657    #[test]
658    #[should_panic]
659    fn deinit_allocator_should_panic() {
660        deinit_allocator();
661    }
662
663    #[test]
664    #[should_panic]
665    fn reinit_allocator_twice_should_panic() {
666        init_allocator(0);
667        reinit_allocator();
668    }
669
670    #[test]
671    #[should_panic]
672    fn allocate_without_allocator_should_panic() {
673        unsafe { allocate(10) };
674    }
675
676    #[test]
677    #[should_panic]
678    fn deallocate_without_allocator_should_panic() {
679        deallocate(SSlice::new(0, 10, false));
680    }
681
682    #[test]
683    #[should_panic]
684    fn reallocate_without_allocator_should_panic() {
685        unsafe { reallocate(SSlice::new(0, 10, false), 20) };
686    }
687
688    #[test]
689    #[should_panic]
690    fn get_allocated_size_without_allocator_should_panic() {
691        get_allocated_size();
692    }
693
694    #[test]
695    #[should_panic]
696    fn get_free_size_without_allocator_should_panic() {
697        get_free_size();
698    }
699
700    #[test]
701    #[should_panic]
702    fn get_custom_data_without_allocator_should_panic() {
703        retrieve_custom_data::<u64>(0);
704    }
705
706    #[test]
707    #[should_panic]
708    fn set_custom_data_without_allocator_should_panic() {
709        store_custom_data(0, SBox::new(0).unwrap());
710    }
711
712    #[test]
713    #[should_panic]
714    fn debug_print_without_allocator_should_panic() {
715        _debug_print_allocator();
716    }
717}