tpm2_protocol/basic/
list.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5use crate::{basic::Uint32, TpmMarshal, TpmProtocolError, TpmResult, TpmSized, TpmUnmarshal};
6use core::{
7    convert::TryFrom,
8    fmt::Debug,
9    mem::{size_of, MaybeUninit},
10    ops::Deref,
11    slice,
12};
13
14/// A fixed-capacity list for TPM structures, implemented over a fixed-size array.
15#[derive(Clone, Copy)]
16pub struct TpmList<T: Copy, const CAPACITY: usize> {
17    items: [MaybeUninit<T>; CAPACITY],
18    len: usize,
19}
20
21impl<T: Copy, const CAPACITY: usize> TpmList<T, CAPACITY> {
22    /// Creates a new, empty `TpmList`.
23    #[must_use]
24    pub fn new() -> Self {
25        Self {
26            items: [const { MaybeUninit::uninit() }; CAPACITY],
27            len: 0,
28        }
29    }
30
31    /// Returns `true` if the list contains no elements.
32    #[must_use]
33    pub fn is_empty(&self) -> bool {
34        self.len == 0
35    }
36
37    /// Appends an element to the back of the list.
38    ///
39    /// # Errors
40    ///
41    /// Returns [`TooManyItems`](crate::TpmProtocolError::TooManyItems) if the list is at
42    /// full capacity.
43    pub fn try_push(&mut self, item: T) -> Result<(), TpmProtocolError> {
44        if self.len >= CAPACITY {
45            return Err(TpmProtocolError::TooManyItems);
46        }
47        self.items[self.len].write(item);
48        self.len += 1;
49        Ok(())
50    }
51
52    /// Appends a slice of elements to the back of the list.
53    ///
54    /// # Errors
55    ///
56    /// Returns [`TooManyItems`](crate::TpmProtocolError::TooManyItems) if the list cannot
57    /// fit all elements from the slice.
58    pub fn try_extend_from_slice(&mut self, slice: &[T]) -> Result<(), TpmProtocolError> {
59        let new_len = self
60            .len
61            .checked_add(slice.len())
62            .ok_or(TpmProtocolError::TooManyItems)?;
63
64        if new_len > CAPACITY {
65            return Err(TpmProtocolError::TooManyItems);
66        }
67
68        for (dest, src) in self.items[self.len..new_len].iter_mut().zip(slice) {
69            dest.write(*src);
70        }
71        self.len = new_len;
72        Ok(())
73    }
74}
75
76#[allow(unsafe_code)]
77impl<T: Copy, const CAPACITY: usize> Deref for TpmList<T, CAPACITY> {
78    type Target = [T];
79
80    /// # Safety
81    ///
82    /// This implementation uses `unsafe` to provide a view into the initialized
83    /// portion of the list. The caller can rely on this being safe because:
84    /// 1. The first `self.len` items are guaranteed to be initialized by the `push` method.
85    /// 2. `MaybeUninit<T>` is guaranteed to have the same memory layout as `T`.
86    fn deref(&self) -> &Self::Target {
87        unsafe { slice::from_raw_parts(self.items.as_ptr().cast::<T>(), self.len) }
88    }
89}
90
91impl<T: Copy, const CAPACITY: usize> Default for TpmList<T, CAPACITY> {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97impl<T: Copy + Debug, const CAPACITY: usize> Debug for TpmList<T, CAPACITY> {
98    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99        f.debug_list().entries(self.iter()).finish()
100    }
101}
102
103impl<T: Copy + PartialEq, const CAPACITY: usize> PartialEq for TpmList<T, CAPACITY> {
104    fn eq(&self, other: &Self) -> bool {
105        **self == **other
106    }
107}
108
109impl<T: Copy + Eq, const CAPACITY: usize> Eq for TpmList<T, CAPACITY> {}
110
111impl<T: TpmSized + Copy, const CAPACITY: usize> TpmSized for TpmList<T, CAPACITY> {
112    const SIZE: usize = size_of::<Uint32>() + (T::SIZE * CAPACITY);
113    fn len(&self) -> usize {
114        size_of::<Uint32>() + self.iter().map(TpmSized::len).sum::<usize>()
115    }
116}
117
118impl<T: TpmMarshal + Copy, const CAPACITY: usize> TpmMarshal for TpmList<T, CAPACITY> {
119    fn marshal(&self, writer: &mut crate::TpmWriter) -> TpmResult<()> {
120        let len = Uint32::try_from(self.len).map_err(|_| TpmProtocolError::IntegerTooLarge)?;
121        TpmMarshal::marshal(&len, writer)?;
122        for item in &**self {
123            TpmMarshal::marshal(item, writer)?;
124        }
125        Ok(())
126    }
127}
128
129impl<T: TpmUnmarshal + Copy, const CAPACITY: usize> TpmUnmarshal for TpmList<T, CAPACITY> {
130    fn unmarshal(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
131        let (count_u32, mut buf) = Uint32::unmarshal(buf)?;
132        let count = u32::from(count_u32) as usize;
133        if count > CAPACITY {
134            return Err(TpmProtocolError::TooManyItems);
135        }
136
137        let mut list = Self::new();
138        for _ in 0..count {
139            let (item, rest) = T::unmarshal(buf)?;
140            list.try_push(item)?;
141            buf = rest;
142        }
143
144        Ok((list, buf))
145    }
146}