├── .gitattributes ├── .github └── workflows │ ├── cargo.yml │ ├── pdd.yml │ ├── tarpaulin.yml │ ├── up.yml │ └── xcop.yml ├── .gitignore ├── .rultor.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── benches ├── push.rs └── vs_vec.rs ├── renovate.json └── src ├── clone.rs ├── ctors.rs ├── debug.rs ├── iterators.rs ├── lib.rs ├── serialization.rs └── stack.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Check out all text files in UNIX format, with LF as end of line 2 | # Don't change this file. If you have any ideas about it, please 3 | # submit a separate issue about it and we'll discuss. 4 | 5 | * text=auto eol=lf 6 | *.java ident 7 | *.xml ident 8 | *.png binary 9 | -------------------------------------------------------------------------------- /.github/workflows/cargo.yml: -------------------------------------------------------------------------------- 1 | name: cargo 2 | on: 3 | push: 4 | pull_request: 5 | concurrency: 6 | group: ${{ github.ref }} 7 | cancel-in-progress: true 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-java@v4 14 | with: 15 | distribution: 'temurin' 16 | java-version: 17 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | - run: cargo --color=never test --all-features -vv -- --nocapture 21 | - run: cargo --color=never test --release --all-features -vv -- --nocapture 22 | - run: cargo --color=never fmt --check 23 | - run: cargo --color=never doc --no-deps 24 | - run: cargo --color=never clippy -- --no-deps 25 | -------------------------------------------------------------------------------- /.github/workflows/pdd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: pdd 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | jobs: 11 | pdd: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: volodya-lombrozo/pdd-action@master 16 | -------------------------------------------------------------------------------- /.github/workflows/tarpaulin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: tarpaulin 3 | on: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | check: 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions-rs/toolchain@v1 13 | with: 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/tarpaulin@v0.1 17 | with: 18 | version: '0.22.0' 19 | args: '--all-features --exclude-files src/lib.rs -- --test-threads 1' 20 | - uses: codecov/codecov-action@v4 21 | with: 22 | fail_ci_if_error: true 23 | -------------------------------------------------------------------------------- /.github/workflows/up.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: up 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - '*' 9 | jobs: 10 | up: 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - uses: actions/checkout@v4 14 | - run: |- 15 | git fetch --tags --force && \ 16 | latest=$(git tag --sort=creatordate | tail -1) && \ 17 | sed -E -i "s/microstack = \"[^\"]+\"/microstack = \"${latest}\"/g" README.md 18 | - uses: peter-evans/create-pull-request@v5 19 | with: 20 | branch: version-up 21 | commit-message: 'new version in README' 22 | delete-branch: true 23 | title: 'New version in README' 24 | assignees: yegor256 25 | base: master -------------------------------------------------------------------------------- /.github/workflows/xcop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: xcop 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | jobs: 11 | xcop: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: g4s8/xcop-action@master 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.relf 4 | *.svg 5 | bin/ 6 | *.bak 7 | tmp/ -------------------------------------------------------------------------------- /.rultor.yml: -------------------------------------------------------------------------------- 1 | docker: 2 | image: yegor256/rultor-image:1.18.0 3 | assets: 4 | credentials: yegor256/home#assets/crates-credentials 5 | install: | 6 | pdd --file=/dev/null 7 | merge: 8 | script: | 9 | cargo --color=never test -vv 10 | cargo --color=never fmt --check 11 | cargo doc --no-deps 12 | cargo clippy 13 | release: 14 | script: |- 15 | [[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit -1 16 | sed -i -e "s/^version = \"0.0.0\"/version = \"${tag}\"/" Cargo.toml 17 | sed -i -e "s/0.0.0/${tag}/" src/lib.rs 18 | cargo --color=never test --all-features -vv -- --nocapture 19 | cargo --color=never test --release --all-features -vv -- --nocapture 20 | cargo --color=never fmt --check 21 | cargo --color=never clippy -- --no-deps 22 | git commit -am "${tag}" 23 | mkdir -p ~/.cargo && cp ../credentials ~/.cargo 24 | cargo --color=never publish 25 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bincode" 7 | version = "1.3.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 10 | dependencies = [ 11 | "serde", 12 | ] 13 | 14 | [[package]] 15 | name = "microstack" 16 | version = "0.0.0" 17 | dependencies = [ 18 | "bincode", 19 | "serde", 20 | ] 21 | 22 | [[package]] 23 | name = "proc-macro2" 24 | version = "1.0.66" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 27 | dependencies = [ 28 | "unicode-ident", 29 | ] 30 | 31 | [[package]] 32 | name = "quote" 33 | version = "1.0.33" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 36 | dependencies = [ 37 | "proc-macro2", 38 | ] 39 | 40 | [[package]] 41 | name = "serde" 42 | version = "1.0.193" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" 45 | dependencies = [ 46 | "serde_derive", 47 | ] 48 | 49 | [[package]] 50 | name = "serde_derive" 51 | version = "1.0.193" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" 54 | dependencies = [ 55 | "proc-macro2", 56 | "quote", 57 | "syn", 58 | ] 59 | 60 | [[package]] 61 | name = "syn" 62 | version = "2.0.29" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" 65 | dependencies = [ 66 | "proc-macro2", 67 | "quote", 68 | "unicode-ident", 69 | ] 70 | 71 | [[package]] 72 | name = "unicode-ident" 73 | version = "1.0.11" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 76 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "microstack" 3 | version = "0.0.0" 4 | edition = "2021" 5 | repository = "https://github.com/yegor256/microstack" 6 | description = "The simplest and the fastest implementation of a fixed-size stack on stack" 7 | readme = "README.md" 8 | license = "MIT" 9 | homepage = "https://github.com/yegor256/microstack" 10 | keywords = ["memory", "stack"] 11 | categories = ["data-structures", "memory-management"] 12 | 13 | [dependencies] 14 | serde = { version = "1.0.193", optional = true, default-features = false } 15 | 16 | [dev-dependencies] 17 | bincode = "1.3.3" 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Yegor Bugayenko 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![cargo](https://github.com/yegor256/microstack/actions/workflows/cargo.yml/badge.svg)](https://github.com/yegor256/microstack/actions/workflows/cargo.yml) 2 | [![crates.io](https://img.shields.io/crates/v/microstack.svg)](https://crates.io/crates/microstack) 3 | [![codecov](https://codecov.io/gh/yegor256/microstack/branch/master/graph/badge.svg)](https://codecov.io/gh/yegor256/microstack) 4 | [![Hits-of-Code](https://hitsofcode.com/github/yegor256/microstack)](https://hitsofcode.com/view/github/yegor256/microstack) 5 | ![Lines of code](https://img.shields.io/tokei/lines/github/yegor256/microstack) 6 | [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/microstack/blob/master/LICENSE.txt) 7 | [![docs.rs](https://img.shields.io/docsrs/microstack)](https://docs.rs/microstack/latest/microstack/) 8 | 9 | This is the simplest and the fastest (faster than [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)!) implementation of a 10 | last-in-first-out [stack data structure](https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29), 11 | on [stack](https://en.wikipedia.org/wiki/Call_stack), 12 | when stack elements are `Copy` implementing primitives. 13 | This is basically a wrapper around an [uninitialized](https://doc.rust-lang.org/nomicon/uninitialized.html) array. 14 | When it is created on stack, its elements contain no specific data. 15 | Then, when you `push_unchecked(x)`, the head of the stack is moved forward 16 | and `x` is placed into the element of the array. When you `pop_unchecked()`, 17 | the head is moved backward and the data is retrieved from the array. 18 | There are no boundary checks, that's why both `push_unchecked()` and `pop_unchecked()` may lead to undefined 19 | behavior. Use `push()` and `pop()`, which are safer, but slower. 20 | For even slower but even safer behavior, you can use `try_push()` and `try_pop()`. 21 | 22 | First, add this to `Cargo.toml`: 23 | 24 | ```toml 25 | [dependencies] 26 | microstack = "0.0.7" 27 | ``` 28 | 29 | Then, use it like this (mind the `unsafe` blocks, they give the fastest performance, 30 | but [undefined behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) 31 | if you go over the stack boundaries): 32 | 33 | ```rust 34 | use microstack::Stack; 35 | let mut s : Stack<&str, 10> = Stack::new(); // allocation on stack 36 | unsafe { s.push_unchecked("foo") }; // no boundary checks here 37 | unsafe { s.push_unchecked("bar") }; // and here 38 | assert_eq!("bar", unsafe { s.pop_unchecked() }); // and here again 39 | assert_eq!(1, s.len()); 40 | ``` 41 | 42 | Pay attention, here the stack is created with an extra 43 | [const generic argument](https://practice.rs/generics-traits/const-generics.html) equal to `10`. This is 44 | the total size of the stack data structure, which is allocated on stack when `::new()` is called. 45 | 46 | Read [the API documentation](https://docs.rs/microstack/latest/microstack/). 47 | 48 | ## How to Contribute 49 | 50 | First, install [Rust](https://www.rust-lang.org/tools/install) and then: 51 | 52 | ```bash 53 | $ cargo test -vv 54 | ``` 55 | 56 | If everything goes well, fork repository, make changes, send us a [pull request](https://www.yegor256.com/2014/04/15/github-guidelines.html). 57 | We will review your changes and apply them to the `master` branch shortly, 58 | provided they don't violate our quality standards. To avoid frustration, 59 | before sending us your pull request please run `cargo test` again. Also, 60 | run `cargo fmt` and `cargo clippy`. 61 | 62 | Also, before you start making changes, run benchmarks: 63 | 64 | ```bash 65 | $ rustup run nightly cargo bench 66 | ``` 67 | 68 | Then, after the changes you make, run it again. Compare the results. If your changes 69 | degrade performance, think twice before submitting a pull request. 70 | -------------------------------------------------------------------------------- /benches/push.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #![feature(test)] 22 | 23 | extern crate test; 24 | use microstack::Stack; 25 | use test::Bencher; 26 | 27 | #[bench] 28 | fn push_different(b: &mut Bencher) { 29 | let mut s: Stack = Stack::new(); 30 | b.iter(|| { 31 | let cap = s.capacity(); 32 | for i in 0..cap { 33 | s.push_unchecked(i); 34 | } 35 | for _ in 0..cap { 36 | s.pop_unchecked(); 37 | } 38 | }); 39 | } 40 | 41 | #[bench] 42 | fn push_and_pop(b: &mut Bencher) { 43 | let mut s: Stack = Stack::new(); 44 | b.iter(|| { 45 | let cap = s.capacity(); 46 | for i in 0..cap { 47 | s.push_unchecked(i); 48 | s.pop_unchecked(); 49 | } 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /benches/vs_vec.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #![feature(test)] 22 | 23 | extern crate test; 24 | use microstack::Stack; 25 | use test::Bencher; 26 | 27 | const CAPACITY: usize = 4096; 28 | 29 | macro_rules! eval { 30 | ($s:expr) => {{ 31 | let cap = $s.capacity(); 32 | for i in 0..cap { 33 | $s.push(i); 34 | } 35 | $s.clone().clear(); 36 | for i in $s.clone().iter() { 37 | assert!(*i < cap); 38 | } 39 | for _ in 0..cap { 40 | $s.pop(); 41 | } 42 | $s.clear(); 43 | }}; 44 | } 45 | 46 | #[bench] 47 | fn stack_push_and_pop(b: &mut Bencher) { 48 | b.iter(|| { 49 | let mut s: Stack = Stack::new(); 50 | eval!(s); 51 | }); 52 | } 53 | 54 | #[bench] 55 | fn vec_push_and_pop(b: &mut Bencher) { 56 | b.iter(|| { 57 | let mut s: Vec = Vec::with_capacity(CAPACITY); 58 | eval!(s); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/clone.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | use crate::Stack; 22 | use std::ptr; 23 | 24 | impl Clone for Stack { 25 | /// Clone it. 26 | #[must_use] 27 | fn clone(&self) -> Self { 28 | let mut s: Self = Self::new(); 29 | s.next = self.next; 30 | unsafe { ptr::copy::(self.items.as_ptr(), s.items.as_mut_ptr(), s.next) }; 31 | s 32 | } 33 | } 34 | 35 | #[test] 36 | fn stack_can_be_cloned() { 37 | let mut s: Stack = Stack::new(); 38 | unsafe { s.push_unchecked(42) }; 39 | assert_eq!(42, s.clone().pop()); 40 | } 41 | 42 | #[test] 43 | fn full_stack_can_be_cloned() { 44 | let mut s: Stack = Stack::new(); 45 | for i in 0..s.capacity() { 46 | unsafe { s.push_unchecked(i) }; 47 | } 48 | assert_eq!(s.capacity() - 1, s.clone().pop()); 49 | } 50 | 51 | #[test] 52 | fn empty_stack_can_be_cloned() { 53 | let m: Stack = Stack::new(); 54 | assert!(m.clone().is_empty()); 55 | } 56 | -------------------------------------------------------------------------------- /src/ctors.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | use crate::Stack; 22 | use std::mem::MaybeUninit; 23 | 24 | impl Default for Stack { 25 | /// Make a default empty [`Stack`]. 26 | #[inline] 27 | #[must_use] 28 | fn default() -> Self { 29 | Self::new() 30 | } 31 | } 32 | 33 | impl Stack { 34 | /// Make it. 35 | /// 36 | /// The size of the stack is defined by the generic argument. 37 | #[inline] 38 | #[must_use] 39 | #[allow(clippy::uninit_assumed_init)] 40 | pub const fn new() -> Self { 41 | unsafe { 42 | Self { 43 | next: 0, 44 | items: MaybeUninit::<[V; N]>::uninit().assume_init(), 45 | } 46 | } 47 | } 48 | } 49 | 50 | #[test] 51 | fn makes_default_stack() { 52 | let s: Stack = Stack::default(); 53 | assert_eq!(0, s.len()); 54 | } 55 | 56 | #[test] 57 | fn makes_new_stack() { 58 | let s: Stack = Stack::new(); 59 | assert_eq!(0, s.len()); 60 | } 61 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | use crate::Stack; 22 | use std::fmt; 23 | use std::fmt::{Debug, Display, Formatter}; 24 | 25 | impl Display for Stack { 26 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 27 | <&Self as Debug>::fmt(&self, f) 28 | } 29 | } 30 | 31 | impl Debug for Stack { 32 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 33 | let mut parts = vec![]; 34 | for v in self.iter() { 35 | parts.push(format!("{v}")); 36 | } 37 | f.write_str(format!("[{}]", parts.join(", ").as_str()).as_str()) 38 | } 39 | } 40 | 41 | #[test] 42 | fn debugs_stack() { 43 | let mut s: Stack<&str, 10> = Stack::new(); 44 | unsafe { s.push_unchecked("one") }; 45 | unsafe { s.push_unchecked("two") }; 46 | assert_eq!("[one, two]", format!("{:?}", s)); 47 | } 48 | 49 | #[test] 50 | fn displays_stack() { 51 | let mut s: Stack<&str, 10> = Stack::new(); 52 | unsafe { s.push_unchecked("one") }; 53 | unsafe { s.push_unchecked("two") }; 54 | assert_eq!("[one, two]", format!("{}", s)); 55 | } 56 | -------------------------------------------------------------------------------- /src/iterators.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | use crate::{IntoIter, Iter, Stack}; 22 | use std::marker::PhantomData; 23 | 24 | impl Iterator for IntoIter { 25 | type Item = V; 26 | 27 | #[inline] 28 | #[must_use] 29 | fn next(&mut self) -> Option { 30 | if self.pos >= self.next { 31 | None 32 | } else { 33 | let v = unsafe { self.items.add(self.pos).read() }; 34 | self.pos += 1; 35 | Some(v) 36 | } 37 | } 38 | } 39 | 40 | impl<'a, V: Copy + 'a, const N: usize> Stack { 41 | /// Into-iterate them. 42 | #[inline] 43 | pub const fn into_iter(&self) -> IntoIter { 44 | IntoIter { 45 | pos: 0, 46 | next: self.next, 47 | items: self.items.as_ptr(), 48 | } 49 | } 50 | } 51 | 52 | impl<'a, V: Copy, const N: usize> Iterator for Iter<'a, V, N> { 53 | type Item = &'a V; 54 | 55 | #[inline] 56 | #[must_use] 57 | fn next(&mut self) -> Option { 58 | if self.pos >= self.next { 59 | None 60 | } else { 61 | unsafe { 62 | let v = self.items.add(self.pos); 63 | self.pos += 1; 64 | v.as_ref() 65 | } 66 | } 67 | } 68 | } 69 | 70 | impl<'a, V: Copy + 'a, const N: usize> Stack { 71 | /// Iterate them. 72 | #[inline] 73 | pub const fn iter(&self) -> Iter { 74 | Iter { 75 | pos: 0, 76 | next: self.next, 77 | items: self.items.as_ptr(), 78 | _marker: PhantomData, 79 | } 80 | } 81 | } 82 | 83 | #[test] 84 | fn push_and_iterate() { 85 | let mut p: Stack = Stack::new(); 86 | unsafe { p.push_unchecked(1) }; 87 | unsafe { p.push_unchecked(2) }; 88 | unsafe { p.push_unchecked(3) }; 89 | let mut sum = 0; 90 | for x in p.iter() { 91 | sum += x; 92 | } 93 | assert_eq!(6, sum); 94 | } 95 | 96 | #[test] 97 | fn push_and_into_iterate() { 98 | let mut p: Stack = Stack::new(); 99 | unsafe { p.push_unchecked(1) }; 100 | unsafe { p.push_unchecked(2) }; 101 | let mut sum = 0; 102 | for x in p.into_iter() { 103 | sum += x; 104 | } 105 | assert_eq!(3, sum); 106 | } 107 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This is a simplest and the fastest implementation of a stack on stack, 22 | //! when stack elements are `Copy` implementing primitives. 23 | //! 24 | //! For example, here is how a stack can be created: 25 | //! 26 | //! ``` 27 | //! use microstack::Stack; 28 | //! let mut s : Stack = Stack::new(); 29 | //! s.push(1); 30 | //! s.push(2); 31 | //! assert_eq!(2, s.pop()); 32 | //! assert_eq!(1, s.len()); 33 | //! ``` 34 | //! 35 | //! Creating a [`Stack`] requires knowing the maximum size of it, upfront. This is 36 | //! what the second type argument `10` is for, in the example above. The stack 37 | //! will have exactly ten elements. An attempt to add an 11th element will lead 38 | //! to a panic. 39 | 40 | #![doc(html_root_url = "https://docs.rs/microstack/0.0.0")] 41 | #![deny(warnings)] 42 | #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] 43 | #![allow(clippy::multiple_inherent_impl)] 44 | #![allow(clippy::multiple_crate_versions)] 45 | 46 | use std::marker::PhantomData; 47 | 48 | mod clone; 49 | mod ctors; 50 | mod debug; 51 | mod iterators; 52 | #[cfg(feature = "serde")] 53 | mod serialization; 54 | mod stack; 55 | 56 | /// This is a simplest and the fastest implementation of a stack on stack, 57 | /// when stack elements are `Copy` implementing primitives. 58 | /// 59 | /// For example, here is how a stack can be created: 60 | /// 61 | /// ``` 62 | /// use microstack::Stack; 63 | /// let mut s : Stack = Stack::new(); 64 | /// s.push(1); 65 | /// s.push(2); 66 | /// assert_eq!(2, s.pop()); 67 | /// ``` 68 | /// 69 | pub struct Stack { 70 | /// The next available position in the array. 71 | next: usize, 72 | /// The fixed-size array of values. 73 | items: [V; N], 74 | } 75 | 76 | /// Iterator. 77 | pub struct Iter<'a, V: Copy, const N: usize> { 78 | /// The position. 79 | pos: usize, 80 | /// The next available position in the array. 81 | next: usize, 82 | /// The fixed-size array of values. 83 | items: *const V, 84 | _marker: PhantomData<&'a V>, 85 | } 86 | 87 | /// Into-iterator. 88 | pub struct IntoIter { 89 | /// The position. 90 | pos: usize, 91 | /// The next available position in the array. 92 | next: usize, 93 | /// The fixed-size array of values. 94 | items: *const V, 95 | } 96 | -------------------------------------------------------------------------------- /src/serialization.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | use crate::Stack; 22 | use serde::de::{SeqAccess, Visitor}; 23 | use serde::ser::SerializeSeq; 24 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 25 | use std::fmt::Formatter; 26 | use std::marker::PhantomData; 27 | 28 | impl Serialize for Stack { 29 | fn serialize(&self, serializer: S) -> Result 30 | where 31 | S: Serializer, 32 | { 33 | let mut map = serializer.serialize_seq(Some(self.next))?; 34 | for v in self.iter() { 35 | map.serialize_element(&v)?; 36 | } 37 | map.end() 38 | } 39 | } 40 | 41 | struct Vi(PhantomData); 42 | 43 | impl<'de, V: Copy + Deserialize<'de>, const N: usize> Visitor<'de> for Vi { 44 | type Value = Stack; 45 | 46 | fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { 47 | formatter.write_str("a Stack") 48 | } 49 | 50 | fn visit_seq(self, mut access: A) -> Result 51 | where 52 | A: SeqAccess<'de>, 53 | { 54 | let mut p: Self::Value = Stack::new(); 55 | while let Some(v) = access.next_element()? { 56 | p.push(v); 57 | } 58 | Ok(p) 59 | } 60 | } 61 | 62 | impl<'de, V: Copy + Deserialize<'de>, const N: usize> Deserialize<'de> for Stack { 63 | fn deserialize(deserializer: D) -> Result 64 | where 65 | D: Deserializer<'de>, 66 | { 67 | deserializer.deserialize_seq(Vi(PhantomData)) 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | use bincode::{deserialize, serialize}; 73 | 74 | #[test] 75 | fn serialize_and_deserialize() { 76 | let mut before: Stack = Stack::new(); 77 | before.push(42); 78 | let bytes: Vec = serialize(&before).unwrap(); 79 | let after: Stack = deserialize(&bytes).unwrap(); 80 | assert_eq!(42, after.into_iter().next().unwrap()); 81 | } 82 | -------------------------------------------------------------------------------- /src/stack.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Yegor Bugayenko 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | use crate::Stack; 22 | 23 | impl Stack { 24 | /// Make it from vector. 25 | #[inline] 26 | #[must_use] 27 | pub fn from_vec(v: Vec) -> Self { 28 | let mut p = Self::new(); 29 | for i in v { 30 | unsafe { p.push_unchecked(i) }; 31 | } 32 | p 33 | } 34 | 35 | /// Get the capacity. 36 | #[inline] 37 | #[must_use] 38 | pub fn capacity(&mut self) -> usize { 39 | N 40 | } 41 | 42 | /// Push new element into it. 43 | /// 44 | /// # Safety 45 | /// 46 | /// It may lead to undefined behavior, if you go over the boundary. 47 | #[inline] 48 | pub unsafe fn push_unchecked(&mut self, v: V) { 49 | self.items.as_mut_ptr().add(self.next).write(v); 50 | self.next += 1; 51 | } 52 | 53 | /// Push new element into it. 54 | /// 55 | /// # Panics 56 | /// 57 | /// If there is no more space in the stack, it will panic. 58 | #[inline] 59 | pub fn push(&mut self, v: V) { 60 | assert!(self.next < N, "No more space left in the stack"); 61 | unsafe { 62 | self.push_unchecked(v); 63 | } 64 | } 65 | 66 | /// Makes an attempt to push a new element into the stack. 67 | /// 68 | /// If there was enough space in the stack, `Ok(v)` is returned, while 69 | /// `Err` is returned otherwise. 70 | /// 71 | /// # Errors 72 | /// 73 | /// If there is not enough space in the stack, `Err` is returned. 74 | #[inline] 75 | pub fn try_push(&mut self, v: V) -> Result<(), String> { 76 | if self.next < N { 77 | self.push(v); 78 | Ok(()) 79 | } else { 80 | Err(format!( 81 | "There are no space left in the stack of {}", 82 | self.capacity() 83 | )) 84 | } 85 | } 86 | 87 | /// Pop a element from it. 88 | /// 89 | /// # Safety 90 | /// 91 | /// If there are no items in the array, the result is undefined. 92 | #[inline] 93 | pub unsafe fn pop_unchecked(&mut self) -> V { 94 | self.next -= 1; 95 | self.items.as_ptr().add(self.next).read() 96 | } 97 | 98 | /// Pop a element from it. 99 | /// 100 | /// # Panics 101 | /// 102 | /// If there are no items in the array, it will panic. 103 | #[inline] 104 | pub fn pop(&mut self) -> V { 105 | assert!(self.next > 0, "No more items left in the stack"); 106 | unsafe { self.pop_unchecked() } 107 | } 108 | 109 | /// Pop a element from it. 110 | /// 111 | /// # Errors 112 | /// 113 | /// If there is no more elements left, it will return `None`. 114 | #[inline] 115 | pub fn try_pop(&mut self) -> Result { 116 | if self.next == 0 { 117 | Err(format!( 118 | "There are no items left in the stack of {}", 119 | self.capacity() 120 | )) 121 | } else { 122 | Ok(self.pop()) 123 | } 124 | } 125 | 126 | /// Clear. 127 | #[inline] 128 | pub fn clear(&mut self) { 129 | self.next = 0; 130 | } 131 | 132 | /// Is it empty. 133 | #[inline] 134 | #[must_use] 135 | pub const fn is_empty(&self) -> bool { 136 | self.len() == 0 137 | } 138 | 139 | /// Length of it. 140 | #[inline] 141 | #[must_use] 142 | pub const fn len(&self) -> usize { 143 | self.next 144 | } 145 | } 146 | 147 | #[test] 148 | fn push_one() { 149 | let mut s: Stack = Stack::new(); 150 | unsafe { s.push_unchecked(42) }; 151 | assert_eq!(42, s.pop()); 152 | } 153 | 154 | #[test] 155 | fn push_safely() { 156 | let mut s: Stack = Stack::new(); 157 | s.push(42); 158 | assert_eq!(42, s.pop()); 159 | } 160 | 161 | #[test] 162 | fn try_to_push() { 163 | let mut s: Stack = Stack::new(); 164 | assert!(s.try_push(42).is_ok()); 165 | assert!(s.try_push(16).is_err()); 166 | assert!(s.try_push(0).err().unwrap().contains("space")); 167 | assert_eq!(42, s.pop()); 168 | } 169 | 170 | #[test] 171 | fn try_to_pop() { 172 | let mut s: Stack = Stack::new(); 173 | assert!(s.try_pop().is_err()); 174 | assert!(s.try_pop().err().unwrap().contains("left")); 175 | } 176 | 177 | #[test] 178 | fn push_after_clear() { 179 | let mut s: Stack = Stack::new(); 180 | unsafe { s.push_unchecked(42) }; 181 | s.clear(); 182 | s.push(16); 183 | assert_eq!(16, s.pop()); 184 | } 185 | 186 | #[test] 187 | fn build_from_vec() { 188 | let mut s: Stack = Stack::from_vec(vec![42]); 189 | assert_eq!(42, s.pop()); 190 | } 191 | 192 | #[test] 193 | fn pop_none() { 194 | let mut s: Stack = Stack::new(); 195 | assert_eq!(0, s.len()); 196 | assert!(s.is_empty()); 197 | assert!(s.try_pop().is_err()); 198 | } 199 | 200 | #[test] 201 | fn read_capacity() { 202 | let mut s: Stack = Stack::new(); 203 | assert_eq!(1, s.capacity()); 204 | } 205 | 206 | #[test] 207 | fn safely_pop() { 208 | let mut s: Stack = Stack::new(); 209 | s.push(42); 210 | assert_eq!(42, s.try_pop().unwrap()); 211 | } 212 | 213 | #[test] 214 | fn with_str() { 215 | let mut s: Stack<&str, 1> = Stack::new(); 216 | s.push("Hello!"); 217 | assert_eq!("Hello!", s.pop()); 218 | } 219 | 220 | #[test] 221 | #[should_panic] 222 | fn panic_on_empty_stack_push() { 223 | let mut s: Stack = Stack::new(); 224 | assert_eq!(0, s.len()); 225 | s.push(11); 226 | } 227 | 228 | #[test] 229 | #[should_panic] 230 | fn panic_on_empty_stack_pop() { 231 | let mut s: Stack = Stack::new(); 232 | assert_eq!(0, s.len()); 233 | s.pop(); 234 | } 235 | 236 | #[test] 237 | fn push_and_pop() { 238 | let mut s: Stack = Stack::new(); 239 | s.push(42); 240 | assert_eq!(42, s.pop()); 241 | } 242 | --------------------------------------------------------------------------------