Skip to content

Commit 65ae3d7

Browse files
authored
Add heapless::Vec-backed SequenceOf and SetOf (#2229)
This reverts commit 48cb730 (#2220) We already have a `heapless` feature, so might as well use it as the backing implementation for these types. This restores the previously removed `SequenceOf` and `SetOf` types, using `heapless::Vec` as their internal data structure. Closes #2218
1 parent 302dd3f commit 65ae3d7

File tree

9 files changed

+616
-48
lines changed

9 files changed

+616
-48
lines changed

.github/workflows/der.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,16 @@ jobs:
9191
- run: cargo hack test --feature-powerset --exclude-features arbitrary,std,heapless
9292
- run: cargo test --features arbitrary
9393
- run: cargo test --features std
94-
#- run: cargo test --all-features # TODO(tarcieri): test heapless
94+
95+
# Test `heapless` feature (requires MSRV 1.87)
96+
test-heapless:
97+
runs-on: ubuntu-latest
98+
steps:
99+
- uses: actions/checkout@v6
100+
- uses: dtolnay/rust-toolchain@master
101+
with:
102+
toolchain: 1.87
103+
- run: cargo test --features heapless
95104

96105
derive:
97106
runs-on: ubuntu-latest

der/src/asn1.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ mod private;
2626
#[cfg(feature = "real")]
2727
mod real;
2828
mod sequence;
29-
#[cfg(feature = "alloc")]
3029
mod sequence_of;
31-
#[cfg(feature = "alloc")]
3230
mod set_of;
3331
mod teletex_string;
3432
mod utc_time;
@@ -69,5 +67,14 @@ pub use self::{
6967
teletex_string::TeletexString,
7068
};
7169

70+
#[cfg(feature = "heapless")]
71+
pub use self::{
72+
sequence_of::{SequenceOf, SequenceOfIter},
73+
set_of::SetOf,
74+
};
75+
76+
#[cfg(any(feature = "alloc", feature = "heapless"))]
77+
pub use set_of::SetOfIter;
78+
7279
#[cfg(feature = "oid")]
7380
pub use const_oid::ObjectIdentifier;

der/src/asn1/context_specific.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ mod tests {
3030
use crate::{Decode, Encode, SliceReader, TagMode, TagNumber, asn1::BitStringRef};
3131
use hex_literal::hex;
3232

33-
#[cfg(feature = "alloc")]
34-
use crate::asn1::{ContextSpecificRef, SetOfVec, Utf8StringRef};
33+
#[cfg(feature = "heapless")]
34+
use crate::asn1::{ContextSpecificRef, SetOf, Utf8StringRef};
3535

3636
// Public key data from `pkcs8` crate's `ed25519-pkcs8-v2.der`
3737
const EXAMPLE_BYTES: &[u8] =
@@ -125,13 +125,13 @@ mod tests {
125125
}
126126

127127
#[test]
128-
#[cfg(feature = "alloc")]
128+
#[cfg(feature = "heapless")]
129129
fn context_specific_explicit_ref() {
130-
let mut set = SetOfVec::new();
130+
let mut set = SetOf::new();
131131
set.insert(8u16).unwrap();
132132
set.insert(7u16).unwrap();
133133

134-
let field = ContextSpecificRef::<SetOfVec<u16>> {
134+
let field = ContextSpecificRef::<SetOf<u16, 2>> {
135135
value: &set,
136136
tag_number: TagNumber(2),
137137
tag_mode: TagMode::Explicit,
@@ -148,7 +148,7 @@ mod tests {
148148
);
149149

150150
let mut reader = SliceReader::new(encoded).unwrap();
151-
let field = ContextSpecific::<SetOfVec<u16>>::decode_explicit(&mut reader, TagNumber(2))
151+
let field = ContextSpecific::<SetOf<u16, 2>>::decode_explicit(&mut reader, TagNumber(2))
152152
.unwrap()
153153
.unwrap();
154154

@@ -158,16 +158,16 @@ mod tests {
158158
}
159159

160160
#[test]
161-
#[cfg(feature = "alloc")]
161+
#[cfg(feature = "heapless")]
162162
fn context_specific_implicit_ref() {
163163
let hello = Utf8StringRef::new("Hello").unwrap();
164164
let world = Utf8StringRef::new("world").unwrap();
165165

166-
let mut set = SetOfVec::new();
166+
let mut set = SetOf::new();
167167
set.insert(hello).unwrap();
168168
set.insert(world).unwrap();
169169

170-
let field = ContextSpecificRef::<SetOfVec<Utf8StringRef<'_>>> {
170+
let field = ContextSpecificRef::<SetOf<Utf8StringRef<'_>, 2>> {
171171
value: &set,
172172
tag_number: TagNumber(2),
173173
tag_mode: TagMode::Implicit,
@@ -185,7 +185,7 @@ mod tests {
185185
);
186186

187187
let mut reader = SliceReader::new(encoded).unwrap();
188-
let field = ContextSpecific::<SetOfVec<Utf8StringRef<'_>>>::decode_implicit(
188+
let field = ContextSpecific::<SetOf<Utf8StringRef<'_>, 2>>::decode_implicit(
189189
&mut reader,
190190
TagNumber(2),
191191
)

der/src/asn1/sequence_of.rs

Lines changed: 235 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,178 @@
11
//! ASN.1 `SEQUENCE OF` support.
22
33
use crate::{
4-
Decode, DecodeValue, DerOrd, Encode, EncodeValue, Error, FixedTag, Header, Length, Reader, Tag,
5-
ValueOrd, Writer, ord::iter_cmp,
4+
DerOrd, Encode, EncodeValue, Error, FixedTag, Length, Tag, ValueOrd, Writer, ord::iter_cmp,
65
};
76
use core::cmp::Ordering;
87

8+
#[cfg(any(feature = "alloc", feature = "heapless"))]
9+
use crate::{Decode, DecodeValue, Header, Reader};
910
#[cfg(feature = "alloc")]
1011
use alloc::vec::Vec;
12+
#[cfg(feature = "heapless")]
13+
use {crate::ErrorKind, core::slice};
14+
15+
/// ASN.1 `SEQUENCE OF` backed by an array.
16+
///
17+
/// This type implements an append-only `SEQUENCE OF` type which is stack-based
18+
/// and does not depend on `alloc` support.
19+
// TODO(tarcieri): use `ArrayVec` when/if it's merged into `core` (rust-lang/rfcs#3316)
20+
#[cfg(feature = "heapless")]
21+
#[derive(Clone, Debug, Eq, PartialEq)]
22+
pub struct SequenceOf<T, const N: usize> {
23+
inner: heapless::Vec<T, N>,
24+
}
25+
26+
#[cfg(feature = "heapless")]
27+
impl<T, const N: usize> SequenceOf<T, N> {
28+
/// Create a new [`SequenceOf`].
29+
pub fn new() -> Self {
30+
Self {
31+
inner: heapless::Vec::new(),
32+
}
33+
}
34+
35+
/// Add an element to this [`SequenceOf`].
36+
pub fn add(&mut self, element: T) -> Result<(), Error> {
37+
self.inner
38+
.push(element)
39+
.map_err(|_| ErrorKind::Overlength.into())
40+
}
41+
42+
/// Borrow the elements of this [`SequenceOf`] as a slice.
43+
pub fn as_slice(&self) -> &[T] {
44+
self.inner.as_slice()
45+
}
46+
47+
/// Borrow the elements of this [`SequenceOf`] mutably as a slice.
48+
pub fn as_mut_slice(&mut self) -> &mut [T] {
49+
self.inner.as_mut_slice()
50+
}
51+
52+
/// Get an element of this [`SequenceOf`].
53+
pub fn get(&self, index: usize) -> Option<&T> {
54+
self.inner.get(index)
55+
}
56+
57+
/// Extract the inner `heapless::Vec`.
58+
pub fn into_inner(self) -> heapless::Vec<T, N> {
59+
self.inner
60+
}
61+
62+
/// Iterate over the elements in this [`SequenceOf`].
63+
pub fn iter(&self) -> SequenceOfIter<'_, T> {
64+
SequenceOfIter {
65+
inner: self.inner.iter(),
66+
}
67+
}
68+
69+
/// Is this [`SequenceOf`] empty?
70+
pub fn is_empty(&self) -> bool {
71+
self.inner.is_empty()
72+
}
73+
74+
/// Number of elements in this [`SequenceOf`].
75+
pub fn len(&self) -> usize {
76+
self.inner.len()
77+
}
78+
}
79+
80+
#[cfg(feature = "heapless")]
81+
impl<T, const N: usize> AsRef<[T]> for SequenceOf<T, N> {
82+
fn as_ref(&self) -> &[T] {
83+
self.as_slice()
84+
}
85+
}
86+
87+
#[cfg(feature = "heapless")]
88+
impl<T, const N: usize> AsMut<[T]> for SequenceOf<T, N> {
89+
fn as_mut(&mut self) -> &mut [T] {
90+
self.as_mut_slice()
91+
}
92+
}
93+
94+
#[cfg(feature = "heapless")]
95+
impl<T, const N: usize> Default for SequenceOf<T, N> {
96+
fn default() -> Self {
97+
Self::new()
98+
}
99+
}
100+
101+
#[cfg(feature = "heapless")]
102+
impl<'a, T, const N: usize> DecodeValue<'a> for SequenceOf<T, N>
103+
where
104+
T: Decode<'a>,
105+
{
106+
type Error = T::Error;
107+
108+
fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> Result<Self, Self::Error> {
109+
let mut sequence_of = Self::new();
110+
111+
while !reader.is_finished() {
112+
sequence_of.add(T::decode(reader)?)?;
113+
}
114+
115+
Ok(sequence_of)
116+
}
117+
}
118+
119+
#[cfg(feature = "heapless")]
120+
impl<T, const N: usize> EncodeValue for SequenceOf<T, N>
121+
where
122+
T: Encode,
123+
{
124+
fn value_len(&self) -> Result<Length, Error> {
125+
self.iter()
126+
.try_fold(Length::ZERO, |len, elem| len + elem.encoded_len()?)
127+
}
128+
129+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
130+
for elem in self.iter() {
131+
elem.encode(writer)?;
132+
}
133+
134+
Ok(())
135+
}
136+
}
137+
138+
#[cfg(feature = "heapless")]
139+
impl<T, const N: usize> FixedTag for SequenceOf<T, N> {
140+
const TAG: Tag = Tag::Sequence;
141+
}
142+
143+
#[cfg(feature = "heapless")]
144+
impl<T, const N: usize> ValueOrd for SequenceOf<T, N>
145+
where
146+
T: DerOrd,
147+
{
148+
fn value_cmp(&self, other: &Self) -> Result<Ordering, Error> {
149+
iter_cmp(self.iter(), other.iter())
150+
}
151+
}
152+
153+
/// Iterator over the elements of an [`SequenceOf`].
154+
#[cfg(feature = "heapless")]
155+
#[derive(Clone, Debug)]
156+
pub struct SequenceOfIter<'a, T> {
157+
/// Inner iterator.
158+
inner: slice::Iter<'a, T>,
159+
}
160+
161+
#[cfg(feature = "heapless")]
162+
impl<'a, T: 'a> Iterator for SequenceOfIter<'a, T> {
163+
type Item = &'a T;
164+
165+
fn next(&mut self) -> Option<&'a T> {
166+
self.inner.next()
167+
}
168+
169+
fn size_hint(&self) -> (usize, Option<usize>) {
170+
self.inner.size_hint()
171+
}
172+
}
173+
174+
#[cfg(feature = "heapless")]
175+
impl<'a, T: 'a> ExactSizeIterator for SequenceOfIter<'a, T> {}
11176

12177
impl<T: Encode> EncodeValue for [T] {
13178
fn value_len(&self) -> Result<Length, Error> {
@@ -24,6 +189,49 @@ impl<T: Encode> EncodeValue for [T] {
24189
}
25190
}
26191

192+
#[cfg(feature = "heapless")]
193+
impl<'a, T, const N: usize> DecodeValue<'a> for [T; N]
194+
where
195+
T: Decode<'a>,
196+
{
197+
type Error = T::Error;
198+
199+
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self, Self::Error> {
200+
let sequence_of = SequenceOf::<T, N>::decode_value(reader, header)?;
201+
sequence_of
202+
.inner
203+
.into_array()
204+
.map_err(|_| reader.error(Self::TAG.length_error()).into())
205+
}
206+
}
207+
208+
impl<T, const N: usize> EncodeValue for [T; N]
209+
where
210+
T: Encode,
211+
{
212+
fn value_len(&self) -> Result<Length, Error> {
213+
self.iter()
214+
.try_fold(Length::ZERO, |len, elem| len + elem.encoded_len()?)
215+
}
216+
217+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
218+
self.as_slice().encode_value(writer)
219+
}
220+
}
221+
222+
impl<T, const N: usize> FixedTag for [T; N] {
223+
const TAG: Tag = Tag::Sequence;
224+
}
225+
226+
impl<T, const N: usize> ValueOrd for [T; N]
227+
where
228+
T: DerOrd,
229+
{
230+
fn value_cmp(&self, other: &Self) -> Result<Ordering, Error> {
231+
iter_cmp(self.iter(), other.iter())
232+
}
233+
}
234+
27235
#[cfg(feature = "alloc")]
28236
impl<'a, T> DecodeValue<'a> for Vec<T>
29237
where
@@ -71,3 +279,28 @@ where
71279
iter_cmp(self.iter(), other.iter())
72280
}
73281
}
282+
283+
#[cfg(all(test, feature = "heapless"))]
284+
mod tests {
285+
use super::SequenceOf;
286+
use crate::ord::DerOrd;
287+
288+
#[test]
289+
fn sequenceof_valueord_value_cmp() {
290+
use core::cmp::Ordering;
291+
292+
let arr1 = {
293+
let mut arr: SequenceOf<u16, 2> = SequenceOf::new();
294+
arr.add(0u16).expect("element to be added");
295+
arr.add(2u16).expect("element to be added");
296+
arr
297+
};
298+
let arr2 = {
299+
let mut arr: SequenceOf<u16, 2> = SequenceOf::new();
300+
arr.add(0u16).expect("element to be added");
301+
arr.add(1u16).expect("element to be added");
302+
arr
303+
};
304+
assert_eq!(arr1.der_cmp(&arr2), Ok(Ordering::Greater));
305+
}
306+
}

0 commit comments

Comments
 (0)