| // 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 |
| ) |
| }); |
| } |