Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1 | // Copyright 2019 Google LLC |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | use byteorder::{LittleEndian, WriteBytesExt}; |
| 16 | |
| 17 | use crate::bitwidth::BitWidth; |
| 18 | use crate::bitwidth::BitWidth::*; |
| 19 | use crate::flexbuffer_type::FlexBufferType; |
| 20 | use crate::flexbuffer_type::FlexBufferType::*; |
| 21 | |
| 22 | /// Internal representation of FlexBuffer Types and Data before writing. |
| 23 | /// These get placed on the builder's stack and are eventually commited. |
| 24 | #[derive(Debug, Clone, Copy, PartialEq)] |
| 25 | pub enum Value { |
| 26 | // Inline types |
| 27 | Null, |
| 28 | Int(i64), |
| 29 | UInt(u64), |
| 30 | Float(f64), |
| 31 | Bool(bool), |
| 32 | /// Null termintated, c_string. Only used with `Map`s. |
| 33 | Key(usize), |
| 34 | /// The other ~20 or so types. |
| 35 | Reference { |
| 36 | address: usize, |
| 37 | child_width: BitWidth, |
| 38 | fxb_type: FlexBufferType, |
| 39 | }, |
| 40 | } |
| 41 | |
| 42 | macro_rules! new_typed_vector { |
| 43 | ($name: ident, $v2: ident, $v3: ident, $v4: ident, $vn: ident) => { |
| 44 | /// Returns a typed vector, fixed length if possible. |
| 45 | /// Address and child width are zero initialized and must be set. |
| 46 | pub fn $name(n: usize) -> Value { |
| 47 | let address = 0; |
| 48 | let child_width = W8; |
| 49 | match n { |
| 50 | 2 => Value::Reference { |
| 51 | address, |
| 52 | child_width, |
| 53 | fxb_type: $v2, |
| 54 | }, |
| 55 | 3 => Value::Reference { |
| 56 | address, |
| 57 | child_width, |
| 58 | fxb_type: $v3, |
| 59 | }, |
| 60 | 4 => Value::Reference { |
| 61 | address, |
| 62 | child_width, |
| 63 | fxb_type: $v4, |
| 64 | }, |
| 65 | _ => Value::Reference { |
| 66 | address, |
| 67 | child_width, |
| 68 | fxb_type: $vn, |
| 69 | }, |
| 70 | } |
| 71 | } |
| 72 | }; |
| 73 | } |
| 74 | |
| 75 | impl Value { |
| 76 | pub fn new_vector() -> Self { |
| 77 | Value::Reference { |
| 78 | address: 0, |
| 79 | child_width: W8, |
| 80 | fxb_type: Vector, |
| 81 | } |
| 82 | } |
| 83 | pub fn new_map() -> Self { |
| 84 | Value::Reference { |
| 85 | address: 0, |
| 86 | child_width: W8, |
| 87 | fxb_type: Map, |
| 88 | } |
| 89 | } |
| 90 | new_typed_vector!( |
| 91 | new_int_vector, |
| 92 | VectorInt2, |
| 93 | VectorInt3, |
| 94 | VectorInt4, |
| 95 | VectorInt |
| 96 | ); |
| 97 | new_typed_vector!( |
| 98 | new_uint_vector, |
| 99 | VectorUInt2, |
| 100 | VectorUInt3, |
| 101 | VectorUInt4, |
| 102 | VectorUInt |
| 103 | ); |
| 104 | new_typed_vector!( |
| 105 | new_float_vector, |
| 106 | VectorFloat2, |
| 107 | VectorFloat3, |
| 108 | VectorFloat4, |
| 109 | VectorFloat |
| 110 | ); |
| 111 | pub fn fxb_type(&self) -> FlexBufferType { |
| 112 | match *self { |
| 113 | Value::Null => Null, |
| 114 | Value::Int(_) => Int, |
| 115 | Value::UInt(_) => UInt, |
| 116 | Value::Float(_) => Float, |
| 117 | Value::Bool(_) => Bool, |
| 118 | Value::Key(_) => Key, |
| 119 | Value::Reference { fxb_type, .. } => fxb_type, |
| 120 | } |
| 121 | } |
| 122 | pub fn is_fixed_length_vector(&self) -> bool { |
| 123 | self.fxb_type().is_fixed_length_vector() |
| 124 | } |
| 125 | pub fn is_inline(&self) -> bool { |
| 126 | self.fxb_type().is_inline() |
| 127 | } |
| 128 | pub fn is_reference(&self) -> bool { |
| 129 | !self.is_inline() |
| 130 | } |
| 131 | pub fn is_key(&self) -> bool { |
| 132 | match self { |
| 133 | Value::Key(_) => true, |
| 134 | _ => false, |
| 135 | } |
| 136 | } |
| 137 | pub fn is_typed_vector_or_map(&self) -> bool { |
| 138 | if let Value::Reference { fxb_type, .. } = self { |
| 139 | fxb_type.is_heterogenous() |
| 140 | } else { |
| 141 | false |
| 142 | } |
| 143 | } |
| 144 | pub fn prefix_length(&self) -> usize { |
| 145 | if self.is_fixed_length_vector() || self.is_inline() { |
| 146 | return 0; |
| 147 | } |
| 148 | if let Value::Reference { fxb_type, .. } = self { |
| 149 | if *fxb_type == Map { |
| 150 | return 3; |
| 151 | } |
| 152 | } |
| 153 | 1 |
| 154 | } |
| 155 | pub fn set_fxb_type_or_panic(&mut self, new_type: FlexBufferType) { |
| 156 | if let Value::Reference { fxb_type, .. } = self { |
| 157 | *fxb_type = new_type; |
| 158 | } else { |
| 159 | panic!("`set_fxb_type_or_panic` called on {:?}", self) |
| 160 | } |
| 161 | } |
| 162 | pub fn set_child_width_or_panic(&mut self, new_width: BitWidth) { |
| 163 | if let Value::Reference { child_width, .. } = self { |
| 164 | *child_width = new_width; |
| 165 | } else { |
| 166 | panic!("`set_child_width_or_panic` called on {:?}", self); |
| 167 | } |
| 168 | } |
| 169 | pub fn get_address(&self) -> Option<usize> { |
| 170 | if let Value::Reference { address, .. } | Value::Key(address) = self { |
| 171 | Some(*address) |
| 172 | } else { |
| 173 | None |
| 174 | } |
| 175 | } |
| 176 | pub fn set_address_or_panic(&mut self, new_address: usize) { |
| 177 | if let Value::Reference { address, .. } | Value::Key(address) = self { |
| 178 | *address = new_address; |
| 179 | } else { |
| 180 | panic!("`set_address_or_panic` called on {:?}", self); |
| 181 | } |
| 182 | } |
| 183 | /// For inline types - the width of the value to be stored. |
| 184 | /// For reference types, the width of the referred. |
| 185 | /// Note Key types always refer to 8 bit data. |
| 186 | pub fn width_or_child_width(&self) -> BitWidth { |
| 187 | match *self { |
| 188 | Value::Int(x) => x.into(), |
| 189 | Value::UInt(x) => x.into(), |
| 190 | Value::Float(x) => x.into(), |
| 191 | Value::Key(_) | Value::Bool(_) | Value::Null => W8, |
| 192 | Value::Reference { child_width, .. } => child_width, |
| 193 | } |
| 194 | } |
| 195 | pub fn relative_address(self, written_at: usize) -> Option<Value> { |
| 196 | self.get_address().map(|address| { |
| 197 | let offset = written_at |
| 198 | .checked_sub(address) |
| 199 | .expect("Error: References may only refer backwards in buffer."); |
| 200 | Value::UInt(offset as u64) |
| 201 | }) |
| 202 | } |
| 203 | /// Computes the minimum required width of `value` when stored in a vector |
| 204 | /// starting at `vector_start` at index `idx` (this index includes the prefix). |
| 205 | /// `Value::Reference{..}` variants require location information because |
| 206 | /// offsets are relative. |
| 207 | pub fn width_in_vector(self, vector_start: usize, idx: usize) -> BitWidth { |
| 208 | match self { |
| 209 | Value::Bool(_) => W8, |
| 210 | Value::Null => W8, |
| 211 | Value::Int(x) => x.into(), |
| 212 | Value::UInt(x) => x.into(), |
| 213 | Value::Float(x) => x.into(), |
| 214 | _ => { |
| 215 | debug_assert!(self.is_reference()); |
| 216 | for &width in BitWidth::iter() { |
| 217 | let bytes = width as usize + 1; |
| 218 | let alignment = (bytes - vector_start % bytes) % bytes; |
| 219 | let written_at = vector_start + alignment + idx * bytes; |
| 220 | // This match must always succeed. |
| 221 | if let Some(Value::UInt(offset)) = self.relative_address(written_at) { |
| 222 | if BitWidth::from(offset) == width { |
| 223 | return width; |
| 224 | } |
| 225 | } |
| 226 | } |
| 227 | unreachable!() |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | pub fn packed_type(self, parent_width: BitWidth) -> u8 { |
| 232 | let width = if self.is_inline() { |
| 233 | std::cmp::max(parent_width, self.width_or_child_width()) |
| 234 | } else { |
| 235 | self.width_or_child_width() |
| 236 | }; |
| 237 | (self.fxb_type() as u8) << 2 | width as u8 |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | pub fn find_vector_type<'a, T>(mut values: T) -> Value |
| 242 | where |
| 243 | T: std::iter::Iterator<Item = &'a Value>, |
| 244 | { |
| 245 | let first = values.next(); |
| 246 | if first.is_none() { |
| 247 | return Value::new_vector(); |
| 248 | } |
| 249 | let mut len = 1; |
| 250 | let init = first.unwrap().fxb_type(); |
| 251 | for v in values { |
| 252 | if v.fxb_type() != init { |
| 253 | return Value::new_vector(); |
| 254 | } |
| 255 | len += 1; |
| 256 | } |
| 257 | let vector_type = match init { |
| 258 | Bool => VectorBool, |
| 259 | UInt => return Value::new_uint_vector(len), |
| 260 | Int => return Value::new_int_vector(len), |
| 261 | Float => return Value::new_float_vector(len), |
| 262 | Key => VectorKey, |
| 263 | // Note that VectorString is deprecated for writing |
| 264 | _ => return Value::new_vector(), |
| 265 | }; |
| 266 | Value::Reference { |
| 267 | address: 0, |
| 268 | child_width: W8, |
| 269 | fxb_type: vector_type, |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | #[inline] |
| 274 | pub fn store_value(buffer: &mut Vec<u8>, mut value: Value, width: BitWidth) { |
| 275 | // Remap to number types. |
| 276 | use Value::*; |
| 277 | if let Some(offset) = value.relative_address(buffer.len()) { |
| 278 | value = offset; |
| 279 | } else { |
| 280 | value = match value { |
| 281 | Bool(x) => UInt(x.into()), |
| 282 | Null => UInt(0), // Should this be 0 bytes? |
| 283 | _ => value, |
| 284 | } |
| 285 | } |
| 286 | let write_result = match (value, width) { |
| 287 | (UInt(x), W8) => buffer.write_u8(x as u8), |
| 288 | (UInt(x), W16) => buffer.write_u16::<LittleEndian>(x as u16), |
| 289 | (UInt(x), W32) => buffer.write_u32::<LittleEndian>(x as u32), |
| 290 | (UInt(x), W64) => buffer.write_u64::<LittleEndian>(x), |
| 291 | (Int(x), W8) => buffer.write_i8(x as i8), |
| 292 | (Int(x), W16) => buffer.write_i16::<LittleEndian>(x as i16), |
| 293 | (Int(x), W32) => buffer.write_i32::<LittleEndian>(x as i32), |
| 294 | (Int(x), W64) => buffer.write_i64::<LittleEndian>(x), |
| 295 | (Float(x), W32) => buffer.write_f32::<LittleEndian>(x as f32), |
| 296 | (Float(x), W64) => buffer.write_f64::<LittleEndian>(x), |
| 297 | (Float(_), _) => unreachable!("Error: Flatbuffers does not support 8 and 16 bit floats."), |
| 298 | _ => unreachable!("Variant not considered: {:?}", value), |
| 299 | }; |
| 300 | write_result.unwrap_or_else(|err| { |
| 301 | panic!( |
| 302 | "Error writing value {:?} with width {:?}: {:?}", |
| 303 | value, width, err |
| 304 | ) |
| 305 | }); |
| 306 | } |