Skip to main content

capnp/
primitive_list.rs

1// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22//! List of primitives.
23
24use core::marker;
25
26use crate::introspect;
27use crate::private::layout::{
28    data_bits_per_element, ElementSize, ListBuilder, ListReader, PointerBuilder, PointerReader,
29    PrimitiveElement,
30};
31use crate::traits::{FromPointerBuilder, FromPointerReader, IndexMove, ListIter};
32use crate::Result;
33
34#[derive(Clone, Copy)]
35pub struct Owned<T> {
36    marker: marker::PhantomData<T>,
37}
38
39impl<T> introspect::Introspect for Owned<T>
40where
41    T: introspect::Introspect,
42{
43    fn introspect() -> introspect::Type {
44        introspect::Type::list_of(T::introspect())
45    }
46}
47
48impl<T> crate::traits::Owned for Owned<T>
49where
50    T: PrimitiveElement + introspect::Introspect,
51{
52    type Reader<'a> = Reader<'a, T>;
53    type Builder<'a> = Builder<'a, T>;
54}
55
56#[derive(Clone, Copy)]
57pub struct Reader<'a, T>
58where
59    T: PrimitiveElement,
60{
61    marker: marker::PhantomData<T>,
62    reader: ListReader<'a>,
63}
64
65impl<'a, T: PrimitiveElement> Reader<'a, T> {
66    pub fn len(&self) -> u32 {
67        self.reader.len()
68    }
69
70    pub fn is_empty(&self) -> bool {
71        self.len() == 0
72    }
73
74    pub fn iter(self) -> ListIter<Reader<'a, T>, T> {
75        let l = self.len();
76        ListIter::new(self, l)
77    }
78}
79
80impl<'a, T: PrimitiveElement> FromPointerReader<'a> for Reader<'a, T> {
81    fn get_from_pointer(
82        reader: &PointerReader<'a>,
83        default: Option<&'a [crate::Word]>,
84    ) -> Result<Reader<'a, T>> {
85        Ok(Reader {
86            reader: reader.get_list(T::element_size(), default)?,
87            marker: marker::PhantomData,
88        })
89    }
90}
91
92impl<T: PrimitiveElement> IndexMove<u32, T> for Reader<'_, T> {
93    fn index_move(&self, index: u32) -> T {
94        self.get(index)
95    }
96}
97
98impl<T: PrimitiveElement> Reader<'_, T> {
99    /// Gets the `T` at position `index`. Panics if `index` is greater than or
100    /// equal to `len()`.
101    pub fn get(&self, index: u32) -> T {
102        assert!(index < self.len());
103        PrimitiveElement::get(&self.reader, index)
104    }
105
106    /// Gets the `T` at position `index`. Returns `None` if `index`
107    /// is greater than or equal to `len()`.
108    pub fn try_get(&self, index: u32) -> Option<T> {
109        if index < self.len() {
110            Some(PrimitiveElement::get(&self.reader, index))
111        } else {
112            None
113        }
114    }
115
116    const _CHECK_SLICE: () = check_slice_supported::<T>();
117
118    /// Attempts to return a view of the list as a native Rust slice.
119    ///
120    /// Returns `None` if either:
121    ///  * The elements of the list are non-contiguous, which can happen if the
122    ///    schema has evolved.
123    ///  * The elements of the list are bit-sized, i.e. T is bool. In the case, the
124    ///    list is bit-packed, and therefore differs in representation from Rust's
125    ///    `&[bool]`.
126    ///
127    /// This method raises a compile-time error if `T` is larger than one
128    /// byte and either the `unaligned` feature is enabled or the target
129    /// is big-endian.
130    pub fn as_slice(&self) -> Option<&[T]> {
131        let () = Self::_CHECK_SLICE;
132        if self.reader.get_element_size() == ElementSize::Bit {
133            // TODO: make this a comple-time check.
134            return None;
135        }
136        if self.reader.get_element_size() == T::element_size() {
137            let bytes = self.reader.into_raw_bytes();
138            let bits_per_element = data_bits_per_element(T::element_size()) as usize;
139            let slice_length = if bits_per_element > 0 {
140                8 * bytes.len() / bits_per_element
141            } else {
142                // This is a List(Void).
143                self.len() as usize
144            };
145            if slice_length == 0 {
146                Some(&[])
147            } else {
148                Some(unsafe {
149                    core::slice::from_raw_parts(bytes.as_ptr() as *const T, slice_length)
150                })
151            }
152        } else {
153            None
154        }
155    }
156}
157
158const fn check_slice_supported<T: PrimitiveElement>() {
159    if core::mem::size_of::<T>() > 1 {
160        if !cfg!(target_endian = "little") {
161            panic!("cannot call as_slice on primitive list of multi-byte elements on non-little endian targets");
162        }
163        if cfg!(feature = "unaligned") {
164            panic!("cannot call as_slice on primitive list of multi-byte elements when unaligned feature is enabled");
165        }
166    }
167}
168
169impl<'a, T> crate::traits::IntoInternalListReader<'a> for Reader<'a, T>
170where
171    T: PrimitiveElement,
172{
173    fn into_internal_list_reader(self) -> ListReader<'a> {
174        self.reader
175    }
176}
177
178pub struct Builder<'a, T>
179where
180    T: PrimitiveElement,
181{
182    marker: marker::PhantomData<T>,
183    builder: ListBuilder<'a>,
184}
185
186impl<'a, T> Builder<'a, T>
187where
188    T: PrimitiveElement,
189{
190    pub fn len(&self) -> u32 {
191        self.builder.len()
192    }
193
194    pub fn is_empty(&self) -> bool {
195        self.len() == 0
196    }
197
198    pub fn into_reader(self) -> Reader<'a, T> {
199        Reader {
200            marker: marker::PhantomData,
201            reader: self.builder.into_reader(),
202        }
203    }
204
205    pub fn set(&mut self, index: u32, value: T) {
206        assert!(index < self.len());
207        PrimitiveElement::set(&self.builder, index, value);
208    }
209
210    const _CHECK_SLICE: () = check_slice_supported::<T>();
211
212    /// Attempts to return a view of the list as a native Rust slice.
213    ///
214    /// Returns `None` if either:
215    ///  * The elements of the list are non-contiguous, which can happen if the
216    ///    schema has evolved.
217    ///  * The elements of the list are bit-sized, i.e. T is bool. In the case, the
218    ///    list is bit-packed, and therefore differs in representation from Rust's
219    ///    `&[bool]`.
220    ///
221    /// This method raises a compile-time error if `T` is larger than one
222    /// byte and either the `unaligned` feature is enabled or the target
223    /// is big-endian.
224    pub fn as_slice(&mut self) -> Option<&mut [T]> {
225        let () = Self::_CHECK_SLICE;
226        if self.builder.get_element_size() == ElementSize::Bit {
227            // TODO: make this a comple-time check.
228            return None;
229        }
230        if self.builder.get_element_size() == T::element_size() {
231            let bytes = self.builder.as_raw_bytes();
232            let bits_per_element = data_bits_per_element(T::element_size()) as usize;
233            let slice_length = if bits_per_element > 0 {
234                8 * bytes.len() / bits_per_element
235            } else {
236                // This is a List(Void).
237                self.len() as usize
238            };
239            if slice_length == 0 {
240                Some(&mut [])
241            } else {
242                Some(unsafe {
243                    core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut T, slice_length)
244                })
245            }
246        } else {
247            None
248        }
249    }
250}
251
252impl<'a, T: PrimitiveElement> FromPointerBuilder<'a> for Builder<'a, T> {
253    fn init_pointer(builder: PointerBuilder<'a>, size: u32) -> Builder<'a, T> {
254        Builder {
255            builder: builder.init_list(T::element_size(), size),
256            marker: marker::PhantomData,
257        }
258    }
259    fn get_from_pointer(
260        builder: PointerBuilder<'a>,
261        default: Option<&'a [crate::Word]>,
262    ) -> Result<Builder<'a, T>> {
263        Ok(Builder {
264            builder: builder.get_list(T::element_size(), default)?,
265            marker: marker::PhantomData,
266        })
267    }
268}
269
270impl<T: PrimitiveElement> Builder<'_, T> {
271    /// Gets the `T` at position `index`. Panics if `index` is greater than or
272    /// equal to `len()`.
273    pub fn get(&self, index: u32) -> T {
274        assert!(index < self.len());
275        PrimitiveElement::get_from_builder(&self.builder, index)
276    }
277
278    /// Gets the `T` at position `index`. Returns `None` if `index`
279    /// is greater than or equal to `len()`.
280    pub fn try_get(&self, index: u32) -> Option<T> {
281        if index < self.len() {
282            Some(PrimitiveElement::get_from_builder(&self.builder, index))
283        } else {
284            None
285        }
286    }
287
288    pub fn reborrow(&mut self) -> Builder<'_, T> {
289        Builder {
290            marker: marker::PhantomData,
291            builder: self.builder.reborrow(),
292        }
293    }
294}
295
296impl<'a, T> crate::traits::SetterInput<Owned<T>> for Reader<'a, T>
297where
298    T: PrimitiveElement,
299{
300    #[inline]
301    fn set_pointer_builder<'b>(
302        mut pointer: PointerBuilder<'b>,
303        value: Reader<'a, T>,
304        canonicalize: bool,
305    ) -> Result<()> {
306        pointer.set_list(&value.reader, canonicalize)
307    }
308}
309
310impl<'a, T> crate::traits::SetterInput<Owned<T>> for &'a [T]
311where
312    T: PrimitiveElement + Copy,
313{
314    #[inline]
315    fn set_pointer_builder<'b>(
316        pointer: PointerBuilder<'b>,
317        value: &'a [T],
318        _canonicalize: bool,
319    ) -> Result<()> {
320        let builder = pointer.init_list(
321            <T as PrimitiveElement>::element_size(),
322            value
323                .len()
324                .try_into()
325                .expect("list size too large to fit in a u32"),
326        );
327        for (idx, v) in value.iter().enumerate() {
328            PrimitiveElement::set(&builder, u32::try_from(idx).unwrap(), *v)
329        }
330        Ok(())
331    }
332}
333
334impl<'a, T, const N: usize> crate::traits::SetterInput<Owned<T>> for &'a [T; N]
335where
336    T: PrimitiveElement + Copy,
337{
338    #[inline]
339    fn set_pointer_builder<'b>(
340        pointer: PointerBuilder<'b>,
341        value: &'a [T; N],
342        canonicalize: bool,
343    ) -> Result<()> {
344        crate::traits::SetterInput::set_pointer_builder(pointer, &value[..], canonicalize)
345    }
346}
347
348impl<'a, T> ::core::iter::IntoIterator for Reader<'a, T>
349where
350    T: PrimitiveElement,
351{
352    type Item = T;
353    type IntoIter = ListIter<Reader<'a, T>, Self::Item>;
354
355    fn into_iter(self) -> Self::IntoIter {
356        self.iter()
357    }
358}
359
360impl<'a, T: PrimitiveElement + crate::introspect::Introspect> From<Reader<'a, T>>
361    for crate::dynamic_value::Reader<'a>
362{
363    fn from(t: Reader<'a, T>) -> crate::dynamic_value::Reader<'a> {
364        crate::dynamic_value::Reader::List(crate::dynamic_list::Reader::new(
365            t.reader,
366            T::introspect(),
367        ))
368    }
369}
370
371impl<'a, T: PrimitiveElement + crate::introspect::Introspect>
372    crate::dynamic_value::DowncastReader<'a> for Reader<'a, T>
373{
374    fn downcast_reader(v: crate::dynamic_value::Reader<'a>) -> Self {
375        let dl: crate::dynamic_list::Reader = v.downcast();
376        assert!(dl.element_type().loose_equals(T::introspect()));
377        Reader {
378            reader: dl.reader,
379            marker: marker::PhantomData,
380        }
381    }
382}
383
384impl<'a, T: PrimitiveElement + crate::introspect::Introspect> From<Builder<'a, T>>
385    for crate::dynamic_value::Builder<'a>
386{
387    fn from(t: Builder<'a, T>) -> crate::dynamic_value::Builder<'a> {
388        crate::dynamic_value::Builder::List(crate::dynamic_list::Builder::new(
389            t.builder,
390            T::introspect(),
391        ))
392    }
393}
394
395impl<'a, T: PrimitiveElement + crate::introspect::Introspect>
396    crate::dynamic_value::DowncastBuilder<'a> for Builder<'a, T>
397{
398    fn downcast_builder(v: crate::dynamic_value::Builder<'a>) -> Self {
399        let dl: crate::dynamic_list::Builder = v.downcast();
400        assert!(dl.element_type().loose_equals(T::introspect()));
401        Builder {
402            builder: dl.builder,
403            marker: marker::PhantomData,
404        }
405    }
406}
407
408impl<T: Copy + PrimitiveElement + crate::introspect::Introspect> core::fmt::Debug
409    for Reader<'_, T>
410{
411    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
412        core::fmt::Debug::fmt(&crate::dynamic_value::Reader::from(*self), f)
413    }
414}