Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a generic length parameter for Vec<T, N> #504

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `QueueView`, the `!Sized` version of `Queue`.
- Added `SortedLinkedListView`, the `!Sized` version of `SortedLinkedList`.
- Added implementation of `Borrow` and `BorrowMut` for `String` and `Vec`.
- Added `LenT` generic to `Vec<T, N>` and `VecView<T>` to save memory when using a sane capacity value.

### Changed

Expand Down
4 changes: 2 additions & 2 deletions src/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl private::Sealed for Min {}
/// struct if you want to write code that's generic over both.
pub struct BinaryHeapInner<T, K, S: Storage> {
pub(crate) _kind: PhantomData<K>,
pub(crate) data: VecInner<T, S>,
pub(crate) data: VecInner<T, usize, S>,
}

/// A priority queue implemented with a binary heap.
Expand Down Expand Up @@ -184,7 +184,7 @@ impl<T, K, const N: usize> BinaryHeap<T, K, N> {

impl<T, K, const N: usize> BinaryHeap<T, K, N> {
/// Returns the underlying `Vec<T,N>`. Order is arbitrary and time is *O*(1).
pub fn into_vec(self) -> Vec<T, N> {
pub fn into_vec(self) -> Vec<T, N, usize> {
self.data
}

Expand Down
11 changes: 6 additions & 5 deletions src/de.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
binary_heap::Kind as BinaryHeapKind, BinaryHeap, Deque, HistoryBuffer, IndexMap, IndexSet,
LinearMap, String, Vec,
LenType, LinearMap, String, Vec,
};
use core::{
fmt,
Expand Down Expand Up @@ -95,21 +95,22 @@ where
}
}

impl<'de, T, const N: usize> Deserialize<'de> for Vec<T, N>
impl<'de, T, LenT: LenType, const N: usize> Deserialize<'de> for Vec<T, N, LenT>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ValueVisitor<'de, T, const N: usize>(PhantomData<(&'de (), T)>);
struct ValueVisitor<'de, T, LenT: LenType, const N: usize>(PhantomData<(&'de (), T, LenT)>);

impl<'de, T, const N: usize> serde::de::Visitor<'de> for ValueVisitor<'de, T, N>
impl<'de, T, LenT, const N: usize> serde::de::Visitor<'de> for ValueVisitor<'de, T, LenT, N>
where
T: Deserialize<'de>,
LenT: LenType,
{
type Value = Vec<T, N>;
type Value = Vec<T, N, LenT>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sequence")
Expand Down
4 changes: 2 additions & 2 deletions src/defmt.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Defmt implementations for heapless types

use crate::{storage::Storage, string::StringInner, vec::VecInner};
use crate::{storage::Storage, string::StringInner, vec::VecInner, LenType};
use defmt::Formatter;

impl<T, S: Storage> defmt::Format for VecInner<T, S>
impl<T, LenT: LenType, S: Storage> defmt::Format for VecInner<T, LenT, S>
where
T: defmt::Format,
{
Expand Down
4 changes: 2 additions & 2 deletions src/indexmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ macro_rules! probe_loop {
}

struct CoreMap<K, V, const N: usize> {
entries: Vec<Bucket<K, V>, N>,
entries: Vec<Bucket<K, V>, N, usize>,
indices: [Option<Pos>; N],
}

Expand Down Expand Up @@ -1298,7 +1298,7 @@ where

#[derive(Clone)]
pub struct IntoIter<K, V, const N: usize> {
entries: Vec<Bucket<K, V>, N>,
entries: Vec<Bucket<K, V>, N, usize>,
}

impl<K, V, const N: usize> Iterator for IntoIter<K, V, N> {
Expand Down
112 changes: 112 additions & 0 deletions src/len_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use core::{
fmt::{Debug, Display},
ops::{Add, AddAssign, Sub, SubAssign},
};

mod private {
pub trait Sealed {}

impl Sealed for u8 {}
impl Sealed for u16 {}
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
impl Sealed for u32 {}

impl Sealed for usize {}
}

macro_rules! impl_lentype {
($($(#[$meta:meta])* $LenT:ty),*) => {$(
$(#[$meta])*
impl LenType for $LenT {
const ZERO: Self = 0;
const ONE: Self = 1;
const MAX: usize = Self::MAX as _;
}
)*}
}

/// A sealed trait representing a valid type to use as a length for a container.
///
/// This cannot be implemented in user code, and is restricted to `u8`, `u16`, `u32`, and `usize`.
pub trait LenType:
private::Sealed
+ Send
+ Sync
+ Copy
+ Display
+ Debug
+ PartialEq
+ Add<Output = Self>
+ AddAssign
+ Sub<Output = Self>
+ SubAssign
+ PartialOrd
+ TryFrom<usize, Error: Debug>
+ TryInto<usize, Error: Debug>
{
/// The zero value of the integer type.
const ZERO: Self;
/// The one value of the integer type.
const ONE: Self;
/// The maximum value of this type, as a `usize`.
const MAX: usize;

/// An infallible conversion from `usize` to `LenT`.
fn from_usize(val: usize) -> Self {
val.try_into().unwrap()
}

/// An infallible conversion from `LenT` to `usize`.
fn into_usize(self) -> usize {
self.try_into().unwrap()
}
}

impl_lentype!(
u8,
u16,
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
u32,
usize
);

macro_rules! impl_lentodefault {
($LenT:ty: $($len:literal),*) => {$(
impl SmallestLenType for Const<$len> {
type Type = $LenT;
}
)*};
}

/// A struct to create individual types for mapping with [`SmallestLenType`].
///
/// See the documentation of [`DefaultLenType`] for a detailed explanation.
pub struct Const<const N: usize>;
GnomedDev marked this conversation as resolved.
Show resolved Hide resolved

/// A trait to map [`Const`] to it's respective [`LenType`].
///
/// See the documentation of [`DefaultLenType`] for a detailed explanation.
#[diagnostic::on_unimplemented(
message = "Length `N` does not have a default `LenType` mapping",
note = "Provide the `LenType` explicitly, such as `usize`"
)]
pub trait SmallestLenType {
type Type: LenType;
}

/// A type alias to perform the `const N: usize` -> `LenType` mapping.
///
/// This is impossible to perform directly, but it is possible to write a `const N: usize` -> related `Type` mapping via a const generic argument,
/// then map from that to an unrelated type via a trait with associated types.
///
/// [`Const`] is the "related type" in the above explaination, [`SmallestLenType`] is the mapping trait.
pub type DefaultLenType<const N: usize> = <Const<N> as SmallestLenType>::Type;

impl_lentodefault!(u8: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255);
impl_lentodefault!(u16: 256, 300, 400, 500, 512, 600, 700, 800, 900, 1000, 1024, 2000, 2048, 4000, 4096, 8000, 8192, 16000, 16384, 32000, 32768, 65000, 65535);
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
impl_lentodefault!(u32: 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648);

pub const fn check_capacity_fits<LenT: LenType, const N: usize>() {
assert!(LenT::MAX >= N, "The capacity is larger than `LenT` can hold, increase the size of `LenT` or reduce the capacity")
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub use indexmap::{
ValuesMut as IndexMapValuesMut,
};
pub use indexset::{FnvIndexSet, IndexSet, Iter as IndexSetIter};
pub use len_type::LenType;
pub use linear_map::LinearMap;
pub use string::String;

Expand All @@ -107,6 +108,7 @@ pub mod deque;
pub mod histbuf;
mod indexmap;
mod indexset;
mod len_type;
pub mod linear_map;
mod slice;
pub mod storage;
Expand Down
4 changes: 2 additions & 2 deletions src/linear_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{

/// Base struct for [`LinearMap`] and [`LinearMapView`]
pub struct LinearMapInner<K, V, S: Storage> {
pub(crate) buffer: VecInner<(K, V), S>,
pub(crate) buffer: VecInner<(K, V), usize, S>,
}

/// A fixed capacity map/dictionary that performs lookups via linear search.
Expand Down Expand Up @@ -445,7 +445,7 @@ pub struct IntoIter<K, V, const N: usize>
where
K: Eq,
{
inner: <Vec<(K, V), N> as IntoIterator>::IntoIter,
inner: <Vec<(K, V), N, usize> as IntoIterator>::IntoIter,
}

impl<K, V, const N: usize> Iterator for IntoIter<K, V, N>
Expand Down
4 changes: 2 additions & 2 deletions src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
storage::Storage,
string::StringInner,
vec::VecInner,
IndexMap, IndexSet,
IndexMap, IndexSet, LenType,
};
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};

Expand Down Expand Up @@ -49,7 +49,7 @@ where
}
}

impl<T, St: Storage> Serialize for VecInner<T, St>
impl<T, LenT: LenType, St: Storage> Serialize for VecInner<T, LenT, St>
where
T: Serialize,
{
Expand Down
21 changes: 13 additions & 8 deletions src/string/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use core::{
};

use crate::{
len_type::LenType,
storage::{OwnedStorage, Storage, ViewStorage},
vec::VecInner,
Vec,
Expand Down Expand Up @@ -47,7 +48,7 @@ impl fmt::Display for FromUtf16Error {
/// In most cases you should use [`String`] or [`StringView`] directly. Only use this
/// struct if you want to write code that's generic over both.
pub struct StringInner<S: Storage> {
vec: VecInner<u8, S>,
vec: VecInner<u8, usize, S>,
}

/// A fixed capacity [`String`](https://doc.rust-lang.org/std/string/struct.String.html).
Expand Down Expand Up @@ -210,9 +211,11 @@ impl<const N: usize> String<N> {
/// # Ok::<(), core::str::Utf8Error>(())
/// ```
#[inline]
pub fn from_utf8(vec: Vec<u8, N>) -> Result<Self, Utf8Error> {
pub fn from_utf8<LenT: LenType>(vec: Vec<u8, N, LenT>) -> Result<Self, Utf8Error> {
core::str::from_utf8(&vec)?;
Ok(Self { vec })

// SAFETY: UTF-8 invariant has just been checked by `str::from_utf8`.
Ok(unsafe { Self::from_utf8_unchecked(vec) })
GnomedDev marked this conversation as resolved.
Show resolved Hide resolved
}

/// Convert UTF-8 bytes into a `String`, without checking that the string
Expand All @@ -237,8 +240,10 @@ impl<const N: usize> String<N> {
/// assert_eq!("💖", sparkle_heart);
/// ```
#[inline]
pub unsafe fn from_utf8_unchecked(vec: Vec<u8, N>) -> Self {
Self { vec }
pub unsafe fn from_utf8_unchecked<LenT: LenType>(vec: Vec<u8, N, LenT>) -> Self {
Self {
vec: vec.cast_len_type(),
}
}

/// Converts a `String` into a byte vector.
Expand All @@ -260,7 +265,7 @@ impl<const N: usize> String<N> {
/// # Ok::<(), ()>(())
/// ```
#[inline]
pub fn into_bytes(self) -> Vec<u8, N> {
pub fn into_bytes(self) -> Vec<u8, N, usize> {
self.vec
}

Expand Down Expand Up @@ -417,7 +422,7 @@ impl<S: Storage> StringInner<S> {
/// assert_eq!(s, "olleh");
/// # Ok::<(), ()>(())
/// ```
pub unsafe fn as_mut_vec(&mut self) -> &mut VecInner<u8, S> {
pub unsafe fn as_mut_vec(&mut self) -> &mut VecInner<u8, usize, S> {
&mut self.vec
}

Expand Down Expand Up @@ -1025,7 +1030,7 @@ mod tests {
#[test]
fn into_bytes() {
let s: String<4> = String::try_from("ab").unwrap();
let b: Vec<u8, 4> = s.into_bytes();
let b: Vec<u8, 4, usize> = s.into_bytes();
assert_eq!(b.len(), 2);
assert_eq!(&[b'a', b'b'], &b[..]);
}
Expand Down
4 changes: 2 additions & 2 deletions src/ufmt.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{storage::Storage, string::StringInner, vec::VecInner};
use crate::{storage::Storage, string::StringInner, vec::VecInner, LenType};
use ufmt_write::uWrite;

impl<S: Storage> uWrite for StringInner<S> {
Expand All @@ -8,7 +8,7 @@ impl<S: Storage> uWrite for StringInner<S> {
}
}

impl<S: Storage> uWrite for VecInner<u8, S> {
impl<LenT: LenType, S: Storage> uWrite for VecInner<u8, LenT, S> {
type Error = ();
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
self.extend_from_slice(s.as_bytes())
Expand Down
Loading
Loading