Skip to main content

capnp/private/
arena.rs

1// Copyright (c) 2013-2017 Sandstorm Development Group, Inc. and contributors
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21use core::slice;
22
23use crate::message;
24use crate::message::Allocator;
25use crate::message::ReaderSegments;
26use crate::private::read_limiter::ReadLimiter;
27use crate::private::units::*;
28use crate::OutputSegments;
29use crate::{Error, ErrorKind, Result};
30
31pub type SegmentId = u32;
32
33pub unsafe trait ReaderArena {
34    // return pointer to start of segment, and number of words in that segment
35    fn get_segment(&self, id: u32) -> Result<(*const u8, u32)>;
36
37    unsafe fn check_offset(
38        &self,
39        segment_id: u32,
40        start: *const u8,
41        offset_in_words: i32,
42    ) -> Result<*const u8> {
43        let (segment_start, segment_len) = self.get_segment(segment_id)?;
44        let this_start: usize = segment_start as usize;
45        let this_size: usize = segment_len as usize * BYTES_PER_WORD;
46        let offset: i64 = i64::from(offset_in_words) * BYTES_PER_WORD as i64;
47        let start_idx = start as usize;
48        if start_idx < this_start || ((start_idx - this_start) as i64 + offset) as usize > this_size
49        {
50            Err(Error::from_kind(
51                ErrorKind::MessageContainsOutOfBoundsPointer,
52            ))
53        } else {
54            unsafe { Ok(start.offset(offset as isize)) }
55        }
56    }
57
58    fn contains_interval(&self, segment_id: u32, start: *const u8, size: usize) -> Result<()>;
59    fn amplified_read(&self, virtual_amount: u64) -> Result<()>;
60
61    fn nesting_limit(&self) -> i32;
62
63    fn size_in_words(&self) -> usize;
64
65    // TODO(apibump): Consider putting extract_cap(), inject_cap(), drop_cap() here
66    //   and on message::Reader. Then we could get rid of Imbue and ImbueMut, and
67    //   layout::StructReader, layout::ListReader, etc. could drop their `cap_table` fields.
68}
69
70pub struct ReaderArenaImpl<S> {
71    segments: S,
72    read_limiter: ReadLimiter,
73    nesting_limit: i32,
74}
75
76#[cfg(feature = "sync_reader")]
77fn _assert_sync() {
78    fn _assert_sync<T: Sync>() {}
79    fn _assert_reader<S: ReaderSegments + Sync>() {
80        _assert_sync::<ReaderArenaImpl<S>>();
81    }
82}
83
84impl<S> ReaderArenaImpl<S>
85where
86    S: ReaderSegments,
87{
88    pub fn new(segments: S, options: message::ReaderOptions) -> Self {
89        let limiter = ReadLimiter::new(options.traversal_limit_in_words);
90        Self {
91            segments,
92            read_limiter: limiter,
93            nesting_limit: options.nesting_limit,
94        }
95    }
96
97    pub fn into_segments(self) -> S {
98        self.segments
99    }
100
101    pub(crate) fn get_segments(&self) -> &S {
102        &self.segments
103    }
104}
105
106unsafe impl<S> ReaderArena for ReaderArenaImpl<S>
107where
108    S: ReaderSegments,
109{
110    fn get_segment(&self, id: u32) -> Result<(*const u8, u32)> {
111        match self.segments.get_segment(id) {
112            Some(seg) => {
113                #[cfg(not(feature = "unaligned"))]
114                {
115                    if seg.as_ptr() as usize % BYTES_PER_WORD != 0 {
116                        return Err(Error::from_kind(ErrorKind::UnalignedSegment));
117                    }
118                }
119
120                Ok((seg.as_ptr(), (seg.len() / BYTES_PER_WORD) as u32))
121            }
122            None => Err(Error::from_kind(ErrorKind::InvalidSegmentId(id))),
123        }
124    }
125
126    fn contains_interval(&self, id: u32, start: *const u8, size_in_words: usize) -> Result<()> {
127        let (segment_start, segment_len) = self.get_segment(id)?;
128        let this_start: usize = segment_start as usize;
129        let this_size: usize = segment_len as usize * BYTES_PER_WORD;
130        let start = start as usize;
131        let size = size_in_words * BYTES_PER_WORD;
132
133        if !(start >= this_start && start - this_start + size <= this_size) {
134            Err(Error::from_kind(
135                ErrorKind::MessageContainsOutOfBoundsPointer,
136            ))
137        } else {
138            self.read_limiter.can_read(size_in_words)
139        }
140    }
141
142    fn amplified_read(&self, virtual_amount: u64) -> Result<()> {
143        self.read_limiter.can_read(virtual_amount as usize)
144    }
145
146    fn nesting_limit(&self) -> i32 {
147        self.nesting_limit
148    }
149
150    fn size_in_words(&self) -> usize {
151        let mut result = 0;
152        for ii in 0..self.segments.len() {
153            if let Some(seg) = self.segments.get_segment(ii as u32) {
154                result += seg.len() / BYTES_PER_WORD;
155            }
156        }
157        result
158    }
159}
160
161pub unsafe trait BuilderArena: ReaderArena {
162    fn allocate(&mut self, segment_id: u32, amount: WordCount32) -> Option<u32>;
163    fn allocate_anywhere(&mut self, amount: u32) -> (SegmentId, u32);
164    fn get_segment_mut(&mut self, id: u32) -> (*mut u8, u32);
165
166    fn as_reader(&self) -> &dyn ReaderArena;
167}
168
169/// A wrapper around a memory segment used in building a message.
170struct BuilderSegment {
171    /// Pointer to the start of the segment.
172    ptr: *mut u8,
173
174    /// Total number of words the segment could potentially use. That is, all
175    /// bytes from `ptr` to `ptr + (capacity * 8)` may be used in the segment.
176    capacity: u32,
177
178    /// Number of words already used in the segment.
179    allocated: u32,
180}
181
182#[cfg(feature = "alloc")]
183type BuilderSegmentArray = alloc::vec::Vec<BuilderSegment>;
184
185#[cfg(not(feature = "alloc"))]
186#[derive(Default)]
187struct BuilderSegmentArray {
188    // In the no-alloc case, we only allow a single segment.
189    segment: Option<BuilderSegment>,
190}
191
192#[cfg(not(feature = "alloc"))]
193impl BuilderSegmentArray {
194    fn len(&self) -> usize {
195        match self.segment {
196            Some(_) => 1,
197            None => 0,
198        }
199    }
200
201    fn push(&mut self, segment: BuilderSegment) {
202        if self.segment.is_some() {
203            panic!("multiple segments are not supported in no-alloc mode")
204        }
205        self.segment = Some(segment);
206    }
207}
208
209#[cfg(not(feature = "alloc"))]
210impl core::ops::Index<usize> for BuilderSegmentArray {
211    type Output = BuilderSegment;
212
213    fn index(&self, index: usize) -> &Self::Output {
214        assert_eq!(index, 0);
215        match &self.segment {
216            Some(s) => s,
217            None => panic!("no segment"),
218        }
219    }
220}
221
222#[cfg(not(feature = "alloc"))]
223impl core::ops::IndexMut<usize> for BuilderSegmentArray {
224    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
225        assert_eq!(index, 0);
226        match &mut self.segment {
227            Some(s) => s,
228            None => panic!("no segment"),
229        }
230    }
231}
232
233pub struct BuilderArenaImplInner<A>
234where
235    A: Allocator,
236{
237    allocator: Option<A>, // None if has already been deallocated.
238    segments: BuilderSegmentArray,
239}
240
241pub struct BuilderArenaImpl<A>
242where
243    A: Allocator,
244{
245    inner: BuilderArenaImplInner<A>,
246}
247
248// BuilderArenaImpl has no interior mutability. Adding these impls
249// allows message::Builder<A> to be Send and/or Sync when appropriate.
250unsafe impl<A> Send for BuilderArenaImpl<A> where A: Send + Allocator {}
251unsafe impl<A> Sync for BuilderArenaImpl<A> where A: Sync + Allocator {}
252
253impl<A> BuilderArenaImpl<A>
254where
255    A: Allocator,
256{
257    pub fn new(allocator: A) -> Self {
258        Self {
259            inner: BuilderArenaImplInner {
260                allocator: Some(allocator),
261                segments: Default::default(),
262            },
263        }
264    }
265
266    /// Allocates a new segment with capacity for at least `minimum_size` words.
267    pub fn allocate_segment(&mut self, minimum_size: u32) -> Result<()> {
268        self.inner.allocate_segment(minimum_size)
269    }
270
271    pub fn get_segments_for_output(&self) -> OutputSegments<'_> {
272        let reff = &self.inner;
273        if reff.segments.len() == 1 {
274            let seg = &reff.segments[0];
275
276            // The user must mutably borrow the `message::Builder` to be able to modify segment memory.
277            // No such borrow will be possible while `self` is still immutably borrowed from this method,
278            // so returning this slice is safe.
279            let slice = unsafe {
280                slice::from_raw_parts(seg.ptr as *const _, seg.allocated as usize * BYTES_PER_WORD)
281            };
282            OutputSegments::SingleSegment([slice])
283        } else {
284            #[cfg(feature = "alloc")]
285            {
286                let mut v = alloc::vec::Vec::with_capacity(reff.segments.len());
287                for seg in &reff.segments {
288                    // See safety argument in above branch.
289                    let slice = unsafe {
290                        slice::from_raw_parts(
291                            seg.ptr as *const _,
292                            seg.allocated as usize * BYTES_PER_WORD,
293                        )
294                    };
295                    v.push(slice);
296                }
297                OutputSegments::MultiSegment(v)
298            }
299            #[cfg(not(feature = "alloc"))]
300            {
301                panic!("invalid number of segments: {}", reff.segments.len());
302            }
303        }
304    }
305
306    pub fn len(&self) -> usize {
307        self.inner.segments.len()
308    }
309
310    pub fn is_empty(&self) -> bool {
311        self.len() == 0
312    }
313
314    /// Retrieves the underlying `Allocator`, deallocating all currently-allocated
315    /// segments.
316    pub fn into_allocator(mut self) -> A {
317        self.inner.deallocate_all();
318        self.inner.allocator.take().unwrap()
319    }
320}
321
322unsafe impl<A> ReaderArena for BuilderArenaImpl<A>
323where
324    A: Allocator,
325{
326    fn get_segment(&self, id: u32) -> Result<(*const u8, u32)> {
327        let seg = &self.inner.segments[id as usize];
328        Ok((seg.ptr, seg.allocated))
329    }
330
331    unsafe fn check_offset(
332        &self,
333        _segment_id: u32,
334        start: *const u8,
335        offset_in_words: i32,
336    ) -> Result<*const u8> {
337        unsafe { Ok(start.offset((i64::from(offset_in_words) * BYTES_PER_WORD as i64) as isize)) }
338    }
339
340    fn contains_interval(&self, _id: u32, _start: *const u8, _size: usize) -> Result<()> {
341        Ok(())
342    }
343
344    fn amplified_read(&self, _virtual_amount: u64) -> Result<()> {
345        Ok(())
346    }
347
348    fn nesting_limit(&self) -> i32 {
349        0x7fffffff
350    }
351
352    fn size_in_words(&self) -> usize {
353        let mut result = 0;
354        for ii in 0..self.inner.segments.len() {
355            result += self.inner.segments[ii].allocated as usize
356        }
357        result
358    }
359}
360
361impl<A> BuilderArenaImplInner<A>
362where
363    A: Allocator,
364{
365    /// Allocates a new segment with capacity for at least `minimum_size` words.
366    fn allocate_segment(&mut self, minimum_size: WordCount32) -> Result<()> {
367        let seg = match &mut self.allocator {
368            Some(a) => a.allocate_segment(minimum_size),
369            None => unreachable!(),
370        };
371        self.segments.push(BuilderSegment {
372            ptr: seg.0,
373            capacity: seg.1,
374            allocated: 0,
375        });
376        Ok(())
377    }
378
379    fn allocate(&mut self, segment_id: u32, amount: WordCount32) -> Option<u32> {
380        let seg = &mut self.segments[segment_id as usize];
381        if amount > seg.capacity - seg.allocated {
382            None
383        } else {
384            let result = seg.allocated;
385            seg.allocated += amount;
386            Some(result)
387        }
388    }
389
390    fn allocate_anywhere(&mut self, amount: u32) -> (SegmentId, u32) {
391        // first try the existing segments, then try allocating a new segment.
392        let allocated_len = self.segments.len() as u32;
393        for segment_id in 0..allocated_len {
394            if let Some(idx) = self.allocate(segment_id, amount) {
395                return (segment_id, idx);
396            }
397        }
398
399        // Need to allocate a new segment.
400
401        self.allocate_segment(amount).expect("allocate new segment");
402        (
403            allocated_len,
404            self.allocate(allocated_len, amount)
405                .expect("use freshly-allocated segment"),
406        )
407    }
408
409    fn deallocate_all(&mut self) {
410        if let Some(a) = &mut self.allocator {
411            #[cfg(feature = "alloc")]
412            for seg in &self.segments {
413                unsafe {
414                    a.deallocate_segment(seg.ptr, seg.capacity, seg.allocated);
415                }
416            }
417
418            #[cfg(not(feature = "alloc"))]
419            if let Some(seg) = &self.segments.segment {
420                unsafe {
421                    a.deallocate_segment(seg.ptr, seg.capacity, seg.allocated);
422                }
423            }
424        }
425    }
426
427    fn get_segment_mut(&mut self, id: u32) -> (*mut u8, u32) {
428        let seg = &self.segments[id as usize];
429        (seg.ptr, seg.capacity)
430    }
431}
432
433unsafe impl<A> BuilderArena for BuilderArenaImpl<A>
434where
435    A: Allocator,
436{
437    fn allocate(&mut self, segment_id: u32, amount: WordCount32) -> Option<u32> {
438        self.inner.allocate(segment_id, amount)
439    }
440
441    fn allocate_anywhere(&mut self, amount: u32) -> (SegmentId, u32) {
442        self.inner.allocate_anywhere(amount)
443    }
444
445    fn get_segment_mut(&mut self, id: u32) -> (*mut u8, u32) {
446        self.inner.get_segment_mut(id)
447    }
448
449    fn as_reader(&self) -> &dyn ReaderArena {
450        self
451    }
452}
453
454impl<A> Drop for BuilderArenaImplInner<A>
455where
456    A: Allocator,
457{
458    fn drop(&mut self) {
459        self.deallocate_all()
460    }
461}
462
463pub struct NullArena;
464
465unsafe impl ReaderArena for NullArena {
466    fn get_segment(&self, _id: u32) -> Result<(*const u8, u32)> {
467        Err(Error::from_kind(ErrorKind::TriedToReadFromNullArena))
468    }
469
470    unsafe fn check_offset(
471        &self,
472        _segment_id: u32,
473        start: *const u8,
474        offset_in_words: i32,
475    ) -> Result<*const u8> {
476        let offset_in_bytes = (offset_in_words as i64) * BYTES_PER_WORD as i64;
477        unsafe { Ok(start.offset(offset_in_bytes as isize)) }
478    }
479
480    fn contains_interval(&self, _id: u32, _start: *const u8, _size: usize) -> Result<()> {
481        Ok(())
482    }
483
484    fn amplified_read(&self, _virtual_amount: u64) -> Result<()> {
485        Ok(())
486    }
487
488    fn nesting_limit(&self) -> i32 {
489        0x7fffffff
490    }
491
492    fn size_in_words(&self) -> usize {
493        0
494    }
495}
496
497/// An arena designed for the specific case of reading messages from single-segment
498/// `Word` arrays in generated code, including constants and raw schema nodes. Performs
499/// bounds checking, so its constructor does not need to be marked `unsafe`. Does
500/// *not* enforce a read limit or a nesting limit.
501pub struct GeneratedCodeArena {
502    words: &'static [crate::Word],
503}
504
505impl GeneratedCodeArena {
506    pub const fn new(words: &'static [crate::Word]) -> Self {
507        assert!((words.len() as u64) < u32::MAX as u64);
508        Self { words }
509    }
510}
511
512unsafe impl ReaderArena for GeneratedCodeArena {
513    fn get_segment(&self, id: u32) -> Result<(*const u8, u32)> {
514        if id == 0 {
515            Ok((self.words.as_ptr() as *const _, self.words.len() as u32))
516        } else {
517            Err(Error::from_kind(ErrorKind::InvalidSegmentId(id)))
518        }
519    }
520
521    fn contains_interval(&self, id: u32, start: *const u8, size_in_words: usize) -> Result<()> {
522        let (segment_start, segment_len) = self.get_segment(id)?;
523        let this_start: usize = segment_start as usize;
524        let this_size: usize = segment_len as usize * BYTES_PER_WORD;
525        let start = start as usize;
526        let size = size_in_words * BYTES_PER_WORD;
527
528        if !(start >= this_start && start - this_start + size <= this_size) {
529            Err(Error::from_kind(
530                ErrorKind::MessageContainsOutOfBoundsPointer,
531            ))
532        } else {
533            Ok(())
534        }
535    }
536
537    fn amplified_read(&self, _virtual_amount: u64) -> Result<()> {
538        Ok(())
539    }
540
541    fn nesting_limit(&self) -> i32 {
542        0x7fffffff
543    }
544
545    fn size_in_words(&self) -> usize {
546        self.words.len()
547    }
548}