bp3d_proto/message/util/
array.rs

1// Copyright (c) 2024, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above copyright notice,
11//       this list of conditions and the following disclaimer in the documentation
12//       and/or other materials provided with the distribution.
13//     * Neither the name of BlockProject 3D nor the names of its contributors
14//       may be used to endorse or promote products derived from this software
15//       without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29use crate::message::payload::list_base::impl_list_base;
30use crate::message::WriteTo;
31use crate::message::{Error, FromBytes, Message};
32use crate::util::{Size, ToUsize, Wrap};
33use std::marker::PhantomData;
34use std::ops::{Index, IndexMut};
35use std::slice::{Chunks, ChunksMut};
36
37pub struct IterMut<'a, Item> {
38    data: ChunksMut<'a, u8>,
39    useless: PhantomData<Item>,
40}
41
42impl<'a, Item> IterMut<'a, Item> {
43    pub fn new(chunks: ChunksMut<'a, u8>) -> Self {
44        Self {
45            data: chunks,
46            useless: PhantomData,
47        }
48    }
49}
50
51impl<'a, Item: Wrap<&'a mut [u8]>> Iterator for IterMut<'a, Item> {
52    type Item = Item;
53
54    fn next(&mut self) -> Option<Self::Item> {
55        self.data.next().map(|v| unsafe { Item::wrap_unchecked(v) })
56    }
57}
58
59pub struct Iter<'a, Item> {
60    data: Chunks<'a, u8>,
61    useless: PhantomData<Item>,
62}
63
64impl<'a, Item> Iter<'a, Item> {
65    pub fn new(chunks: Chunks<'a, u8>) -> Self {
66        Self {
67            data: chunks,
68            useless: PhantomData,
69        }
70    }
71}
72
73impl<'a, Item: Wrap<&'a [u8]>> Iterator for Iter<'a, Item> {
74    type Item = Item;
75
76    fn next(&mut self) -> Option<Self::Item> {
77        self.data.next().map(|v| unsafe { Item::wrap_unchecked(v) })
78    }
79}
80
81#[derive(Copy, Clone, Debug)]
82pub struct Array<B, T, Item> {
83    data: B,
84    len: usize,
85    useless: PhantomData<T>,
86    useless1: PhantomData<Item>,
87}
88
89impl<B, T, Item> Array<B, T, Item> {
90    /// Creates an array from raw parts.
91    /// This function assumes that data has the number of items specified by len.
92    ///
93    /// # Arguments
94    ///
95    /// * `data`: the data buffer.
96    /// * `len`: the number of items to be read from the buffer.
97    ///
98    /// # Safety
99    ///
100    /// A wrong length could result in UB where the array iterator, getter or setter attempts to
101    /// slice out of bounds with a future optimization in release builds, currently it will only
102    /// result in a panic.
103    pub unsafe fn from_raw_parts(data: B, len: usize) -> Array<B, T, Item> {
104        Array {
105            data,
106            len,
107            useless: PhantomData,
108            useless1: PhantomData,
109        }
110    }
111}
112
113impl<B: AsRef<[u8]>, T, Item: Size> Array<B, T, Item> {
114    pub fn new(data: B) -> Array<B, T, Item> {
115        let len = data.as_ref().len() / Item::SIZE;
116        unsafe { Array::from_raw_parts(data, len) }
117    }
118}
119
120impl_list_base!(Array);
121
122impl<B: AsRef<[u8]>, T, Item> Array<B, T, Item> {
123    pub fn to_ref<Item1>(&self) -> Array<&[u8], T, Item1> {
124        unsafe { Array::from_raw_parts(self.data.as_ref(), self.len) }
125    }
126}
127
128impl<B: AsRef<[u8]>, T, Item: Size> Array<B, T, Item> {
129    pub fn from_parts(data: B, len: usize) -> Option<Array<B, T, Item>> {
130        match data.as_ref().len() == len * Item::SIZE {
131            true => Some(unsafe { Array::from_raw_parts(data, len) }),
132            false => None,
133        }
134    }
135}
136
137impl<'a, B: AsRef<[u8]>, T, Item: Size> Array<B, T, Item> {
138    pub fn get(&'a self, index: usize) -> Option<&'a [u8]> {
139        if index >= self.len {
140            None
141        } else {
142            let start = index * Item::SIZE;
143            let end = start + Item::SIZE;
144            Some(&self.data.as_ref()[start..end])
145        }
146    }
147
148    pub fn iter(&'a self) -> Chunks<'a, u8> {
149        self.data.as_ref().chunks(Item::SIZE)
150    }
151}
152
153impl<'a, B: AsMut<[u8]>, T, Item: Size> Array<B, T, Item> {
154    pub fn get_mut(&'a mut self, index: usize) -> Option<&'a mut [u8]> {
155        if index >= self.len {
156            None
157        } else {
158            let start = index * Item::SIZE;
159            let end = start + Item::SIZE;
160            Some(&mut self.data.as_mut()[start..end])
161        }
162    }
163
164    pub fn iter_mut(&'a mut self) -> ChunksMut<'a, u8> {
165        self.data.as_mut().chunks_mut(Item::SIZE)
166    }
167}
168
169impl<B: AsRef<[u8]>, T, Item: Size> Index<usize> for Array<B, T, Item> {
170    type Output = [u8];
171
172    fn index(&self, index: usize) -> &Self::Output {
173        if index >= self.len {
174            panic!("attempt to index item out of bounds, index={}, len={}", index, self.len)
175        }
176        let start = index * Item::SIZE;
177        let end = start + Item::SIZE;
178        &self.data.as_ref()[start..end]
179    }
180}
181
182impl<B: AsRef<[u8]> + AsMut<[u8]>, T, Item: Size> IndexMut<usize> for Array<B, T, Item> {
183    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
184        if index >= self.len {
185            panic!("attempt to index item out of bounds, index={}, len={}", index, self.len)
186        }
187        let start = index * Item::SIZE;
188        let end = start + Item::SIZE;
189        &mut self.data.as_mut()[start..end]
190    }
191}
192
193impl<'a, T: FromBytes<'a, Output: ToUsize>, Item: Size> FromBytes<'a> for Array<&'a [u8], T, Item> {
194    type Output = Array<&'a [u8], T, Item>;
195
196    fn from_bytes(slice: &'a [u8]) -> Result<Message<Self::Output>, Error> {
197        let msg = T::from_bytes(slice)?;
198        let control_size = msg.size();
199        let len = msg.into_inner().to_usize();
200        let total_size = control_size + (len * Item::SIZE);
201        if slice.len() < total_size {
202            Err(Error::Truncated)
203        } else {
204            let data = &slice[control_size..total_size];
205            Ok(Message::new(
206                total_size,
207                Array {
208                    data,
209                    len,
210                    useless: PhantomData,
211                    useless1: PhantomData,
212                },
213            ))
214        }
215    }
216}