/* * Copyright 2018 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use core::marker::PhantomData; use core::mem::size_of; use core::ops::Deref; use crate::endian_scalar::{emplace_scalar, read_scalar, read_scalar_at}; use crate::follow::Follow; use crate::push::Push; pub const FLATBUFFERS_MAX_BUFFER_SIZE: usize = (1u64 << 31) as usize; pub const FILE_IDENTIFIER_LENGTH: usize = 4; pub const VTABLE_METADATA_FIELDS: usize = 2; pub const SIZE_U8: usize = size_of::(); pub const SIZE_I8: usize = size_of::(); pub const SIZE_U16: usize = size_of::(); pub const SIZE_I16: usize = size_of::(); pub const SIZE_U32: usize = size_of::(); pub const SIZE_I32: usize = size_of::(); pub const SIZE_U64: usize = size_of::(); pub const SIZE_I64: usize = size_of::(); pub const SIZE_F32: usize = size_of::(); pub const SIZE_F64: usize = size_of::(); pub const SIZE_SOFFSET: usize = SIZE_I32; pub const SIZE_UOFFSET: usize = SIZE_U32; pub const SIZE_VOFFSET: usize = SIZE_I16; pub const SIZE_SIZEPREFIX: usize = SIZE_UOFFSET; /// SOffsetT is a relative pointer from tables to their vtables. pub type SOffsetT = i32; /// UOffsetT is used represent both for relative pointers and lengths of vectors. pub type UOffsetT = u32; /// VOffsetT is a relative pointer in vtables to point from tables to field data. pub type VOffsetT = u16; /// TableFinishedWIPOffset marks a WIPOffset as being for a finished table. #[derive(Clone, Copy)] pub struct TableFinishedWIPOffset {} /// TableUnfinishedWIPOffset marks a WIPOffset as being for an unfinished table. #[derive(Clone, Copy)] pub struct TableUnfinishedWIPOffset {} /// UnionWIPOffset marks a WIPOffset as being for a union value. #[derive(Clone, Copy)] pub struct UnionWIPOffset {} /// VTableWIPOffset marks a WIPOffset as being for a vtable. #[derive(Clone, Copy)] pub struct VTableWIPOffset {} /// WIPOffset contains an UOffsetT with a special meaning: it is the location of /// data relative to the *end* of an in-progress FlatBuffer. The /// FlatBufferBuilder uses this to track the location of objects in an absolute /// way. The impl of Push converts a WIPOffset into a ForwardsUOffset. #[derive(Debug)] pub struct WIPOffset(UOffsetT, PhantomData); // We cannot use derive for these two impls, as the derived impls would only // implement `Copy` and `Clone` for `T: Copy` and `T: Clone` respectively. // However `WIPOffset` can always be copied, no matter that `T` you // have. impl Copy for WIPOffset {} impl Clone for WIPOffset { #[inline(always)] fn clone(&self) -> Self { *self } } impl Eq for WIPOffset {} impl PartialEq for WIPOffset { fn eq(&self, o: &WIPOffset) -> bool { self.value() == o.value() } } impl Deref for WIPOffset { type Target = UOffsetT; #[inline] fn deref(&self) -> &UOffsetT { &self.0 } } impl<'a, T: 'a> WIPOffset { /// Create a new WIPOffset. #[inline] pub fn new(o: UOffsetT) -> WIPOffset { WIPOffset(o, PhantomData) } /// Return a wrapped value that brings its meaning as a union WIPOffset /// into the type system. #[inline(always)] pub fn as_union_value(self) -> WIPOffset { WIPOffset::new(self.0) } /// Get the underlying value. #[inline(always)] pub fn value(self) -> UOffsetT { self.0 } } impl Push for WIPOffset { type Output = ForwardsUOffset; #[inline(always)] unsafe fn push(&self, dst: &mut [u8], written_len: usize) { let n = (SIZE_UOFFSET + written_len - self.value() as usize) as UOffsetT; emplace_scalar::(dst, n); } } impl Push for ForwardsUOffset { type Output = Self; #[inline(always)] unsafe fn push(&self, dst: &mut [u8], written_len: usize) { self.value().push(dst, written_len); } } /// ForwardsUOffset is used by Follow to traverse a FlatBuffer: the pointer /// is incremented by the value contained in this type. #[derive(Debug)] pub struct ForwardsUOffset(UOffsetT, PhantomData); // We cannot use derive for these two impls, as the derived impls would only // implement `Copy` and `Clone` for `T: Copy` and `T: Clone` respectively. // However `ForwardsUOffset` can always be copied, no matter that `T` you // have. impl Copy for ForwardsUOffset {} impl Clone for ForwardsUOffset { #[inline(always)] fn clone(&self) -> Self { *self } } impl ForwardsUOffset { #[inline(always)] pub fn value(self) -> UOffsetT { self.0 } } impl<'a, T: Follow<'a>> Follow<'a> for ForwardsUOffset { type Inner = T::Inner; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { let slice = &buf[loc..loc + SIZE_UOFFSET]; let off = read_scalar::(slice) as usize; T::follow(buf, loc + off) } } /// ForwardsVOffset is used by Follow to traverse a FlatBuffer: the pointer /// is incremented by the value contained in this type. #[derive(Debug)] pub struct ForwardsVOffset(VOffsetT, PhantomData); impl ForwardsVOffset { #[inline(always)] pub fn value(&self) -> VOffsetT { self.0 } } impl<'a, T: Follow<'a>> Follow<'a> for ForwardsVOffset { type Inner = T::Inner; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { let slice = &buf[loc..loc + SIZE_VOFFSET]; let off = read_scalar::(slice) as usize; T::follow(buf, loc + off) } } impl Push for ForwardsVOffset { type Output = Self; #[inline] unsafe fn push(&self, dst: &mut [u8], written_len: usize) { self.value().push(dst, written_len); } } /// ForwardsSOffset is used by Follow to traverse a FlatBuffer: the pointer /// is incremented by the *negative* of the value contained in this type. #[derive(Debug)] pub struct BackwardsSOffset(SOffsetT, PhantomData); impl BackwardsSOffset { #[inline(always)] pub fn value(&self) -> SOffsetT { self.0 } } impl<'a, T: Follow<'a>> Follow<'a> for BackwardsSOffset { type Inner = T::Inner; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { let slice = &buf[loc..loc + SIZE_SOFFSET]; let off = read_scalar::(slice); T::follow(buf, (loc as SOffsetT - off) as usize) } } impl Push for BackwardsSOffset { type Output = Self; #[inline] unsafe fn push(&self, dst: &mut [u8], written_len: usize) { self.value().push(dst, written_len); } } /// SkipSizePrefix is used by Follow to traverse a FlatBuffer: the pointer is /// incremented by a fixed constant in order to skip over the size prefix value. pub struct SkipSizePrefix(PhantomData); impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipSizePrefix { type Inner = T::Inner; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { T::follow(buf, loc + SIZE_SIZEPREFIX) } } /// SkipRootOffset is used by Follow to traverse a FlatBuffer: the pointer is /// incremented by a fixed constant in order to skip over the root offset value. pub struct SkipRootOffset(PhantomData); impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipRootOffset { type Inner = T::Inner; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { T::follow(buf, loc + SIZE_UOFFSET) } } /// FileIdentifier is used by Follow to traverse a FlatBuffer: the pointer is /// dereferenced into a byte slice, whose bytes are the file identifer value. pub struct FileIdentifier; impl<'a> Follow<'a> for FileIdentifier { type Inner = &'a [u8]; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { &buf[loc..loc + FILE_IDENTIFIER_LENGTH] } } /// SkipFileIdentifier is used by Follow to traverse a FlatBuffer: the pointer /// is incremented by a fixed constant in order to skip over the file /// identifier value. pub struct SkipFileIdentifier(PhantomData); impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipFileIdentifier { type Inner = T::Inner; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { T::follow(buf, loc + FILE_IDENTIFIER_LENGTH) } } impl<'a> Follow<'a> for bool { type Inner = bool; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { read_scalar_at::(buf, loc) != 0 } } /// Follow trait impls for primitive types. /// /// Ideally, these would be implemented as a single impl using trait bounds on /// EndianScalar, but implementing Follow that way causes a conflict with /// other impls. macro_rules! impl_follow_for_endian_scalar { ($ty:ident) => { impl<'a> Follow<'a> for $ty { type Inner = $ty; #[inline(always)] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { read_scalar_at::<$ty>(buf, loc) } } }; } impl_follow_for_endian_scalar!(u8); impl_follow_for_endian_scalar!(u16); impl_follow_for_endian_scalar!(u32); impl_follow_for_endian_scalar!(u64); impl_follow_for_endian_scalar!(i8); impl_follow_for_endian_scalar!(i16); impl_follow_for_endian_scalar!(i32); impl_follow_for_endian_scalar!(i64); impl_follow_for_endian_scalar!(f32); impl_follow_for_endian_scalar!(f64);