mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-20 05:05:06 +00:00
Rust Flexbuffers (#5669)
* Cargo clippy lints * more lints * more lints * Restored a doc comment * Comment on float eps-eq and adjusted casting * Rust Flexbuffers * more serde tests, removed some unsafe * Redid serde to be map-like and Reader is Display * Moved iter from Reader to VectorReader * Serious quickcheck + bugs * wvo api * Made types smaller for a reasonable speedup * redid reading in a way that's a bit faster. Profiling shows the rust slowdown as building +10%, reading +20% * src/bin are developer binaries in rust * Root and Map width are not packed * key null check is debug only + doc changes * BuilderOptions * Documentation * Documentation * Moved tests to rust_usage_test * Moved rust flexbuffers samples to Flatbuffers/samples * Fixed RustTest * Fixed for Rust 1.37.0 * Upgraded to rust 1_40_0 * fixed a little-endian-only feature in a test * 1.40.0 * fixed some benchmarks for bigendian * Updated .bat file * misspelling * Gold Flexbuffer test. * Serialize,Deserialize, std::error::Error for Errors. * Undo rustfmt in integration_test.rs * from_slice instead of from_vec * Added comments to unsafe blocks * expanded on comment * bump Co-authored-by: CasperN <cneo@google.com>
This commit is contained in:
306
rust/flexbuffers/src/builder/value.rs
Normal file
306
rust/flexbuffers/src/builder/value.rs
Normal file
@@ -0,0 +1,306 @@
|
||||
// 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 {
|
||||
match self {
|
||||
Value::Key(_) => true,
|
||||
_ => 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
|
||||
)
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user