mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-04 04:33:23 +00:00
* inital commit of rust object api * Required fields support. * clang fallthrough * Fix unused variables * just don't fall through * remove comment * s/panic/unreachable * Tests for object API * Added defaults * deleted unintentionally added files and updated .bat file * fix bat file * clang format * Cargo clippy checks * remove commented out code * clippy allows * Remove matches! macro since we're not yet at Rust v1.42 * install clippy in RustTest.sh * move line Co-authored-by: Casper Neo <cneo@google.com>
308 lines
9.8 KiB
Rust
308 lines
9.8 KiB
Rust
// Copyright 2019 Google LLC
|
|
//
|
|
// 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
|
|
//
|
|
// https://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 byteorder::{LittleEndian, WriteBytesExt};
|
|
|
|
use crate::bitwidth::BitWidth;
|
|
use crate::bitwidth::BitWidth::*;
|
|
use crate::flexbuffer_type::FlexBufferType;
|
|
use crate::flexbuffer_type::FlexBufferType::*;
|
|
|
|
/// Internal representation of FlexBuffer Types and Data before writing.
|
|
/// These get placed on the builder's stack and are eventually commited.
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum Value {
|
|
// Inline types
|
|
Null,
|
|
Int(i64),
|
|
UInt(u64),
|
|
Float(f64),
|
|
Bool(bool),
|
|
/// Null termintated, c_string. Only used with `Map`s.
|
|
Key(usize),
|
|
/// The other ~20 or so types.
|
|
Reference {
|
|
address: usize,
|
|
child_width: BitWidth,
|
|
fxb_type: FlexBufferType,
|
|
},
|
|
}
|
|
|
|
macro_rules! new_typed_vector {
|
|
($name: ident, $v2: ident, $v3: ident, $v4: ident, $vn: ident) => {
|
|
/// Returns a typed vector, fixed length if possible.
|
|
/// Address and child width are zero initialized and must be set.
|
|
pub fn $name(n: usize) -> Value {
|
|
let address = 0;
|
|
let child_width = W8;
|
|
match n {
|
|
2 => Value::Reference {
|
|
address,
|
|
child_width,
|
|
fxb_type: $v2,
|
|
},
|
|
3 => Value::Reference {
|
|
address,
|
|
child_width,
|
|
fxb_type: $v3,
|
|
},
|
|
4 => Value::Reference {
|
|
address,
|
|
child_width,
|
|
fxb_type: $v4,
|
|
},
|
|
_ => Value::Reference {
|
|
address,
|
|
child_width,
|
|
fxb_type: $vn,
|
|
},
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl Value {
|
|
pub fn new_vector() -> Self {
|
|
Value::Reference {
|
|
address: 0,
|
|
child_width: W8,
|
|
fxb_type: Vector,
|
|
}
|
|
}
|
|
pub fn new_map() -> Self {
|
|
Value::Reference {
|
|
address: 0,
|
|
child_width: W8,
|
|
fxb_type: Map,
|
|
}
|
|
}
|
|
new_typed_vector!(
|
|
new_int_vector,
|
|
VectorInt2,
|
|
VectorInt3,
|
|
VectorInt4,
|
|
VectorInt
|
|
);
|
|
new_typed_vector!(
|
|
new_uint_vector,
|
|
VectorUInt2,
|
|
VectorUInt3,
|
|
VectorUInt4,
|
|
VectorUInt
|
|
);
|
|
new_typed_vector!(
|
|
new_float_vector,
|
|
VectorFloat2,
|
|
VectorFloat3,
|
|
VectorFloat4,
|
|
VectorFloat
|
|
);
|
|
pub fn fxb_type(&self) -> FlexBufferType {
|
|
match *self {
|
|
Value::Null => Null,
|
|
Value::Int(_) => Int,
|
|
Value::UInt(_) => UInt,
|
|
Value::Float(_) => Float,
|
|
Value::Bool(_) => Bool,
|
|
Value::Key(_) => Key,
|
|
Value::Reference { fxb_type, .. } => fxb_type,
|
|
}
|
|
}
|
|
pub fn is_fixed_length_vector(&self) -> bool {
|
|
self.fxb_type().is_fixed_length_vector()
|
|
}
|
|
pub fn is_inline(&self) -> bool {
|
|
self.fxb_type().is_inline()
|
|
}
|
|
pub fn is_reference(&self) -> bool {
|
|
!self.is_inline()
|
|
}
|
|
pub fn is_key(&self) -> bool {
|
|
if let Value::Key(_) = self {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
pub fn is_typed_vector_or_map(&self) -> bool {
|
|
if let Value::Reference { fxb_type, .. } = self {
|
|
fxb_type.is_heterogenous()
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
pub fn prefix_length(&self) -> usize {
|
|
if self.is_fixed_length_vector() || self.is_inline() {
|
|
return 0;
|
|
}
|
|
if let Value::Reference { fxb_type, .. } = self {
|
|
if *fxb_type == Map {
|
|
return 3;
|
|
}
|
|
}
|
|
1
|
|
}
|
|
pub fn set_fxb_type_or_panic(&mut self, new_type: FlexBufferType) {
|
|
if let Value::Reference { fxb_type, .. } = self {
|
|
*fxb_type = new_type;
|
|
} else {
|
|
panic!("`set_fxb_type_or_panic` called on {:?}", self)
|
|
}
|
|
}
|
|
pub fn set_child_width_or_panic(&mut self, new_width: BitWidth) {
|
|
if let Value::Reference { child_width, .. } = self {
|
|
*child_width = new_width;
|
|
} else {
|
|
panic!("`set_child_width_or_panic` called on {:?}", self);
|
|
}
|
|
}
|
|
pub fn get_address(&self) -> Option<usize> {
|
|
if let Value::Reference { address, .. } | Value::Key(address) = self {
|
|
Some(*address)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
pub fn set_address_or_panic(&mut self, new_address: usize) {
|
|
if let Value::Reference { address, .. } | Value::Key(address) = self {
|
|
*address = new_address;
|
|
} else {
|
|
panic!("`set_address_or_panic` called on {:?}", self);
|
|
}
|
|
}
|
|
/// For inline types - the width of the value to be stored.
|
|
/// For reference types, the width of the referred.
|
|
/// Note Key types always refer to 8 bit data.
|
|
pub fn width_or_child_width(&self) -> BitWidth {
|
|
match *self {
|
|
Value::Int(x) => x.into(),
|
|
Value::UInt(x) => x.into(),
|
|
Value::Float(x) => x.into(),
|
|
Value::Key(_) | Value::Bool(_) | Value::Null => W8,
|
|
Value::Reference { child_width, .. } => child_width,
|
|
}
|
|
}
|
|
pub fn relative_address(self, written_at: usize) -> Option<Value> {
|
|
self.get_address().map(|address| {
|
|
let offset = written_at
|
|
.checked_sub(address)
|
|
.expect("Error: References may only refer backwards in buffer.");
|
|
Value::UInt(offset as u64)
|
|
})
|
|
}
|
|
/// Computes the minimum required width of `value` when stored in a vector
|
|
/// starting at `vector_start` at index `idx` (this index includes the prefix).
|
|
/// `Value::Reference{..}` variants require location information because
|
|
/// offsets are relative.
|
|
pub fn width_in_vector(self, vector_start: usize, idx: usize) -> BitWidth {
|
|
match self {
|
|
Value::Bool(_) => W8,
|
|
Value::Null => W8,
|
|
Value::Int(x) => x.into(),
|
|
Value::UInt(x) => x.into(),
|
|
Value::Float(x) => x.into(),
|
|
_ => {
|
|
debug_assert!(self.is_reference());
|
|
for &width in BitWidth::iter() {
|
|
let bytes = width as usize + 1;
|
|
let alignment = (bytes - vector_start % bytes) % bytes;
|
|
let written_at = vector_start + alignment + idx * bytes;
|
|
// This match must always succeed.
|
|
if let Some(Value::UInt(offset)) = self.relative_address(written_at) {
|
|
if BitWidth::from(offset) == width {
|
|
return width;
|
|
}
|
|
}
|
|
}
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
pub fn packed_type(self, parent_width: BitWidth) -> u8 {
|
|
let width = if self.is_inline() {
|
|
std::cmp::max(parent_width, self.width_or_child_width())
|
|
} else {
|
|
self.width_or_child_width()
|
|
};
|
|
(self.fxb_type() as u8) << 2 | width as u8
|
|
}
|
|
}
|
|
|
|
pub fn find_vector_type<'a, T>(mut values: T) -> Value
|
|
where
|
|
T: std::iter::Iterator<Item = &'a Value>,
|
|
{
|
|
let first = values.next();
|
|
if first.is_none() {
|
|
return Value::new_vector();
|
|
}
|
|
let mut len = 1;
|
|
let init = first.unwrap().fxb_type();
|
|
for v in values {
|
|
if v.fxb_type() != init {
|
|
return Value::new_vector();
|
|
}
|
|
len += 1;
|
|
}
|
|
let vector_type = match init {
|
|
Bool => VectorBool,
|
|
UInt => return Value::new_uint_vector(len),
|
|
Int => return Value::new_int_vector(len),
|
|
Float => return Value::new_float_vector(len),
|
|
Key => VectorKey,
|
|
// Note that VectorString is deprecated for writing
|
|
_ => return Value::new_vector(),
|
|
};
|
|
Value::Reference {
|
|
address: 0,
|
|
child_width: W8,
|
|
fxb_type: vector_type,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn store_value(buffer: &mut Vec<u8>, mut value: Value, width: BitWidth) {
|
|
// Remap to number types.
|
|
use Value::*;
|
|
if let Some(offset) = value.relative_address(buffer.len()) {
|
|
value = offset;
|
|
} else {
|
|
value = match value {
|
|
Bool(x) => UInt(x.into()),
|
|
Null => UInt(0), // Should this be 0 bytes?
|
|
_ => value,
|
|
}
|
|
}
|
|
let write_result = match (value, width) {
|
|
(UInt(x), W8) => buffer.write_u8(x as u8),
|
|
(UInt(x), W16) => buffer.write_u16::<LittleEndian>(x as u16),
|
|
(UInt(x), W32) => buffer.write_u32::<LittleEndian>(x as u32),
|
|
(UInt(x), W64) => buffer.write_u64::<LittleEndian>(x),
|
|
(Int(x), W8) => buffer.write_i8(x as i8),
|
|
(Int(x), W16) => buffer.write_i16::<LittleEndian>(x as i16),
|
|
(Int(x), W32) => buffer.write_i32::<LittleEndian>(x as i32),
|
|
(Int(x), W64) => buffer.write_i64::<LittleEndian>(x),
|
|
(Float(x), W32) => buffer.write_f32::<LittleEndian>(x as f32),
|
|
(Float(x), W64) => buffer.write_f64::<LittleEndian>(x),
|
|
(Float(_), _) => unreachable!("Error: Flatbuffers does not support 8 and 16 bit floats."),
|
|
_ => unreachable!("Variant not considered: {:?}", value),
|
|
};
|
|
write_result.unwrap_or_else(|err| {
|
|
panic!(
|
|
"Error writing value {:?} with width {:?}: {:?}",
|
|
value, width, err
|
|
)
|
|
});
|
|
}
|