mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-10 23:17:27 +00:00
[Rust] Add support for fixed size arrays (#6548)
* Add support for fixed size arrays * clang-format * Update rust image to 1.51 to support const generics * Handle correctly big endian * Add fuzz tests and clean code * Add struct fuzz test and optimize struct arrays for api * Bump flatbuffers crate version
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flatbuffers"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
edition = "2018"
|
||||
authors = ["Robert Winslow <hello@rwinslow.com>", "FlatBuffers Maintainers"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
136
rust/flatbuffers/src/array.rs
Normal file
136
rust/flatbuffers/src/array.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2021 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 crate::follow::Follow;
|
||||
use crate::{
|
||||
vector::{SafeSliceAccess, VectorIter},
|
||||
EndianScalar,
|
||||
};
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::size_of;
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Array<'a, T: 'a, const N: usize>(&'a [u8], PhantomData<T>);
|
||||
|
||||
impl<'a, T: 'a, const N: usize> Debug for Array<'a, T, N>
|
||||
where
|
||||
T: 'a + Follow<'a>,
|
||||
<T as Follow<'a>>::Inner: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, const N: usize> Array<'a, T, N> {
|
||||
#[inline(always)]
|
||||
pub fn new(buf: &'a [u8]) -> Self {
|
||||
debug_assert!(size_of::<T>() * N == buf.len());
|
||||
|
||||
Array {
|
||||
0: buf,
|
||||
1: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Follow<'a> + 'a, const N: usize> Array<'a, T, N> {
|
||||
#[inline(always)]
|
||||
pub fn get(&self, idx: usize) -> T::Inner {
|
||||
debug_assert!(idx < N);
|
||||
let sz = size_of::<T>();
|
||||
T::follow(self.0, sz * idx)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn iter(&self) -> VectorIter<'a, T> {
|
||||
VectorIter::from_slice(self.0, self.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Follow<'a> + Debug, const N: usize> Into<[T::Inner; N]> for Array<'a, T, N> {
|
||||
#[inline(always)]
|
||||
fn into(self) -> [T::Inner; N] {
|
||||
array_init(|i| self.get(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SafeSliceAccess + 'a, const N: usize> Array<'a, T, N> {
|
||||
pub fn safe_slice(self) -> &'a [T] {
|
||||
let sz = size_of::<T>();
|
||||
debug_assert!(sz > 0);
|
||||
let ptr = self.0.as_ptr() as *const T;
|
||||
unsafe { from_raw_parts(ptr, N) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement Follow for all possible Arrays that have Follow-able elements.
|
||||
impl<'a, T: Follow<'a> + 'a, const N: usize> Follow<'a> for Array<'a, T, N> {
|
||||
type Inner = Array<'a, T, N>;
|
||||
#[inline(always)]
|
||||
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
|
||||
Array::new(&buf[loc..loc + N * size_of::<T>()])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emplace_scalar_array<T: EndianScalar, const N: usize>(
|
||||
buf: &mut [u8],
|
||||
loc: usize,
|
||||
src: &[T; N],
|
||||
) {
|
||||
let mut buf_ptr = buf[loc..].as_mut_ptr() as *mut T;
|
||||
for item in src.iter() {
|
||||
let item_le = item.to_little_endian();
|
||||
unsafe {
|
||||
buf_ptr.write(item_le);
|
||||
buf_ptr = buf_ptr.add(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Follow<'a> + 'a, const N: usize> IntoIterator for Array<'a, T, N> {
|
||||
type Item = T::Inner;
|
||||
type IntoIter = VectorIter<'a, T>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn array_init<F, T, const N: usize>(mut initializer: F) -> [T; N]
|
||||
where
|
||||
F: FnMut(usize) -> T,
|
||||
{
|
||||
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
|
||||
let mut ptr_i = array.as_mut_ptr() as *mut T;
|
||||
|
||||
unsafe {
|
||||
for i in 0..N {
|
||||
let value_i = initializer(i);
|
||||
ptr_i.write(value_i);
|
||||
ptr_i = ptr_i.add(1);
|
||||
}
|
||||
array.assume_init()
|
||||
}
|
||||
}
|
||||
@@ -253,12 +253,9 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
|
||||
// Gets The pointer to the size of the string
|
||||
let str_memory = &buf[buf.len() - ptr..];
|
||||
// Gets the size of the written string from buffer
|
||||
let size = u32::from_le_bytes([
|
||||
str_memory[0],
|
||||
str_memory[1],
|
||||
str_memory[2],
|
||||
str_memory[3],
|
||||
]) as usize;
|
||||
let size =
|
||||
u32::from_le_bytes([str_memory[0], str_memory[1], str_memory[2], str_memory[3]])
|
||||
as usize;
|
||||
// Size of the string size
|
||||
let string_size: usize = 4;
|
||||
// Fetches actual string bytes from index of string after string size
|
||||
@@ -728,23 +725,21 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
|
||||
// could be empty (e.g. for empty tables, or for all-default values).
|
||||
debug_assert!(
|
||||
self.nested,
|
||||
format!(
|
||||
"incorrect FlatBufferBuilder usage: {} must be called while in a nested state",
|
||||
fn_name
|
||||
)
|
||||
"incorrect FlatBufferBuilder usage: {} must be called while in a nested state",
|
||||
fn_name
|
||||
);
|
||||
}
|
||||
#[inline]
|
||||
fn assert_not_nested(&self, msg: &'static str) {
|
||||
debug_assert!(!self.nested, msg);
|
||||
debug_assert!(!self.nested, "{}", msg);
|
||||
}
|
||||
#[inline]
|
||||
fn assert_finished(&self, msg: &'static str) {
|
||||
debug_assert!(self.finished, msg);
|
||||
debug_assert!(self.finished, "{}", msg);
|
||||
}
|
||||
#[inline]
|
||||
fn assert_not_finished(&self, msg: &'static str) {
|
||||
debug_assert!(!self.finished, msg);
|
||||
debug_assert!(!self.finished, "{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ pub fn emplace_scalar<T: EndianScalar>(s: &mut [u8], x: T) {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
&x_le as *const T as *const u8,
|
||||
s.as_mut_ptr() as *mut u8,
|
||||
size_of::<T>()
|
||||
size_of::<T>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -174,11 +174,7 @@ pub fn read_scalar<T: EndianScalar>(s: &[u8]) -> T {
|
||||
let mut mem = core::mem::MaybeUninit::<T>::uninit();
|
||||
// Since [u8] has alignment 1, we copy it into T which may have higher alignment.
|
||||
let x = unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
s.as_ptr(),
|
||||
mem.as_mut_ptr() as *mut u8,
|
||||
size_of::<T>()
|
||||
);
|
||||
core::ptr::copy_nonoverlapping(s.as_ptr(), mem.as_mut_ptr() as *mut u8, size_of::<T>());
|
||||
mem.assume_init()
|
||||
};
|
||||
x.from_little_endian()
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
//! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: <https://github.com/google/flatbuffers>
|
||||
//! (On OSX, you can install FlatBuffers from `HEAD` with the Homebrew package manager.)
|
||||
|
||||
mod array;
|
||||
mod builder;
|
||||
mod endian_scalar;
|
||||
mod follow;
|
||||
@@ -40,6 +41,7 @@ mod verifier;
|
||||
mod vtable;
|
||||
mod vtable_writer;
|
||||
|
||||
pub use crate::array::{array_init, emplace_scalar_array, Array};
|
||||
pub use crate::builder::FlatBufferBuilder;
|
||||
pub use crate::endian_scalar::{
|
||||
byte_swap_f32, byte_swap_f64, emplace_scalar, read_scalar, read_scalar_at, EndianScalar,
|
||||
|
||||
@@ -29,11 +29,15 @@ use crate::primitives::*;
|
||||
|
||||
pub struct Vector<'a, T: 'a>(&'a [u8], usize, PhantomData<T>);
|
||||
|
||||
impl<'a, T:'a> Default for Vector<'a, T> {
|
||||
impl<'a, T: 'a> Default for Vector<'a, T> {
|
||||
fn default() -> Self {
|
||||
// Static, length 0 vector.
|
||||
// Note that derived default causes UB due to issues in read_scalar_at /facepalm.
|
||||
Self(&[0; core::mem::size_of::<UOffsetT>()], 0, Default::default())
|
||||
Self(
|
||||
&[0; core::mem::size_of::<UOffsetT>()],
|
||||
0,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +92,7 @@ impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn iter(&self) -> VectorIter<'a, T> {
|
||||
VectorIter::new(*self)
|
||||
VectorIter::from_vector(*self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +189,7 @@ pub struct VectorIter<'a, T: 'a> {
|
||||
|
||||
impl<'a, T: 'a> VectorIter<'a, T> {
|
||||
#[inline]
|
||||
pub fn new(inner: Vector<'a, T>) -> Self {
|
||||
pub fn from_vector(inner: Vector<'a, T>) -> Self {
|
||||
VectorIter {
|
||||
buf: inner.0,
|
||||
// inner.1 is the location of the data for the vector.
|
||||
@@ -196,6 +200,16 @@ impl<'a, T: 'a> VectorIter<'a, T> {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_slice(buf: &'a [u8], items_num: usize) -> Self {
|
||||
VectorIter {
|
||||
buf,
|
||||
loc: 0,
|
||||
remaining: items_num,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Follow<'a> + 'a> Clone for VectorIter<'a, T> {
|
||||
|
||||
@@ -39,8 +39,10 @@ pub enum InvalidFlatbuffer {
|
||||
required: &'static str,
|
||||
error_trace: ErrorTrace,
|
||||
},
|
||||
#[error("Union exactly one of union discriminant (`{field_type}`) and value \
|
||||
(`{field}`) are present.\n{error_trace}")]
|
||||
#[error(
|
||||
"Union exactly one of union discriminant (`{field_type}`) and value \
|
||||
(`{field}`) are present.\n{error_trace}"
|
||||
)]
|
||||
InconsistentUnion {
|
||||
field: &'static str,
|
||||
field_type: &'static str,
|
||||
@@ -70,8 +72,10 @@ pub enum InvalidFlatbuffer {
|
||||
range: Range<usize>,
|
||||
error_trace: ErrorTrace,
|
||||
},
|
||||
#[error("Signed offset at position {position} has value {soffset} which points out of bounds.\
|
||||
\n{error_trace}")]
|
||||
#[error(
|
||||
"Signed offset at position {position} has value {soffset} which points out of bounds.\
|
||||
\n{error_trace}"
|
||||
)]
|
||||
SignedOffsetOutOfBounds {
|
||||
soffset: SOffsetT,
|
||||
position: usize,
|
||||
|
||||
Reference in New Issue
Block a user