├── .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 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Check.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
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 | [](https://crates.io/crates/inplace_it)
4 | [](https://github.com/NotIntMan/inplace_it/blob/master/LICENSE.txt)
5 | [](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 |
--------------------------------------------------------------------------------