1// Copyright 2018 Google Inc. All rights reserved. 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// http://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 15import std 16 17namespace flatbuffers 18 19class handle: 20 buf_:string 21 pos_:int 22 23// More strongly typed than a naked int, at no cost. 24struct offset: 25 o:int 26 27enum sizeof: 28 sz_8 = 1 29 sz_16 = 2 30 sz_32 = 4 31 sz_64 = 8 32 sz_voffset = 2 33 sz_uoffset = 4 34 sz_soffset = 4 35 sz_metadata_fields = 2 36 37class builder: 38 buf = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 39 current_vtable:[int] = [] 40 head = 0 41 minalign = 1 42 object_end = 0 43 vtables:[int] = [] 44 nested = false 45 finished = false 46 47 // Optionally call this right after creating the builder for a larger initial buffer. 48 def Initial(initial_size:int): 49 buf = "\x00".repeat_string(initial_size) 50 51 def Start(): 52 // Get the start of useful data in the underlying byte buffer. 53 return buf.length - head 54 55 def Offset(): 56 // Offset relative to the end of the buffer. 57 return offset { head } 58 59 // Returns a copy of the part of the buffer containing only the finished FlatBuffer 60 def SizedCopy(): 61 assert finished 62 return buf.substring(Start(), -1) 63 64 def StartNesting(): 65 assert not nested 66 nested = true 67 68 def EndNesting(): 69 assert nested 70 nested = false 71 72 def StartObject(numfields): 73 StartNesting() 74 current_vtable = map(numfields): 0 75 object_end = head 76 minalign = 1 77 78 def EndObject(): 79 EndNesting() 80 // Prepend a zero scalar to the object. Later in this function we'll 81 // write an offset here that points to the object's vtable: 82 PrependInt32(0) 83 let object_offset = head 84 // Write out new vtable speculatively. 85 let vtable_size = (current_vtable.length + sz_metadata_fields) * sz_voffset 86 while current_vtable.length: 87 let o = current_vtable.pop() 88 PrependVOffsetT(if o: object_offset - o else: 0) 89 // The two metadata fields are written last. 90 // First, store the object bytesize: 91 PrependVOffsetT(object_offset - object_end) 92 // Second, store the vtable bytesize: 93 PrependVOffsetT(vtable_size) 94 // Search backwards through existing vtables, because similar vtables 95 // are likely to have been recently appended. See 96 // BenchmarkVtableDeduplication for a case in which this heuristic 97 // saves about 30% of the time used in writing objects with duplicate 98 // tables. 99 def find_existing_table(): 100 reverse(vtables) vt2_offset: 101 // Find the other vtable: 102 let vt2_start = buf.length - vt2_offset 103 let vt2_len = buf.read_int16_le(vt2_start) 104 // Compare the other vtable to the one under consideration. 105 // If they are equal, return the offset: 106 if vtable_size == vt2_len and 107 not compare_substring(buf, Start(), buf, vt2_start, vtable_size): 108 return vt2_offset 109 return 0 110 let existing_vtable = find_existing_table() 111 if existing_vtable: 112 // Found a duplicate vtable, remove the one we wrote. 113 head = object_offset 114 // Write the offset to the found vtable in the 115 // already-allocated offset at the beginning of this object: 116 buf.write_int32_le(Start(), existing_vtable - object_offset) 117 else: 118 // Did not find a vtable, so keep the one we wrote. 119 // Next, write the offset to the new vtable in the 120 // already-allocated offset at the beginning of this object: 121 buf.write_int32_le(buf.length - object_offset, head - object_offset) 122 // Finally, store this vtable in memory for future 123 // deduplication: 124 vtables.push(head) 125 return offset { object_offset } 126 127 def Pad(n): 128 for(n): 129 buf, head = buf.write_int8_le_back(head, 0) 130 131 def Prep(size, additional_bytes): 132 // Track the biggest thing we've ever aligned to. 133 if size > minalign: 134 minalign = size 135 // Find the amount of alignment needed such that `size` is properly 136 // aligned after `additionalBytes`: 137 let align_size = ((~(head + additional_bytes)) + 1) & (size - 1) 138 Pad(align_size) 139 140 def PrependUOffsetTRelative(off:offset): 141 // Prepends an unsigned offset into vector data, relative to where it will be written. 142 Prep(sz_uoffset, 0) 143 assert off.o <= head 144 PlaceUOffsetT(head - off.o + sz_uoffset) 145 146 def StartVector(elem_size, num_elems, alignment): 147 // Initializes bookkeeping for writing a new vector. 148 StartNesting() 149 Prep(sz_32, elem_size * num_elems) 150 Prep(alignment, elem_size * num_elems) // In case alignment > int. 151 return Offset() 152 153 def EndVector(vector_num_elems): 154 EndNesting() 155 // we already made space for this, so write without PrependUint32 156 PlaceUOffsetT(vector_num_elems) 157 return Offset() 158 159 def CreateString(s:string): 160 // writes a null-terminated byte string. 161 StartNesting() 162 Prep(sz_32, s.length + 1) 163 buf, head = buf.write_substring_back(head, s, true) 164 return EndVector(s.length) 165 166 def CreateByteVector(s:string): 167 // writes a non-null-terminated byte string. 168 StartNesting() 169 Prep(sz_32, s.length) 170 buf, head = buf.write_substring_back(head, s, false) 171 return EndVector(s.length) 172 173 def Slot(slotnum): 174 assert nested 175 while current_vtable.length <= slotnum: current_vtable.push(0) 176 current_vtable[slotnum] = head 177 178 def __Finish(root_table:offset, size_prefix:int): 179 // Finish finalizes a buffer, pointing to the given root_table 180 assert not finished 181 assert not nested 182 var prep_size = sz_32 183 if size_prefix: 184 prep_size += sz_32 185 Prep(minalign, prep_size) 186 PrependUOffsetTRelative(root_table) 187 if size_prefix: 188 PrependInt32(head) 189 finished = true 190 return Start() 191 192 def Finish(root_table:offset): 193 return __Finish(root_table, false) 194 195 def FinishSizePrefixed(root_table:offset): 196 return __Finish(root_table, true) 197 198 def PrependBool(x): 199 buf, head = buf.write_int8_le_back(head, x) 200 201 def PrependByte(x): 202 buf, head = buf.write_int8_le_back(head, x) 203 204 def PrependUint8(x): 205 buf, head = buf.write_int8_le_back(head, x) 206 207 def PrependUint16(x): 208 Prep(sz_16, 0) 209 buf, head = buf.write_int16_le_back(head, x) 210 211 def PrependUint32(x): 212 Prep(sz_32, 0) 213 buf, head = buf.write_int32_le_back(head, x) 214 215 def PrependUint64(x): 216 Prep(sz_64, 0) 217 buf, head = buf.write_int64_le_back(head, x) 218 219 def PrependInt8(x): 220 buf, head = buf.write_int8_le_back(head, x) 221 222 def PrependInt16(x): 223 Prep(sz_16, 0) 224 buf, head = buf.write_int16_le_back(head, x) 225 226 def PrependInt32(x): 227 Prep(sz_32, 0) 228 buf, head = buf.write_int32_le_back(head, x) 229 230 def PrependInt64(x): 231 Prep(sz_64, 0) 232 buf, head = buf.write_int64_le_back(head, x) 233 234 def PrependFloat32(x): 235 Prep(sz_32, 0) 236 buf, head = buf.write_float32_le_back(head, x) 237 238 def PrependFloat64(x): 239 Prep(sz_64, 0) 240 buf, head = buf.write_float64_le_back(head, x) 241 242 def PrependVOffsetT(x): 243 Prep(sz_voffset, 0) 244 buf, head = buf.write_int16_le_back(head, x) 245 246 def PlaceVOffsetT(x): 247 buf, head = buf.write_int16_le_back(head, x) 248 249 def PlaceSOffsetT(x): 250 buf, head = buf.write_int32_le_back(head, x) 251 252 def PlaceUOffsetT(x): 253 buf, head = buf.write_int32_le_back(head, x) 254 255 def PrependSlot(o:int, x, d, f): 256 if x != d: 257 f(x) 258 Slot(o) 259 260 def PrependSlot(o:int, x, f): 261 f(x) 262 Slot(o) 263 264 def PrependBoolSlot(o, x, d): PrependSlot(o, x, d): PrependBool(_) 265 def PrependByteSlot(o, x, d): PrependSlot(o, x, d): PrependByte(_) 266 def PrependUint8Slot(o, x, d): PrependSlot(o, x, d): PrependUint8(_) 267 def PrependUint16Slot(o, x, d): PrependSlot(o, x, d): PrependUint16(_) 268 def PrependUint32Slot(o, x, d): PrependSlot(o, x, d): PrependUint32(_) 269 def PrependUint64Slot(o, x, d): PrependSlot(o, x, d): PrependUint64(_) 270 def PrependInt8Slot(o, x, d): PrependSlot(o, x, d): PrependInt8(_) 271 def PrependInt16Slot(o, x, d): PrependSlot(o, x, d): PrependInt16(_) 272 def PrependInt32Slot(o, x, d): PrependSlot(o, x, d): PrependInt32(_) 273 def PrependInt64Slot(o, x, d): PrependSlot(o, x, d): PrependInt64(_) 274 def PrependFloat32Slot(o, x, d): PrependSlot(o, x, d): PrependFloat32(_) 275 def PrependFloat64Slot(o, x, d): PrependSlot(o, x, d): PrependFloat64(_) 276 277 def PrependBoolSlot(o, x): PrependSlot(o, x): PrependBool(_) 278 def PrependByteSlot(o, x): PrependSlot(o, x): PrependByte(_) 279 def PrependUint8Slot(o, x): PrependSlot(o, x): PrependUint8(_) 280 def PrependUint16Slot(o, x): PrependSlot(o, x): PrependUint16(_) 281 def PrependUint32Slot(o, x): PrependSlot(o, x): PrependUint32(_) 282 def PrependUint64Slot(o, x): PrependSlot(o, x): PrependUint64(_) 283 def PrependInt8Slot(o, x): PrependSlot(o, x): PrependInt8(_) 284 def PrependInt16Slot(o, x): PrependSlot(o, x): PrependInt16(_) 285 def PrependInt32Slot(o, x): PrependSlot(o, x): PrependInt32(_) 286 def PrependInt64Slot(o, x): PrependSlot(o, x): PrependInt64(_) 287 def PrependFloat32Slot(o, x): PrependSlot(o, x): PrependFloat32(_) 288 def PrependFloat64Slot(o, x): PrependSlot(o, x): PrependFloat64(_) 289 290 def PrependUOffsetTRelativeSlot(o:int, x:offset): 291 if x.o: 292 PrependUOffsetTRelative(x) 293 Slot(o) 294 295 def PrependStructSlot(v:int, x:offset): 296 if x.o: 297 // Structs are always stored inline, so need to be created right 298 // where they are used. You'll get this error if you created it 299 // elsewhere. 300 assert x.o == head 301 Slot(v) 302