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