Skip to content

Commit fb62d12

Browse files
authored
Merge pull request #504 from GnomedDev/vec-generic-length
Implement a generic length parameter for Vec<T, N>
2 parents da404c1 + ca47366 commit fb62d12

13 files changed

+400
-204
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4848
- Added `truncate` to `IndexMap`.
4949
- Added `get_index` and `get_index_mut` to `IndexMap`.
5050
- Added `String::uDisplay`.
51+
- Added `LenT` generic to `Vec<T, N>` and `VecView<T>` to save memory when using a sane capacity value.
5152

5253
### Changed
5354

src/binary_heap.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl private::Sealed for Min {}
5757
/// struct if you want to write code that's generic over both.
5858
pub struct BinaryHeapInner<T, K, S: VecStorage<T> + ?Sized> {
5959
pub(crate) _kind: PhantomData<K>,
60-
pub(crate) data: VecInner<T, S>,
60+
pub(crate) data: VecInner<T, usize, S>,
6161
}
6262

6363
/// A priority queue implemented with a binary heap.
@@ -181,7 +181,7 @@ impl<T, K, const N: usize> BinaryHeap<T, K, N> {
181181

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

src/de.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
binary_heap::Kind as BinaryHeapKind, BinaryHeap, Deque, HistoryBuffer, IndexMap, IndexSet,
3-
LinearMap, String, Vec,
2+
binary_heap::Kind as BinaryHeapKind, len_type::LenType, BinaryHeap, Deque, HistoryBuffer,
3+
IndexMap, IndexSet, LinearMap, String, Vec,
44
};
55
use core::{
66
fmt,
@@ -95,21 +95,22 @@ where
9595
}
9696
}
9797

98-
impl<'de, T, const N: usize> Deserialize<'de> for Vec<T, N>
98+
impl<'de, T, LenT: LenType, const N: usize> Deserialize<'de> for Vec<T, N, LenT>
9999
where
100100
T: Deserialize<'de>,
101101
{
102102
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103103
where
104104
D: Deserializer<'de>,
105105
{
106-
struct ValueVisitor<'de, T, const N: usize>(PhantomData<(&'de (), T)>);
106+
struct ValueVisitor<'de, T, LenT: LenType, const N: usize>(PhantomData<(&'de (), T, LenT)>);
107107

108-
impl<'de, T, const N: usize> serde::de::Visitor<'de> for ValueVisitor<'de, T, N>
108+
impl<'de, T, LenT, const N: usize> serde::de::Visitor<'de> for ValueVisitor<'de, T, LenT, N>
109109
where
110110
T: Deserialize<'de>,
111+
LenT: LenType,
111112
{
112-
type Value = Vec<T, N>;
113+
type Value = Vec<T, N, LenT>;
113114

114115
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
115116
formatter.write_str("a sequence")

src/defmt.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! Defmt implementations for heapless types
22
33
use crate::{
4+
len_type::LenType,
45
string::{StringInner, StringStorage},
56
vec::{VecInner, VecStorage},
67
};
78
use defmt::Formatter;
89

9-
impl<T, S: VecStorage<T> + ?Sized> defmt::Format for VecInner<T, S>
10+
impl<T, LenT: LenType, S: VecStorage<T> + ?Sized> defmt::Format for VecInner<T, LenT, S>
1011
where
1112
T: defmt::Format,
1213
{

src/indexmap.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ macro_rules! probe_loop {
138138
}
139139

140140
struct CoreMap<K, V, const N: usize> {
141-
entries: Vec<Bucket<K, V>, N>,
141+
entries: Vec<Bucket<K, V>, N, usize>,
142142
indices: [Option<Pos>; N],
143143
}
144144

@@ -1417,7 +1417,7 @@ where
14171417

14181418
#[derive(Clone)]
14191419
pub struct IntoIter<K, V, const N: usize> {
1420-
entries: Vec<Bucket<K, V>, N>,
1420+
entries: Vec<Bucket<K, V>, N, usize>,
14211421
}
14221422

14231423
impl<K, V, const N: usize> Iterator for IntoIter<K, V, N> {

src/len_type.rs

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use core::{
2+
fmt::{Debug, Display},
3+
ops::{Add, AddAssign, Sub, SubAssign},
4+
};
5+
6+
pub trait Sealed:
7+
Send
8+
+ Sync
9+
+ Copy
10+
+ Display
11+
+ Debug
12+
+ PartialEq
13+
+ Add<Output = Self>
14+
+ AddAssign
15+
+ Sub<Output = Self>
16+
+ SubAssign
17+
+ PartialOrd
18+
+ TryFrom<usize, Error: Debug>
19+
+ TryInto<usize, Error: Debug>
20+
{
21+
/// The zero value of the integer type.
22+
const ZERO: Self;
23+
/// The one value of the integer type.
24+
const ONE: Self;
25+
/// The maximum value of this type, as a `usize`.
26+
const MAX: usize;
27+
28+
/// An infallible conversion from `usize` to `LenT`.
29+
#[inline]
30+
fn from_usize(val: usize) -> Self {
31+
val.try_into().unwrap()
32+
}
33+
34+
/// An infallible conversion from `LenT` to `usize`.
35+
#[inline]
36+
fn into_usize(self) -> usize {
37+
self.try_into().unwrap()
38+
}
39+
}
40+
41+
macro_rules! impl_lentype {
42+
($($(#[$meta:meta])* $LenT:ty),*) => {$(
43+
$(#[$meta])*
44+
impl Sealed for $LenT {
45+
const ZERO: Self = 0;
46+
const ONE: Self = 1;
47+
const MAX: usize = Self::MAX as _;
48+
}
49+
50+
$(#[$meta])*
51+
impl LenType for $LenT {}
52+
)*}
53+
}
54+
55+
/// A sealed trait representing a valid type to use as a length for a container.
56+
///
57+
/// This cannot be implemented in user code, and is restricted to `u8`, `u16`, `u32`, and `usize`.
58+
pub trait LenType: Sealed {}
59+
60+
impl_lentype!(
61+
u8,
62+
u16,
63+
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
64+
u32,
65+
usize
66+
);
67+
68+
macro_rules! impl_lentodefault {
69+
($LenT:ty: $($len:literal),*) => {$(
70+
impl SmallestLenType for Const<$len> {
71+
type Type = $LenT;
72+
}
73+
)*};
74+
}
75+
76+
/// A struct to create individual types for mapping with [`SmallestLenType`].
77+
///
78+
/// See the documentation of [`DefaultLenType`] for a detailed explanation.
79+
pub struct Const<const N: usize>;
80+
81+
/// A trait to map [`Const`] to it's respective [`LenType`].
82+
///
83+
/// See the documentation of [`DefaultLenType`] for a detailed explanation.
84+
#[diagnostic::on_unimplemented(
85+
message = "Length `N` does not have a default `LenType` mapping",
86+
note = "Provide the `LenType` explicitly, such as `usize`"
87+
)]
88+
pub trait SmallestLenType {
89+
type Type: LenType;
90+
}
91+
92+
/// A type alias to perform the `const N: usize` -> `LenType` mapping.
93+
///
94+
/// This is impossible to perform directly, but it is possible to write a `const N: usize` -> related `Type` mapping via a const generic argument,
95+
/// then map from that to an unrelated type via a trait with associated types.
96+
///
97+
/// [`Const`] is the "related type" in the above explaination, [`SmallestLenType`] is the mapping trait.
98+
pub type DefaultLenType<const N: usize> = <Const<N> as SmallestLenType>::Type;
99+
100+
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);
101+
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);
102+
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
103+
impl_lentodefault!(u32: 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648);
104+
105+
pub const fn check_capacity_fits<LenT: LenType, const N: usize>() {
106+
assert!(LenT::MAX >= N, "The capacity is larger than `LenT` can hold, increase the size of `LenT` or reduce the capacity");
107+
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ pub use indexmap::{
165165
ValuesMut as IndexMapValuesMut,
166166
};
167167
pub use indexset::{FnvIndexSet, IndexSet, Iter as IndexSetIter};
168+
pub use len_type::LenType;
168169
pub use linear_map::LinearMap;
169170
pub use string::String;
170171

@@ -178,6 +179,7 @@ pub mod deque;
178179
pub mod histbuf;
179180
mod indexmap;
180181
mod indexset;
182+
mod len_type;
181183
pub mod linear_map;
182184
mod slice;
183185
pub mod storage;

src/linear_map.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub type ViewStorage<K, V> = ViewVecStorage<(K, V)>;
8989

9090
/// Base struct for [`LinearMap`] and [`LinearMapView`]
9191
pub struct LinearMapInner<K, V, S: LinearMapStorage<K, V> + ?Sized> {
92-
pub(crate) buffer: VecInner<(K, V), S>,
92+
pub(crate) buffer: VecInner<(K, V), usize, S>,
9393
}
9494

9595
/// A fixed capacity map/dictionary that performs lookups via linear search.
@@ -543,7 +543,7 @@ pub struct IntoIter<K, V, const N: usize>
543543
where
544544
K: Eq,
545545
{
546-
inner: <Vec<(K, V), N> as IntoIterator>::IntoIter,
546+
inner: <Vec<(K, V), N, usize> as IntoIterator>::IntoIter,
547547
}
548548

549549
impl<K, V, const N: usize> Iterator for IntoIter<K, V, N>

src/ser.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
binary_heap::{BinaryHeapInner, Kind as BinaryHeapKind},
55
deque::DequeInner,
66
histbuf::{HistBufStorage, HistoryBufferInner},
7+
len_type::LenType,
78
linear_map::{LinearMapInner, LinearMapStorage},
89
string::{StringInner, StringStorage},
910
vec::{VecInner, VecStorage},
@@ -48,7 +49,7 @@ where
4849
}
4950
}
5051

51-
impl<T, St: VecStorage<T> + ?Sized> Serialize for VecInner<T, St>
52+
impl<T, LenT: LenType, St: VecStorage<T>> Serialize for VecInner<T, LenT, St>
5253
where
5354
T: Serialize,
5455
{

src/string/mod.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ use core::{
1111
str::{self, Utf8Error},
1212
};
1313

14-
use crate::vec::{OwnedVecStorage, Vec, VecInner, ViewVecStorage};
1514
use crate::CapacityError;
15+
use crate::{
16+
len_type::LenType,
17+
vec::{OwnedVecStorage, Vec, VecInner, ViewVecStorage},
18+
};
1619

1720
mod drain;
1821
pub use drain::Drain;
@@ -131,7 +134,7 @@ pub type ViewStorage = ViewVecStorage<u8>;
131134
/// In most cases you should use [`String`] or [`StringView`] directly. Only use this
132135
/// struct if you want to write code that's generic over both.
133136
pub struct StringInner<S: StringStorage + ?Sized> {
134-
vec: VecInner<u8, S>,
137+
vec: VecInner<u8, usize, S>,
135138
}
136139

137140
/// A fixed capacity [`String`](https://doc.rust-lang.org/std/string/struct.String.html).
@@ -229,9 +232,11 @@ impl<const N: usize> String<N> {
229232
/// # Ok::<(), core::str::Utf8Error>(())
230233
/// ```
231234
#[inline]
232-
pub fn from_utf8(vec: Vec<u8, N>) -> Result<Self, Utf8Error> {
235+
pub fn from_utf8<LenT: LenType>(vec: Vec<u8, N, LenT>) -> Result<Self, Utf8Error> {
233236
core::str::from_utf8(&vec)?;
234-
Ok(Self { vec })
237+
238+
// SAFETY: UTF-8 invariant has just been checked by `str::from_utf8`.
239+
Ok(unsafe { Self::from_utf8_unchecked(vec) })
235240
}
236241

237242
/// Convert UTF-8 bytes into a `String`, without checking that the string
@@ -256,8 +261,10 @@ impl<const N: usize> String<N> {
256261
/// assert_eq!("💖", sparkle_heart);
257262
/// ```
258263
#[inline]
259-
pub const unsafe fn from_utf8_unchecked(vec: Vec<u8, N>) -> Self {
260-
Self { vec }
264+
pub unsafe fn from_utf8_unchecked<LenT: LenType>(vec: Vec<u8, N, LenT>) -> Self {
265+
Self {
266+
vec: vec.cast_len_type(),
267+
}
261268
}
262269

263270
/// Converts a `String` into a byte vector.
@@ -279,7 +286,7 @@ impl<const N: usize> String<N> {
279286
/// # Ok::<(), heapless::CapacityError>(())
280287
/// ```
281288
#[inline]
282-
pub fn into_bytes(self) -> Vec<u8, N> {
289+
pub fn into_bytes(self) -> Vec<u8, N, usize> {
283290
self.vec
284291
}
285292
}
@@ -457,7 +464,7 @@ impl<S: StringStorage + ?Sized> StringInner<S> {
457464
/// assert_eq!(s, "olleh");
458465
/// # Ok::<(), heapless::CapacityError>(())
459466
/// ```
460-
pub unsafe fn as_mut_vec(&mut self) -> &mut VecInner<u8, S> {
467+
pub unsafe fn as_mut_vec(&mut self) -> &mut VecInner<u8, usize, S> {
461468
&mut self.vec
462469
}
463470

@@ -1068,7 +1075,7 @@ mod tests {
10681075
#[test]
10691076
fn into_bytes() {
10701077
let s: String<4> = String::try_from("ab").unwrap();
1071-
let b: Vec<u8, 4> = s.into_bytes();
1078+
let b: Vec<u8, 4, usize> = s.into_bytes();
10721079
assert_eq!(b.len(), 2);
10731080
assert_eq!(b"ab", &b[..]);
10741081
}

src/ufmt.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
len_type::LenType,
23
string::{StringInner, StringStorage},
34
vec::{VecInner, VecStorage},
45
CapacityError,
@@ -24,7 +25,7 @@ impl<S: StringStorage + ?Sized> uWrite for StringInner<S> {
2425
}
2526
}
2627

27-
impl<S: VecStorage<u8> + ?Sized> uWrite for VecInner<u8, S> {
28+
impl<LenT: LenType, S: VecStorage<u8> + ?Sized> uWrite for VecInner<u8, LenT, S> {
2829
type Error = CapacityError;
2930
#[inline]
3031
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {

0 commit comments

Comments
 (0)