Skip to main content

inspect_format/container/
common.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
6
7pub trait BlockContainer {
8    type Data;
9    type ShareableData;
10
11    /// Returns the size of the container.
12    fn len(&self) -> usize;
13
14    /// Returns whether the container is empty or not.
15    fn is_empty(&self) -> bool {
16        self.len() == 0
17    }
18}
19
20/// Trait implemented by an Inspect container that can be read from.
21pub trait ReadBytes: BlockContainer {
22    /// Returns a slice of the given size at the given offset if one exists of the exact size.
23    fn get_slice_at(&self, offset: usize, size: usize) -> Option<&[u8]>;
24
25    /// Returns a slice of the given size at the beginning of the container if one exists of the
26    /// exact size.
27    #[inline]
28    fn get_slice(&self, size: usize) -> Option<&[u8]> {
29        self.get_slice_at(0, size)
30    }
31
32    /// Returns the value at the given offset, if one exists.
33    #[inline]
34    fn get_value<T: ContainerValue>(&self, offset: usize) -> Option<T> {
35        self.get_slice_at(offset, std::mem::size_of::<T>())
36            .and_then(|slice| T::read_from_bytes(slice).ok())
37    }
38}
39
40pub trait CopyBytes: BlockContainer {
41    fn copy_bytes_at(&self, offset: usize, dst: &mut [u8]);
42
43    fn copy_bytes(&self, dst: &mut [u8]) {
44        self.copy_bytes_at(0, dst)
45    }
46}
47
48/// Trait implemented by primitive types that can be read from and written to an Inspect container.
49pub trait ContainerValue:
50    FromBytes + IntoBytes + KnownLayout + Immutable + Copy + private::Sealed + 'static
51{
52}
53
54mod private {
55    pub trait Sealed {}
56}
57
58macro_rules! impl_container_value {
59    ($($type:ty),*) => {
60        $(
61            impl private::Sealed for $type {}
62            impl ContainerValue for $type {}
63        )*
64    };
65}
66
67impl_container_value!(u8, u16, u32, u64, i64, f64);
68
69/// Trait implemented by container to which bytes can be written.
70pub trait WriteBytes {
71    /// Returns an exclusive reference to a slice of the given size at the given offset if one
72    /// exists of the exact size.
73    fn get_slice_mut_at(&mut self, offset: usize, size: usize) -> Option<&mut [u8]>;
74
75    /// Returns an exclusive reference to a slice of the given size at the beginning of the
76    /// container if one exists of the exact size.
77    #[inline]
78    fn get_slice_mut(&mut self, size: usize) -> Option<&mut [u8]> {
79        self.get_slice_mut_at(0, size)
80    }
81
82    #[inline]
83    fn copy_from_slice_at(&mut self, offset: usize, bytes: &[u8]) {
84        // TODO: error
85        if let Some(slice) = self.get_slice_mut_at(offset, bytes.len()) {
86            slice.copy_from_slice(bytes);
87        }
88    }
89
90    #[inline]
91    fn copy_from_slice(&mut self, bytes: &[u8]) {
92        self.copy_from_slice_at(0, bytes);
93    }
94
95    /// Sets the value at the given offset, if in bounds.
96    #[inline]
97    fn set_value<T: ContainerValue>(
98        &mut self,
99        offset: usize,
100        value: T,
101    ) -> Result<(), crate::Error> {
102        if let Some(slice) = self.get_slice_mut_at(offset, std::mem::size_of::<T>()) {
103            slice.copy_from_slice(value.as_bytes());
104            Ok(())
105        } else {
106            Err(crate::Error::InvalidOffset(offset))
107        }
108    }
109}
110
111impl BlockContainer for Vec<u8> {
112    type Data = Self;
113    type ShareableData = ();
114
115    /// The number of bytes in the buffer.
116    #[inline]
117    fn len(&self) -> usize {
118        self.as_slice().len()
119    }
120}
121
122impl ReadBytes for Vec<u8> {
123    #[inline]
124    fn get_slice_at(&self, offset: usize, size: usize) -> Option<&[u8]> {
125        self.as_slice().get_slice_at(offset, size)
126    }
127}
128
129impl CopyBytes for Vec<u8> {
130    #[inline]
131    fn copy_bytes_at(&self, offset: usize, dst: &mut [u8]) {
132        if let Some(slice) = self.as_slice().get_slice_at(offset, dst.len()) {
133            dst.copy_from_slice(slice);
134        }
135    }
136}
137
138impl BlockContainer for [u8] {
139    type Data = ();
140    type ShareableData = ();
141
142    #[inline]
143    fn len(&self) -> usize {
144        <[u8]>::len(self)
145    }
146}
147
148impl ReadBytes for [u8] {
149    #[inline]
150    fn get_slice_at(&self, offset: usize, size: usize) -> Option<&[u8]> {
151        let upper_bound = offset.checked_add(size)?;
152        if offset >= self.len() || upper_bound > self.len() {
153            return None;
154        }
155        Some(&self[offset..upper_bound])
156    }
157}
158
159impl<const N: usize> BlockContainer for [u8; N] {
160    type Data = Self;
161    type ShareableData = ();
162
163    /// The number of bytes in the buffer.
164    #[inline]
165    fn len(&self) -> usize {
166        self.as_slice().len()
167    }
168}
169
170impl<const N: usize> ReadBytes for [u8; N] {
171    #[inline]
172    fn get_slice_at(&self, offset: usize, size: usize) -> Option<&[u8]> {
173        self.as_slice().get_slice_at(offset, size)
174    }
175}
176
177/// Trait implemented by an Inspect container that can be written to.
178impl<const N: usize> WriteBytes for [u8; N] {
179    #[inline]
180    fn get_slice_mut_at(&mut self, offset: usize, size: usize) -> Option<&mut [u8]> {
181        if offset >= self.len() {
182            return None;
183        }
184        let upper_bound = offset.checked_add(size)?;
185        if upper_bound > self.len() {
186            return None;
187        }
188        Some(&mut self[offset..upper_bound])
189    }
190}