From 76058032f5a83bd3b9fb4228fb3cdb28e63274f9 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 6 Jan 2020 19:29:40 -0700 Subject: [PATCH] base files --- .gitignore | 2 + Cargo.toml | 12 +++++ LICENSE-ZLIB.md | 11 +++++ rustfmt.toml | 8 ++++ src/lib.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/basic.rs | 33 ++++++++++++++ 6 files changed, 182 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE-ZLIB.md create mode 100644 rustfmt.toml create mode 100644 src/lib.rs create mode 100644 tests/basic.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8a0c108 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tinyvec" +description = "Just, really the littlest Vec you could need. So smol." +version = "0.0.0-alpha" +authors = ["Lokathor "] +edition = "2018" +license = "Zlib" +keywords = ["vec", "no_std", "smol"] +categories = ["data-structures", "no-std"] + +[dependencies] +# not even std! diff --git a/LICENSE-ZLIB.md b/LICENSE-ZLIB.md new file mode 100644 index 0000000..d70707c --- /dev/null +++ b/LICENSE-ZLIB.md @@ -0,0 +1,11 @@ +Copyright (c) 2019 Daniel "Lokathor" Gee. + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..51a2076 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,8 @@ +merge_imports = true +use_try_shorthand = true +tab_spaces = 2 +max_width = 80 +color = "Never" +use_small_heuristics = "Max" +format_code_in_doc_comments = true +wrap_comments = true diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fd3e14a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,116 @@ +#![no_std] +#![forbid(unsafe_code)] + +//! Just, really the littlest Vec you could need. So smol. + +use core::ops::{Deref, DerefMut}; +use core::mem::replace; + +extern crate alloc; +use alloc::vec::Vec; + +// Note(Lokathor): We just want to hide the enum details away. Rust doesn't let +// you be an enum with private variants, so instead we make this be a private +// inner field of the public `TinyVec` type. +#[derive(Debug, Clone)] +enum Payload { + Inline { len: usize, data: [T; 8] }, + Heap(Vec), +} + +/// A `TinyVec` is like a `Vec`, but it will store up to 8 elements +/// "inline" on the stack before transitioning into being a normal `Vec`. +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct TinyVec(Payload); + +// TODO: impl a better Debug + +impl Default for TinyVec { + fn default() -> Self { + Self::new() + } +} + +impl Deref for TinyVec { + type Target = [T]; + fn deref(&self) -> &[T] { + match &self.0 { + Payload::Inline { len, data } => { + &data[..*len] + } + Payload::Heap(vec) => { + &vec + } + } + } +} + +impl DerefMut for TinyVec { + fn deref_mut(&mut self) -> &mut [T] { + match &mut self.0 { + Payload::Inline { len, data } => { + &mut data[..*len] + } + Payload::Heap(ref mut vec) => { + &mut vec[..] + } + } + } +} + +impl TinyVec { + pub fn new() -> Self { + Self(Payload::Inline { + len: 0, + data: [ + T::default(), + T::default(), + T::default(), + T::default(), + T::default(), + T::default(), + T::default(), + T::default(), + ], + }) + } + + pub fn push(&mut self, val: T) { + match &mut self.0 { + Payload::Inline { len: 8, data } => { + let mut v = Vec::with_capacity(8 + 10); + for data_mut in data.iter_mut() { + v.push(replace(data_mut, T::default())); + } + v.push(val); + replace(&mut self.0, Payload::Heap(v)); + } + Payload::Inline { len, data } => { + debug_assert!(*len < 8, "push: illegal len: {}", len); + data[*len] = val; + *len += 1; + } + Payload::Heap(ref mut vec) => { + vec.push(val) + } + } + } + + pub fn pop(&mut self) -> Option { + match &mut self.0 { + Payload::Inline { len: 0, .. } => { + None + } + Payload::Inline { len, data } => { + debug_assert!(*len > 0, "pop: illegal len: {}", len); + let out = replace(&mut data[*len - 1], T::default()); + *len -= 1; + Some(out) + } + Payload::Heap(ref mut vec) => { + vec.pop() + } + } + } +} diff --git a/tests/basic.rs b/tests/basic.rs new file mode 100644 index 0000000..e36b7e5 --- /dev/null +++ b/tests/basic.rs @@ -0,0 +1,33 @@ + +extern crate std; + +use tinyvec::*; + +#[test] +fn test_push_pop() { + let mut tv = TinyVec::new(); + + assert_eq!(tv.pop(), None); + + tv.push(5_i32); + assert_eq!(tv.pop(), Some(5_i32)); + assert_eq!(tv.len(), 0); + + for i in 0 .. 10 { + tv.push(i); + } + assert_eq!(tv.len(), 10); + assert_eq!(tv.pop(), Some(9)); + assert_eq!(tv.pop(), Some(8)); + assert_eq!(tv.pop(), Some(7)); + assert_eq!(tv.pop(), Some(6)); + assert_eq!(tv.pop(), Some(5)); + assert_eq!(tv.pop(), Some(4)); + assert_eq!(tv.pop(), Some(3)); + assert_eq!(tv.pop(), Some(2)); + assert_eq!(tv.pop(), Some(1)); + assert_eq!(tv.pop(), Some(0)); + assert_eq!(tv.pop(), None); + assert_eq!(tv.pop(), None); + assert_eq!(tv.pop(), None); +}