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