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}