├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .idea ├── .gitignore ├── dictionaries │ ├── NPC.xml │ └── d_demin.xml ├── inplace_it.iml ├── misc.xml ├── modules.xml ├── runConfigurations │ ├── Check.xml │ └── Test.xml └── vcs.xml ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── src ├── alloc_array.rs ├── fixed_array.rs ├── guards │ ├── mod.rs │ ├── slice_memory_guard.rs │ └── uninitialized_slice_memory_guard.rs └── lib.rs └── tests ├── alloc_array.rs ├── drop_correctness.rs └── stackalloc_correctness.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-n-test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Build 11 | run: cargo build --verbose 12 | - name: Run tests 13 | run: cargo test --verbose --all 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/dictionaries/NPC.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | deref 5 | inplace 6 | uninit 7 | usize 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/dictionaries/d_demin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | deref 5 | inplace 6 | inplaceable 7 | iterator's 8 | uninit 9 | usize 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/inplace_it.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Check.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inplace_it" 3 | version = "0.3.6" 4 | authors = ["Dmitry Demin "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "Place small arrays on the stack with a low-cost!" 8 | repository = "https://github.com/NotIntMan/inplace_it" 9 | readme = "README.md" 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dmitry Demin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inplace it! 2 | 3 | [![Version badge](https://img.shields.io/crates/v/inplace_it.svg)](https://crates.io/crates/inplace_it) 4 | [![License badge](https://img.shields.io/crates/l/inplace_it.svg)](https://github.com/NotIntMan/inplace_it/blob/master/LICENSE.txt) 5 | [![Build Status](https://github.com/NotIntMan/inplace_it/workflows/Build%20and%20test/badge.svg)](https://github.com/NotIntMan/inplace_it/actions) 6 | 7 | Place small arrays on the stack with a low cost! 8 | 9 | The only price you should pay for this is the price of choosing 10 | a type based on the size of the requested array! This is just one `match` and `call`! 11 | 12 | ## What? 13 | 14 | This crate is created for one purpose: allocating small arrays on the stack. 15 | The simplest way to use it is: 16 | 17 | ```rust 18 | use inplace_it::{inplace_or_alloc_array, UninitializedSliceMemoryGuard}; 19 | 20 | inplace_or_alloc_array( 21 | 150, // size of needed array to allocate 22 | |mut uninit_guard: UninitializedSliceMemoryGuard| { // and this is consumer of uninitialized memory 23 | 24 | // Size of stack-allocated memory can be more or equal to requested, but never less. 25 | assert_eq!(160, uninit_guard.len()); 26 | 27 | { 28 | // You can borrow guard to reuse memory 29 | let borrowed_uninit_guard = uninit_guard.borrow(); 30 | // Let's initialize memory 31 | // Note that borrowed_uninit_guard will be consumed (destroyed to produce initialized memory guard) 32 | let init_guard = borrowed_uninit_guard.init(|index| index as u16 + 1); 33 | // Memory now contains elements [1, 2, ..., 160] 34 | // Lets check it. Sum of [1, 2, ..., 160] = 12880 35 | let sum: u16 = init_guard.iter().sum(); 36 | assert_eq!(sum, 12880); 37 | } 38 | 39 | { 40 | // If you don't want to reuse memory, you can init new guard directly 41 | let init_guard = uninit_guard.init(|index| index as u16 * 2); 42 | // Memory now contains elements [0, 2, 4, ..., 318] 43 | // Lets check it. Sum of [0, 2, 4, ..., 318] = 25440 44 | let sum: u16 = init_guard.iter().sum(); 45 | assert_eq!(sum, 25440); 46 | } 47 | } 48 | ) 49 | ``` 50 | 51 | ## Why? 52 | 53 | Because allocation on the stack (i.e. placing variables) is **MUCH FASTER** then usual 54 | allocating in the heap. 55 | 56 | ## Moar! 57 | 58 | You can read the [API reference](https://docs.rs/inplace_it) for more details 59 | or create an [new issue](https://github.com/NotIntMan/inplace_it/issues/new) 60 | to submit a bug, feature request or just ask a question. 61 | 62 | ## Release notes 63 | 64 | | Version | Notes | 65 | |---------|-------| 66 | | 0.3.6 | Add no_std support. | 67 | | 0.3.5 | Remove useless FixedArray trait. | 68 | | 0.3.4 | Fix incorrect use of unstable intrinsic. | 69 | | 0.3.3 | Some sugar for easy placing from `Iterator`'s. | 70 | | 0.3.2 | Placing of uninit memory moved out from `try_inplace_array` to disallow compiler to optimize it.| 71 | | 0.3.1 | Initialize with an exact-size iterator. | 72 | | 0.3.0 |
  • API safety. No more unsafe external functions.
  • Drop correctness. No more dropping of uninitialized memory.
| 73 | | 0.2.2 | Fixed drop-correctness for safe functions. Now unsafe function do not drop your data but safe function do it correctly. | 74 | -------------------------------------------------------------------------------- /src/alloc_array.rs: -------------------------------------------------------------------------------- 1 | use core::mem::MaybeUninit; 2 | use alloc::vec::Vec; 3 | 4 | use crate::try_inplace_array; 5 | use crate::guards::UninitializedSliceMemoryGuard; 6 | 7 | /// `alloc_array` is used when `inplace_or_alloc_array` realize that the size of requested array of `T` 8 | /// is too large and should be replaced in the heap. 9 | /// 10 | /// It allocates a vector with `size` elements and fills it up with help of `init` closure 11 | /// and then pass a reference to a slice of the vector into the `consumer` closure. 12 | /// `consumer`'s result will be returned. 13 | pub fn alloc_array) -> R>(size: usize, consumer: Consumer) -> R { 14 | unsafe { 15 | let mut memory_holder = Vec::>::with_capacity(size); 16 | memory_holder.set_len(size); 17 | let result = consumer(UninitializedSliceMemoryGuard::new(&mut *memory_holder)); 18 | memory_holder.set_len(0); 19 | result 20 | } 21 | } 22 | 23 | /// `inplace_or_alloc_array` is a central function of this crate. 24 | /// It's trying to place an array of `T` on the stack and pass the guard of memory into the 25 | /// `consumer` closure. `consumer`'s result will be returned. 26 | /// 27 | /// If the result of array of `T` is more than 4096 then the vector will be allocated 28 | /// in the heap and will be used instead of stack-based fixed-size array. 29 | /// 30 | /// Sometimes size of allocated array might be more than requested. For sizes larger than 32, 31 | /// the following formula is used: `roundUp(size/32)*32`. This is a simplification that used 32 | /// for keeping code short, simple and able to optimize. 33 | /// For example, for requested 50 item `[T; 64]` will be allocated. 34 | /// For 120 items - `[T; 128]` and so on. 35 | /// 36 | /// Note that rounding size up is working for fixed-sized arrays only. If function decides to 37 | /// allocate a vector then its size will be equal to requested. 38 | /// 39 | /// # Examples 40 | /// 41 | /// ```rust 42 | /// use inplace_it::{ 43 | /// inplace_or_alloc_array, 44 | /// UninitializedSliceMemoryGuard, 45 | /// }; 46 | /// 47 | /// let sum: u16 = inplace_or_alloc_array(100, |uninit_guard: UninitializedSliceMemoryGuard| { 48 | /// assert_eq!(uninit_guard.len(), 128); 49 | /// // For now, our memory is placed/allocated but uninitialized. 50 | /// // Let's initialize it! 51 | /// let guard = uninit_guard.init(|index| index as u16 * 2); 52 | /// // For now, memory contains content like [0, 2, 4, 6, ..., 252, 254] 53 | /// guard.iter().sum() 54 | /// }); 55 | /// // Sum of [0, 2, 4, 6, ..., 252, 254] = sum of [0, 1, 2, 3, ..., 126, 127] * 2 = ( 127 * (127+1) ) / 2 * 2 56 | /// assert_eq!(sum, 127 * 128); 57 | /// ``` 58 | pub fn inplace_or_alloc_array(size: usize, consumer: Consumer) -> R 59 | where Consumer: FnOnce(UninitializedSliceMemoryGuard) -> R 60 | { 61 | match try_inplace_array(size, consumer) { 62 | Ok(result) => result, 63 | Err(consumer) => alloc_array(size, consumer), 64 | } 65 | } 66 | 67 | /// `inplace_or_alloc_from_iter` is helper function used to easy trying to place data from `Iterator`. 68 | /// 69 | /// It tries to get upper bound of `size_hint` of iterator and forward it to `inplace_or_alloc_array` function. 70 | /// It there is not upper bound hint from iterator, then it just `collect` your data into `Vec`. 71 | /// 72 | /// If iterator contains more data that `size_hint` said 73 | /// (and more than `try_inplace_array` function placed on stack), 74 | /// then items will be moved and collected (by iterating) into `Vec` also. 75 | /// 76 | /// # Examples 77 | /// 78 | /// ```rust 79 | /// // Some random number to demonstrate 80 | /// let count = 42; 81 | /// let iterator = 0..count; 82 | /// 83 | /// let result = ::inplace_it::inplace_or_alloc_from_iter(iterator.clone(), |mem| { 84 | /// assert_eq!(mem.len(), count); 85 | /// assert!(mem.iter().cloned().eq(iterator)); 86 | /// 87 | /// // Some result 88 | /// format!("{}", mem.len()) 89 | /// }); 90 | /// assert_eq!(result, format!("{}", count)); 91 | /// ``` 92 | pub fn inplace_or_alloc_from_iter(iter: Iter, consumer: Consumer) -> R 93 | where Iter: Iterator, 94 | Consumer: FnOnce(&mut [Iter::Item]) -> R, 95 | { 96 | match iter.size_hint().1 { 97 | Some(upper_bound_hint) => { 98 | inplace_or_alloc_array(upper_bound_hint, |uninitialized_guard| { 99 | match uninitialized_guard.init_with_dyn_iter(iter) { 100 | Ok(mut guard) => consumer(&mut *guard), 101 | Err(mut vec) => consumer(&mut *vec), 102 | } 103 | }) 104 | } 105 | None => { 106 | let mut vec = iter.collect::>(); 107 | consumer(&mut *vec) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/fixed_array.rs: -------------------------------------------------------------------------------- 1 | use crate::guards::UninitializedSliceMemoryGuard; 2 | use core::mem::MaybeUninit; 3 | 4 | /// `try_inplace_array` trying to place an array of `T` on the stack and pass the guard of memory into the 5 | /// `consumer` closure. `consumer`'s result will be returned as `Ok(result)`. 6 | /// 7 | /// If the result of array of `T` is more than 4096 then `Err(consumer)` will be returned. 8 | /// 9 | /// Sometimes size of allocated array might be more than requested. For sizes larger than 32, 10 | /// the following formula is used: `roundUp(size/32)*32`. This is a simplification that used 11 | /// for keeping code short, simple and able to optimize. 12 | /// For example, for requested 50 item `[T; 64]` will be allocated. 13 | /// For 120 items - `[T; 128]` and so on. 14 | /// 15 | /// Note that rounding size up is working for fixed-sized arrays only. If function decides to 16 | /// allocate a vector then its size will be equal to requested. 17 | /// 18 | /// # Examples 19 | /// 20 | /// ```rust 21 | /// use inplace_it::{ 22 | /// try_inplace_array, 23 | /// UninitializedSliceMemoryGuard, 24 | /// }; 25 | /// 26 | /// let sum = try_inplace_array(100, |uninit_guard: UninitializedSliceMemoryGuard| { 27 | /// assert_eq!(uninit_guard.len(), 128); 28 | /// // For now, our memory is placed/allocated but uninitialized. 29 | /// // Let's initialize it! 30 | /// let guard = uninit_guard.init(|index| index as u16 * 2); 31 | /// // For now, memory contains content like [0, 2, 4, 6, ..., 252, 254] 32 | /// let sum: u16 = guard.iter().sum(); 33 | /// sum 34 | /// }); 35 | /// // Sum of [0, 2, 4, 6, ..., 252, 254] = sum of [0, 1, 2, 3, ..., 126, 127] * 2 = ( 127 * (127+1) ) / 2 * 2 36 | /// match sum { 37 | /// Ok(sum) => assert_eq!(sum, 127 * 128), 38 | /// Err(_) => unreachable!("Placing fails"), 39 | /// }; 40 | /// ``` 41 | pub fn try_inplace_array(size: usize, consumer: Consumer) -> Result 42 | where Consumer: FnOnce(UninitializedSliceMemoryGuard) -> R 43 | { 44 | macro_rules! inplace { 45 | ($size: expr) => {unsafe { 46 | indirect(move || { 47 | let mut memory: [MaybeUninit; $size] = MaybeUninit::uninit().assume_init(); 48 | consumer(UninitializedSliceMemoryGuard::new(&mut memory)) 49 | }) 50 | }}; 51 | } 52 | #[cfg(target_pointer_width = "8")] 53 | let result = match size { 54 | 0 => inplace!(0), 55 | 1 => inplace!(1), 56 | 2 => inplace!(2), 57 | 3 => inplace!(3), 58 | 4 => inplace!(4), 59 | 5 => inplace!(5), 60 | 6 => inplace!(6), 61 | 7 => inplace!(7), 62 | 8 => inplace!(8), 63 | 9 => inplace!(9), 64 | 10 => inplace!(10), 65 | 11 => inplace!(11), 66 | 12 => inplace!(12), 67 | 13 => inplace!(13), 68 | 14 => inplace!(14), 69 | 15 => inplace!(15), 70 | 16 => inplace!(16), 71 | 17 => inplace!(17), 72 | 18 => inplace!(18), 73 | 19 => inplace!(19), 74 | 20 => inplace!(20), 75 | 21 => inplace!(21), 76 | 22 => inplace!(22), 77 | 23 => inplace!(23), 78 | 24 => inplace!(24), 79 | 25 => inplace!(25), 80 | 26 => inplace!(26), 81 | 27 => inplace!(27), 82 | 28 => inplace!(28), 83 | 29 => inplace!(29), 84 | 30 => inplace!(30), 85 | 31 => inplace!(31), 86 | 32 => inplace!(32), 87 | 33..=64 => inplace!(64), 88 | 65..=96 => inplace!(96), 89 | 97..=127 => inplace!(127), 90 | _ => return Err(consumer), 91 | }; 92 | #[cfg(not(target_pointer_width = "8"))] 93 | let result = match size { 94 | 0 => inplace!(0), 95 | 1 => inplace!(1), 96 | 2 => inplace!(2), 97 | 3 => inplace!(3), 98 | 4 => inplace!(4), 99 | 5 => inplace!(5), 100 | 6 => inplace!(6), 101 | 7 => inplace!(7), 102 | 8 => inplace!(8), 103 | 9 => inplace!(9), 104 | 10 => inplace!(10), 105 | 11 => inplace!(11), 106 | 12 => inplace!(12), 107 | 13 => inplace!(13), 108 | 14 => inplace!(14), 109 | 15 => inplace!(15), 110 | 16 => inplace!(16), 111 | 17 => inplace!(17), 112 | 18 => inplace!(18), 113 | 19 => inplace!(19), 114 | 20 => inplace!(20), 115 | 21 => inplace!(21), 116 | 22 => inplace!(22), 117 | 23 => inplace!(23), 118 | 24 => inplace!(24), 119 | 25 => inplace!(25), 120 | 26 => inplace!(26), 121 | 27 => inplace!(27), 122 | 28 => inplace!(28), 123 | 29 => inplace!(29), 124 | 30 => inplace!(30), 125 | 31 => inplace!(31), 126 | 32 => inplace!(32), 127 | 33..=64 => inplace!(64), 128 | 65..=96 => inplace!(96), 129 | 97..=128 => inplace!(128), 130 | 129..=160 => inplace!(160), 131 | 161..=192 => inplace!(192), 132 | 193..=224 => inplace!(224), 133 | 225..=256 => inplace!(256), 134 | 257..=288 => inplace!(288), 135 | 289..=320 => inplace!(320), 136 | 321..=352 => inplace!(352), 137 | 353..=384 => inplace!(384), 138 | 385..=416 => inplace!(416), 139 | 417..=448 => inplace!(448), 140 | 449..=480 => inplace!(480), 141 | 481..=512 => inplace!(512), 142 | 513..=544 => inplace!(544), 143 | 545..=576 => inplace!(576), 144 | 577..=608 => inplace!(608), 145 | 609..=640 => inplace!(640), 146 | 641..=672 => inplace!(672), 147 | 673..=704 => inplace!(704), 148 | 705..=736 => inplace!(736), 149 | 737..=768 => inplace!(768), 150 | 769..=800 => inplace!(800), 151 | 801..=832 => inplace!(832), 152 | 833..=864 => inplace!(864), 153 | 865..=896 => inplace!(896), 154 | 897..=928 => inplace!(928), 155 | 929..=960 => inplace!(960), 156 | 961..=992 => inplace!(992), 157 | 993..=1024 => inplace!(1024), 158 | 1025..=1056 => inplace!(1056), 159 | 1057..=1088 => inplace!(1088), 160 | 1089..=1120 => inplace!(1120), 161 | 1121..=1152 => inplace!(1152), 162 | 1153..=1184 => inplace!(1184), 163 | 1185..=1216 => inplace!(1216), 164 | 1217..=1248 => inplace!(1248), 165 | 1249..=1280 => inplace!(1280), 166 | 1281..=1312 => inplace!(1312), 167 | 1313..=1344 => inplace!(1344), 168 | 1345..=1376 => inplace!(1376), 169 | 1377..=1408 => inplace!(1408), 170 | 1409..=1440 => inplace!(1440), 171 | 1441..=1472 => inplace!(1472), 172 | 1473..=1504 => inplace!(1504), 173 | 1505..=1536 => inplace!(1536), 174 | 1537..=1568 => inplace!(1568), 175 | 1569..=1600 => inplace!(1600), 176 | 1601..=1632 => inplace!(1632), 177 | 1633..=1664 => inplace!(1664), 178 | 1665..=1696 => inplace!(1696), 179 | 1697..=1728 => inplace!(1728), 180 | 1729..=1760 => inplace!(1760), 181 | 1761..=1792 => inplace!(1792), 182 | 1793..=1824 => inplace!(1824), 183 | 1825..=1856 => inplace!(1856), 184 | 1857..=1888 => inplace!(1888), 185 | 1889..=1920 => inplace!(1920), 186 | 1921..=1952 => inplace!(1952), 187 | 1953..=1984 => inplace!(1984), 188 | 1985..=2016 => inplace!(2016), 189 | 2017..=2048 => inplace!(2048), 190 | 2049..=2080 => inplace!(2080), 191 | 2081..=2112 => inplace!(2112), 192 | 2113..=2144 => inplace!(2144), 193 | 2145..=2176 => inplace!(2176), 194 | 2177..=2208 => inplace!(2208), 195 | 2209..=2240 => inplace!(2240), 196 | 2241..=2272 => inplace!(2272), 197 | 2273..=2304 => inplace!(2304), 198 | 2305..=2336 => inplace!(2336), 199 | 2337..=2368 => inplace!(2368), 200 | 2369..=2400 => inplace!(2400), 201 | 2401..=2432 => inplace!(2432), 202 | 2433..=2464 => inplace!(2464), 203 | 2465..=2496 => inplace!(2496), 204 | 2497..=2528 => inplace!(2528), 205 | 2529..=2560 => inplace!(2560), 206 | 2561..=2592 => inplace!(2592), 207 | 2593..=2624 => inplace!(2624), 208 | 2625..=2656 => inplace!(2656), 209 | 2657..=2688 => inplace!(2688), 210 | 2689..=2720 => inplace!(2720), 211 | 2721..=2752 => inplace!(2752), 212 | 2753..=2784 => inplace!(2784), 213 | 2785..=2816 => inplace!(2816), 214 | 2817..=2848 => inplace!(2848), 215 | 2849..=2880 => inplace!(2880), 216 | 2881..=2912 => inplace!(2912), 217 | 2913..=2944 => inplace!(2944), 218 | 2945..=2976 => inplace!(2976), 219 | 2977..=3008 => inplace!(3008), 220 | 3009..=3040 => inplace!(3040), 221 | 3041..=3072 => inplace!(3072), 222 | 3073..=3104 => inplace!(3104), 223 | 3105..=3136 => inplace!(3136), 224 | 3137..=3168 => inplace!(3168), 225 | 3169..=3200 => inplace!(3200), 226 | 3201..=3232 => inplace!(3232), 227 | 3233..=3264 => inplace!(3264), 228 | 3265..=3296 => inplace!(3296), 229 | 3297..=3328 => inplace!(3328), 230 | 3329..=3360 => inplace!(3360), 231 | 3361..=3392 => inplace!(3392), 232 | 3393..=3424 => inplace!(3424), 233 | 3425..=3456 => inplace!(3456), 234 | 3457..=3488 => inplace!(3488), 235 | 3489..=3520 => inplace!(3520), 236 | 3521..=3552 => inplace!(3552), 237 | 3553..=3584 => inplace!(3584), 238 | 3585..=3616 => inplace!(3616), 239 | 3617..=3648 => inplace!(3648), 240 | 3649..=3680 => inplace!(3680), 241 | 3681..=3712 => inplace!(3712), 242 | 3713..=3744 => inplace!(3744), 243 | 3745..=3776 => inplace!(3776), 244 | 3777..=3808 => inplace!(3808), 245 | 3809..=3840 => inplace!(3840), 246 | 3841..=3872 => inplace!(3872), 247 | 3873..=3904 => inplace!(3904), 248 | 3905..=3936 => inplace!(3936), 249 | 3937..=3968 => inplace!(3968), 250 | 3969..=4000 => inplace!(4000), 251 | 4001..=4032 => inplace!(4032), 252 | 4033..=4064 => inplace!(4064), 253 | 4065..=4096 => inplace!(4096), 254 | _ => return Err(consumer), 255 | }; 256 | Ok(result) 257 | } 258 | 259 | #[inline(never)] 260 | fn indirect(fun: impl FnOnce() -> R) -> R { 261 | fun() 262 | } 263 | -------------------------------------------------------------------------------- /src/guards/mod.rs: -------------------------------------------------------------------------------- 1 | mod uninitialized_slice_memory_guard; 2 | mod slice_memory_guard; 3 | 4 | pub use uninitialized_slice_memory_guard::*; 5 | pub use slice_memory_guard::*; 6 | -------------------------------------------------------------------------------- /src/guards/slice_memory_guard.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ops::{Deref, DerefMut}, 3 | mem::{MaybeUninit, transmute}, 4 | ptr::{drop_in_place, write, copy_nonoverlapping}, 5 | }; 6 | use alloc::vec::Vec; 7 | 8 | /// Guard-struct used for correctly initialize uninitialized memory and `drop` it when guard goes out of scope. 9 | /// Usually, you *should not* use this struct to handle your memory. 10 | /// 11 | /// ### Safety 12 | /// 13 | /// If you use this struct manually, remember: `&mut [MaybeUninit]`'s content will be overwriten while initialization. 14 | /// So it is *not safe* to apply this struct to already initialized data and it can lead to *memory leaks*. 15 | /// 16 | /// ### Example 17 | /// ```rust 18 | /// use inplace_it::SliceMemoryGuard; 19 | /// use core::mem::MaybeUninit; 20 | /// 21 | /// // Placing uninitialized memory 22 | /// let mut memory: [MaybeUninit; 100] = unsafe { MaybeUninit::uninit().assume_init() }; 23 | /// // Initializing guard 24 | /// let mut memory_guard = unsafe { 25 | /// SliceMemoryGuard::new( 26 | /// // Borrowing memory 27 | /// &mut memory, 28 | /// // Forwarding initializer 29 | /// |index| index * 2 30 | /// ) 31 | /// }; 32 | /// 33 | /// // For now, memory contains content like [0, 2, 4, 6, ..., 196, 198] 34 | /// 35 | /// // Using memory 36 | /// // Sum of [0, 2, 4, 6, ..., 196, 198] = sum of [0, 1, 2, 3, ..., 98, 99] * 2 = ( 99 * (99+1) ) / 2 * 2 37 | /// let sum: usize = memory_guard.iter().sum(); 38 | /// assert_eq!(sum, 99 * 100); 39 | /// 40 | /// ``` 41 | pub struct SliceMemoryGuard<'a, T> { 42 | memory: &'a mut [MaybeUninit], 43 | } 44 | 45 | impl<'a, T> SliceMemoryGuard<'a, T> { 46 | /// Initialize memory guard 47 | #[inline] 48 | pub unsafe fn new(memory: &'a mut [MaybeUninit], mut init: impl FnMut(usize) -> T) -> Self { 49 | for (index, item) in memory.into_iter().enumerate() { 50 | write(item.as_mut_ptr(), init(index)); 51 | } 52 | SliceMemoryGuard { memory } 53 | } 54 | 55 | /// Initialize memory guard using given iterator. 56 | /// Automatically shrink's memory to given items' count. 57 | /// `Ok(guard)` will be returned in this case. 58 | /// 59 | /// If items' count is too large to place in memory, moves it into new `Vec` and continue collecting into it. 60 | /// `Err(vec)` will be returned in this case. 61 | #[inline] 62 | pub unsafe fn new_from_iter(memory: &'a mut [MaybeUninit], mut iter: impl Iterator) -> Result> { 63 | // Fulfilling placed memory 64 | for (index, item) in memory.into_iter().enumerate() { 65 | match iter.next() { 66 | // While iterator returns new value, write it 67 | Some(value) => { 68 | write(item.as_mut_ptr(), value); 69 | } 70 | // When it returns None then slicing memory and returning the guard 71 | None => { 72 | return Ok(SliceMemoryGuard { 73 | memory: &mut memory[0..index], 74 | }); 75 | } 76 | } 77 | } 78 | 79 | if let Some(next_item) = iter.next() { 80 | // If iterator still contains values to return, collect it into the vector 81 | let mut vec = Vec::::with_capacity( 82 | // We cannot trust the `size_hint` anymore 83 | memory.len() + 1 84 | ); 85 | 86 | // First, copying already fulfilled memory into the heap 87 | vec.set_len(memory.len()); 88 | copy_nonoverlapping(memory.as_mut_ptr() as *mut T, vec.as_mut_ptr(), memory.len()); 89 | 90 | // Then, append it with the rest iterator's items 91 | vec.push(next_item); 92 | vec.extend(iter); 93 | 94 | Err(vec) 95 | } else { 96 | // If iterator is done after fulfilling all available memory, just return the guard 97 | Ok(SliceMemoryGuard { memory }) 98 | } 99 | } 100 | } 101 | 102 | impl<'a, T> Deref for SliceMemoryGuard<'a, T> { 103 | type Target = [T]; 104 | 105 | #[inline] 106 | fn deref(&self) -> &Self::Target { 107 | unsafe { transmute::<&[MaybeUninit], &[T]>(&self.memory) } 108 | } 109 | } 110 | 111 | impl<'a, T> DerefMut for SliceMemoryGuard<'a, T> { 112 | #[inline] 113 | fn deref_mut(&mut self) -> &mut Self::Target { 114 | unsafe { transmute::<&mut [MaybeUninit], &mut [T]>(&mut self.memory) } 115 | } 116 | } 117 | 118 | impl<'a, T> Drop for SliceMemoryGuard<'a, T> { 119 | #[inline] 120 | fn drop(&mut self) { 121 | for item in self.memory.into_iter() { 122 | unsafe { drop_in_place(item.as_mut_ptr()); } 123 | } 124 | } 125 | } 126 | 127 | #[cfg(test)] 128 | mod tests { 129 | use super::*; 130 | 131 | #[test] 132 | fn new_from_iter_uses_exactly_same_count_that_collects() { 133 | let mut memory: [MaybeUninit; 128] = unsafe { MaybeUninit::uninit().assume_init() }; 134 | 135 | // 0 and 128 cases should work also fine 136 | for count in 0..128 { 137 | let result = unsafe { SliceMemoryGuard::new_from_iter(&mut memory, 0..count) }; 138 | assert!(result.is_ok()); 139 | let guard = result.unwrap(); 140 | assert_eq!(guard.len(), count); 141 | assert!(guard.iter().cloned().eq(0..count)); 142 | } 143 | 144 | // cases when Vec should be returned 145 | for count in [129, 200, 512].iter().cloned() { 146 | let result = unsafe { SliceMemoryGuard::new_from_iter(&mut memory, 0..count) }; 147 | assert!(result.is_err()); 148 | let vec = result.err().unwrap(); 149 | assert_eq!(vec.len(), count); 150 | assert_eq!(vec, (0..count).collect::>()); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/guards/uninitialized_slice_memory_guard.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | mem::MaybeUninit, 3 | ops::{ 4 | RangeBounds, 5 | Bound, 6 | }, 7 | }; 8 | use alloc::vec::Vec; 9 | use crate::guards::SliceMemoryGuard; 10 | 11 | /// Guard-struct used to own uninitialized memory and provide functions for initializing it. 12 | /// Usually, you *should not* use this struct to handle your memory. 13 | /// 14 | /// Initializing functions spawns [SliceMemoryGuard] which will provide access to initialized memory. 15 | /// It also means memory can be used again after [SliceMemoryGuard] is dropped. 16 | /// 17 | /// ### Safety 18 | /// 19 | /// If you use this struct manually, remember: `&mut [MaybeUninit]`'s content will be overwriten while initialization. 20 | /// So it is *not safe* to apply this struct to already initialized data and it can lead to *memory leaks*. 21 | /// 22 | /// ### Example 23 | /// ```rust 24 | /// use inplace_it::UninitializedSliceMemoryGuard; 25 | /// use core::mem::MaybeUninit; 26 | /// 27 | /// // Placing uninitialized memory 28 | /// let mut memory: [MaybeUninit; 100] = unsafe { MaybeUninit::uninit().assume_init() }; 29 | /// // Initializing guard 30 | /// let mut uninit_memory_guard = unsafe { UninitializedSliceMemoryGuard::new(&mut memory) }; 31 | /// 32 | /// { 33 | /// // Initializing memory 34 | /// let mut memory_guard = uninit_memory_guard 35 | /// // we need to call .borrow() because out init-API consumes uninit-guard 36 | /// .borrow() 37 | /// // then passing initialize closure and the guard is ok 38 | /// .init(|index| index * 2); 39 | /// // For now, memory contains content like [0, 2, 4, 6, ..., 196, 198] 40 | /// 41 | /// // Using memory 42 | /// // Sum of [0, 2, 4, 6, ..., 196, 198] = sum of [0, 1, 2, 3, ..., 98, 99] * 2 = ( 99 * (99+1) ) / 2 * 2 43 | /// let sum: usize = memory_guard.iter().sum(); 44 | /// assert_eq!(sum, 99 * 100); 45 | /// // memory_guard dropped here 46 | /// } 47 | /// 48 | /// // uninit_memory_guard is available again now 49 | /// 50 | /// { 51 | /// // Initializing memory 52 | /// let mut memory_guard = uninit_memory_guard.init(|index| index * index); 53 | /// // For now, memory contains content like [0, 1, 4, 9, ..., 9604, 9801] 54 | /// 55 | /// // Using memory 56 | /// // Sum of [0, 1, 4, 9, ..., 9604, 9801] = 99 * (99 + 1) * (2 * 99 + 1) / 6 57 | /// let sum: usize = memory_guard.iter().sum(); 58 | /// assert_eq!(sum, 99 * (99 + 1) * (2 * 99 + 1) / 6); 59 | /// // memory_guard dropped here 60 | /// } 61 | /// 62 | /// ``` 63 | /// 64 | /// [SliceMemoryGuard]: struct.SliceMemoryGuard.html 65 | pub struct UninitializedSliceMemoryGuard<'a, T> { 66 | memory: &'a mut [MaybeUninit], 67 | } 68 | 69 | impl<'a, T> UninitializedSliceMemoryGuard<'a, T> { 70 | /// Initialize memory guard 71 | #[inline] 72 | pub unsafe fn new(memory: &'a mut [MaybeUninit]) -> Self { 73 | Self { memory } 74 | } 75 | 76 | /// Get the length of memory slice 77 | #[inline] 78 | pub fn len(&self) -> usize { 79 | self.memory.len() 80 | } 81 | 82 | /// Construct new memory guard with new bounds. 83 | /// 84 | /// Can be used to shrink memory. 85 | /// 86 | /// ### Panics 87 | /// 88 | /// Panic can be reached when given `range` is out of memory's range. 89 | #[inline] 90 | pub fn slice(self, range: impl RangeBounds) -> Self { 91 | let start = match range.start_bound() { 92 | Bound::Excluded(n) => n.saturating_add(1), 93 | Bound::Included(n) => *n, 94 | Bound::Unbounded => 0, 95 | }; 96 | let end = match range.end_bound() { 97 | Bound::Excluded(n) => *n, 98 | Bound::Included(n) => n.saturating_add(1), 99 | Bound::Unbounded => self.memory.len(), 100 | }; 101 | Self { 102 | memory: &mut self.memory[start..end], 103 | } 104 | } 105 | 106 | /// Initialize memory and make new guard of initialized memory. 107 | /// Given `init` closure will be used to initialize elements of memory slice. 108 | #[inline] 109 | pub fn init(self, init: impl FnMut(usize) -> T) -> SliceMemoryGuard<'a, T> { 110 | unsafe { 111 | SliceMemoryGuard::new(self.memory, init) 112 | } 113 | } 114 | 115 | /// Initialize memory and make new guard of initialized memory. 116 | /// Given `source` slice will be used to initialize elements of memory slice. 117 | /// Returned guard will contain sliced memory to `source`'s length. 118 | /// 119 | /// ### Panics 120 | /// 121 | /// Panic can be reached when given `source`'s range is out of memory's range. 122 | #[inline] 123 | pub fn init_copy_of(self, source: &[T]) -> SliceMemoryGuard<'a, T> 124 | where T: Clone 125 | { 126 | self.slice(..source.len()).init(|index| { source[index].clone() }) 127 | } 128 | 129 | /// Initialize memory and make new guard of initialized memory. 130 | /// Given `iter` exact-size iterator will be used to initialize elements of memory slice. 131 | /// Returned guard will contain sliced memory to `iter`'s length. 132 | /// 133 | /// ### Panics 134 | /// 135 | /// Panic can be reached when given `iter`'s length is out of memory's range. 136 | #[inline] 137 | pub fn init_with_iter(self, mut iter: impl ExactSizeIterator) -> SliceMemoryGuard<'a, T> { 138 | self.slice(..iter.len()).init(|_index| { iter.next().unwrap() }) 139 | } 140 | 141 | /// Initialize memory guard using given iterator. 142 | /// Automatically shrink's memory to given items' count. 143 | /// `Ok(guard)` will be returned in this case. 144 | /// 145 | /// If items' count is too large to place in memory, moves it into new `Vec` and continue collecting into it. 146 | /// `Err(vec)` will be returned in this case. 147 | #[inline] 148 | pub fn init_with_dyn_iter(self, iter: impl Iterator) -> Result, Vec> { 149 | unsafe { 150 | SliceMemoryGuard::new_from_iter(self.memory, iter) 151 | } 152 | } 153 | 154 | /// Create new uninit memory guard with less or equal lifetime to original guard's lifetime. 155 | /// This function should be used to reuse memory because init-API consumes the guard. 156 | #[inline] 157 | pub fn borrow(&mut self) -> UninitializedSliceMemoryGuard { 158 | unsafe { 159 | UninitializedSliceMemoryGuard::new(self.memory) 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Inplace it! 2 | //! 3 | //! Place small arrays on the stack with a low cost! 4 | //! 5 | //! The only price you should pay for this is the price of choosing 6 | //! a type based on the size of the requested array! This is just one `match`! 7 | //! 8 | //! ## What? 9 | //! 10 | //! This crate is created for one purpose: allocating small arrays on the stack. 11 | //! The simplest way to use it is: 12 | //! 13 | //! ```rust 14 | //! use inplace_it::{inplace_or_alloc_array, UninitializedSliceMemoryGuard}; 15 | //! 16 | //! inplace_or_alloc_array( 17 | //! 150, // size of needed array to allocate 18 | //! |mut uninit_guard: UninitializedSliceMemoryGuard| { // and this is consumer of uninitialized memory 19 | //! 20 | //! // Size of stack-allocated memory can be more or equal to requested, but never less. 21 | //! assert_eq!(160, uninit_guard.len()); 22 | //! 23 | //! { 24 | //! // You can borrow guard to reuse memory 25 | //! let borrowed_uninit_guard = uninit_guard.borrow(); 26 | //! // Let's initialize memory 27 | //! // Note that borrowed_uninit_guard will be consumed (destroyed to produce initialized memory guard) 28 | //! let init_guard = borrowed_uninit_guard.init(|index| index as u16 + 1); 29 | //! // Memory now contains elements [1, 2, ..., 160] 30 | //! // Lets check it. Sum of [1, 2, ..., 160] = 12880 31 | //! let sum: u16 = init_guard.iter().sum(); 32 | //! assert_eq!(sum, 12880); 33 | //! } 34 | //! 35 | //! { 36 | //! // If you don't want to reuse memory, you can init new guard directly 37 | //! let init_guard = uninit_guard.init(|index| index as u16 * 2); 38 | //! // Memory now contains elements [0, 2, 4, ..., 318] 39 | //! // Lets check it. Sum of [0, 2, 4, ..., 318] = 25440 40 | //! let sum: u16 = init_guard.iter().sum(); 41 | //! assert_eq!(sum, 25440); 42 | //! } 43 | //! } 44 | //! ) 45 | //! ``` 46 | //! 47 | //! ## Why? 48 | //! 49 | //! Because allocation on the stack (i.e. placing variables) is **MUCH FASTER** then usual 50 | //! allocating in the heap. 51 | //! 52 | 53 | #![no_std] 54 | 55 | extern crate alloc; 56 | 57 | mod guards; 58 | mod fixed_array; 59 | mod alloc_array; 60 | 61 | pub use guards::*; 62 | pub use fixed_array::*; 63 | pub use alloc_array::*; 64 | -------------------------------------------------------------------------------- /tests/alloc_array.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn inplace_or_alloc_from_iter_works_fine() { 3 | // Don't forget to update 4096 and related constants 4 | 5 | // 0 and 4096 cases should work also fine 6 | for count in (0..8192).step_by(128) { 7 | let result = ::inplace_it::inplace_or_alloc_from_iter(0..count, |mem| { 8 | assert_eq!(mem.len(), count); 9 | assert!(mem.iter().cloned().eq(0..count)); 10 | return mem.len() * 2; 11 | }); 12 | assert_eq!(result, count * 2); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/drop_correctness.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use inplace_it::*; 3 | use std::mem::MaybeUninit; 4 | 5 | struct DropCounter { 6 | count: Cell, 7 | } 8 | 9 | impl DropCounter { 10 | fn with_current R, R>(f: F) -> R { 11 | thread_local!( 12 | static COUNTER: DropCounter = DropCounter {count: Cell::new(0)}; 13 | ); 14 | COUNTER.with(f) 15 | } 16 | 17 | #[inline] 18 | fn get() -> usize { 19 | DropCounter::with_current(|c| c.count.get()) 20 | } 21 | 22 | #[inline] 23 | fn inc() { 24 | DropCounter::with_current(|c| c.count.set(c.count.get() + 1)); 25 | } 26 | 27 | #[inline] 28 | fn clear() { 29 | DropCounter::with_current(|c| c.count.set(0)); 30 | } 31 | } 32 | 33 | struct DropCounterTrigger(u8 /* One byte to avoid zero-sized types optimizations */); 34 | 35 | impl DropCounterTrigger { 36 | fn new() -> Self { 37 | Self(228) 38 | } 39 | } 40 | 41 | impl Drop for DropCounterTrigger { 42 | #[inline] 43 | fn drop(&mut self) { 44 | DropCounter::inc(); 45 | } 46 | } 47 | 48 | #[test] 49 | fn maybe_uninit_works_as_expected() { 50 | DropCounter::clear(); 51 | drop(MaybeUninit::::uninit()); 52 | assert_eq!(DropCounter::get(), 0); 53 | DropCounter::clear(); 54 | drop(MaybeUninit::::new(DropCounterTrigger::new())); 55 | assert_eq!(DropCounter::get(), 0); 56 | DropCounter::clear(); 57 | let mut memory = MaybeUninit::::new(DropCounterTrigger::new()); 58 | unsafe { core::ptr::drop_in_place(memory.as_mut_ptr()); } 59 | drop(memory); 60 | assert_eq!(DropCounter::get(), 1); 61 | } 62 | 63 | #[test] 64 | fn inplace_array_should_correctly_drop_values() { 65 | for i in (0..4096).step_by(8) { 66 | DropCounter::clear(); 67 | try_inplace_array(i, |guard: UninitializedSliceMemoryGuard| { 68 | assert!(guard.len() >= i); 69 | }).map_err(|_| format!("Cannot inplace array of {} size", i)).unwrap(); 70 | assert_eq!(DropCounter::get(), 0); 71 | DropCounter::clear(); 72 | let len = try_inplace_array(i, |guard| { 73 | let len = guard.len(); 74 | guard.init(|_| DropCounterTrigger::new()); 75 | len 76 | }).map_err(|_| format!("Cannot inplace array of {} size", i)).unwrap(); 77 | assert_eq!(DropCounter::get(), len); 78 | DropCounter::clear(); 79 | try_inplace_array(i, |guard| { 80 | let guard = guard.slice(..i); 81 | assert_eq!(guard.len(), i); 82 | guard.init(|_| DropCounterTrigger::new()); 83 | }).map_err(|_| format!("Cannot inplace array of {} size", i)).unwrap(); 84 | assert_eq!(DropCounter::get(), i); 85 | } 86 | } 87 | 88 | #[test] 89 | fn alloc_array_should_correctly_drop_values() { 90 | for i in (0..4096).step_by(8) { 91 | DropCounter::clear(); 92 | alloc_array(i, |_guard: UninitializedSliceMemoryGuard| {}); 93 | assert_eq!(DropCounter::get(), 0); 94 | DropCounter::clear(); 95 | alloc_array(i, |guard| { 96 | guard.init(|_| DropCounterTrigger::new()); 97 | }); 98 | assert_eq!(DropCounter::get(), i); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/stackalloc_correctness.rs: -------------------------------------------------------------------------------- 1 | use inplace_it::*; 2 | use std::cmp::Ordering; 3 | 4 | #[inline(never)] 5 | fn get_stack_pointer_value() -> usize { 6 | use std::mem::transmute; 7 | 8 | let begin_of_function_stack: usize = 0; 9 | unsafe { 10 | transmute::<&usize, usize>(&begin_of_function_stack) 11 | } 12 | } 13 | 14 | fn calculate_stack_consumption(begin: usize, end: usize) -> usize { 15 | if begin >= end { 16 | begin - end 17 | } else { 18 | end - begin 19 | } 20 | } 21 | 22 | /// This test measures stack memory consumption from 0 to 4096 items by step 32 23 | /// Then, it calculates "tangent of an angle" (y/x) from "point of zero" (0 items and it's stack size). 24 | /// 25 | /// It bad cases, when compiler optimizes `try_inplace_array` function so that it doesn't make sense, 26 | /// difference between lowest and highest borders is huge. 27 | /// 28 | /// In good cases, all "points" are form almost a line so all "tangents of an angle" do not have much difference among themselves. 29 | /// And the difference of the borders is small. It's about 0 <= D <= 2. 30 | /// Also, "tangents of an angle" about 0 < T <= 3. In bad cases tangents are VERY HUGE. 31 | /// 32 | /// This is what that test checks. 33 | #[test] 34 | fn stack_memory_should_allocate_no_more_than_needed() { 35 | #[inline(never)] 36 | fn inplace_and_sum(size: usize) -> usize { 37 | let begin = get_stack_pointer_value(); 38 | let result = try_inplace_array(size, |mem: UninitializedSliceMemoryGuard| { 39 | let end = get_stack_pointer_value(); 40 | let mem = mem.init(|i| i); 41 | let mut sum = 0usize; 42 | for i in mem.iter() { 43 | sum += *i as usize; 44 | } 45 | // To sure sum operation was not optimized to no-op 46 | let len = mem.len(); 47 | assert_eq!(sum, if len > 0 { 48 | len * (len - 1) / 2 49 | } else { 50 | 0 51 | }); 52 | let stack_consumption = calculate_stack_consumption(begin, end); 53 | stack_consumption 54 | }); 55 | match result { 56 | Ok(result) => result, 57 | Err(_) => panic!("Inplace should never fail is this test"), 58 | } 59 | } 60 | 61 | fn calc_differential_coefficients(mut data: impl Iterator) -> impl Iterator { 62 | let (zero_x, zero_y) = data.next() 63 | .expect("Input iterator should not be empty"); 64 | 65 | data.map(move |(x, y)| { 66 | let dx = (x - zero_x) as f64; 67 | let dy = (y - zero_y) as f64; 68 | dy / dx 69 | }) 70 | } 71 | 72 | fn calc_differentials_borders_dispersion(data: &[f64]) -> f64 { 73 | let min = data.iter().min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)) 74 | .expect("Input dataset should not be empty"); 75 | let max = data.iter().max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)) 76 | .expect("Input dataset should not be empty"); 77 | max - min 78 | } 79 | 80 | let stack_sizes = (0..=4096).step_by(32) 81 | .map(|length| { 82 | let stack_size = inplace_and_sum(length); 83 | const USIZE_LAYOUT: usize = std::mem::size_of::() + std::mem::align_of::(); // usize layout coefficient 84 | let stack_size = stack_size / USIZE_LAYOUT; 85 | (length, stack_size) 86 | }) 87 | .collect::>(); 88 | 89 | // dbg!(&stack_sizes); 90 | 91 | let stack_sizes_differentials = calc_differential_coefficients(stack_sizes.into_iter()) 92 | .collect::>(); 93 | 94 | for differential in &stack_sizes_differentials { 95 | assert!(*differential <= 3.0); 96 | } 97 | 98 | let differentials_borders_dispersion = calc_differentials_borders_dispersion(&stack_sizes_differentials); 99 | 100 | // dbg!(stack_sizes_differentials, differentials_borders_dispersion); 101 | 102 | assert!(differentials_borders_dispersion <= 2.0); 103 | } 104 | --------------------------------------------------------------------------------