xref: /aosp_15_r20/external/flatbuffers/dart/lib/src/builder.dart (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Workerimport 'dart:convert';
2*890232f2SAndroid Build Coastguard Workerimport 'dart:typed_data';
3*890232f2SAndroid Build Coastguard Worker
4*890232f2SAndroid Build Coastguard Workerimport 'types.dart';
5*890232f2SAndroid Build Coastguard Worker
6*890232f2SAndroid Build Coastguard Worker/// The main builder class for creation of a FlexBuffer.
7*890232f2SAndroid Build Coastguard Workerclass Builder {
8*890232f2SAndroid Build Coastguard Worker  final ByteData _buffer;
9*890232f2SAndroid Build Coastguard Worker  List<_StackValue> _stack = [];
10*890232f2SAndroid Build Coastguard Worker  List<_StackPointer> _stackPointers = [];
11*890232f2SAndroid Build Coastguard Worker  int _offset = 0;
12*890232f2SAndroid Build Coastguard Worker  bool _finished = false;
13*890232f2SAndroid Build Coastguard Worker  final Map<String, _StackValue> _stringCache = {};
14*890232f2SAndroid Build Coastguard Worker  final Map<String, _StackValue> _keyCache = {};
15*890232f2SAndroid Build Coastguard Worker  final Map<_KeysHash, _StackValue> _keyVectorCache = {};
16*890232f2SAndroid Build Coastguard Worker  final Map<int, _StackValue> _indirectIntCache = {};
17*890232f2SAndroid Build Coastguard Worker  final Map<double, _StackValue> _indirectDoubleCache = {};
18*890232f2SAndroid Build Coastguard Worker
19*890232f2SAndroid Build Coastguard Worker  /// Instantiate the builder if you intent to gradually build up the buffer by calling
20*890232f2SAndroid Build Coastguard Worker  /// add... methods and calling [finish] to receive the the resulting byte array.
21*890232f2SAndroid Build Coastguard Worker  ///
22*890232f2SAndroid Build Coastguard Worker  /// The default size of internal buffer is set to 2048. Provide a different value in order to avoid buffer copies.
23*890232f2SAndroid Build Coastguard Worker  Builder({int size = 2048}) : _buffer = ByteData(size);
24*890232f2SAndroid Build Coastguard Worker
25*890232f2SAndroid Build Coastguard Worker  /// Use this method in order to turn an object into a FlexBuffer directly.
26*890232f2SAndroid Build Coastguard Worker  ///
27*890232f2SAndroid Build Coastguard Worker  /// Use the manual instantiation of the [Builder] and gradual addition of values, if performance is more important than convenience.
28*890232f2SAndroid Build Coastguard Worker  static ByteBuffer buildFromObject(Object? value) {
29*890232f2SAndroid Build Coastguard Worker    final builder = Builder();
30*890232f2SAndroid Build Coastguard Worker    builder._add(value);
31*890232f2SAndroid Build Coastguard Worker    final buffer = builder.finish();
32*890232f2SAndroid Build Coastguard Worker    final byteData = ByteData(buffer.lengthInBytes);
33*890232f2SAndroid Build Coastguard Worker    byteData.buffer.asUint8List().setAll(0, buffer);
34*890232f2SAndroid Build Coastguard Worker    return byteData.buffer;
35*890232f2SAndroid Build Coastguard Worker  }
36*890232f2SAndroid Build Coastguard Worker
37*890232f2SAndroid Build Coastguard Worker  void _add(Object? value) {
38*890232f2SAndroid Build Coastguard Worker    if (value == null) {
39*890232f2SAndroid Build Coastguard Worker      addNull();
40*890232f2SAndroid Build Coastguard Worker    } else if (value is bool) {
41*890232f2SAndroid Build Coastguard Worker      addBool(value);
42*890232f2SAndroid Build Coastguard Worker    } else if (value is int) {
43*890232f2SAndroid Build Coastguard Worker      addInt(value);
44*890232f2SAndroid Build Coastguard Worker    } else if (value is double) {
45*890232f2SAndroid Build Coastguard Worker      addDouble(value);
46*890232f2SAndroid Build Coastguard Worker    } else if (value is ByteBuffer) {
47*890232f2SAndroid Build Coastguard Worker      addBlob(value);
48*890232f2SAndroid Build Coastguard Worker    } else if (value is String) {
49*890232f2SAndroid Build Coastguard Worker      addString(value);
50*890232f2SAndroid Build Coastguard Worker    } else if (value is List<dynamic>) {
51*890232f2SAndroid Build Coastguard Worker      startVector();
52*890232f2SAndroid Build Coastguard Worker      for (var i = 0; i < value.length; i++) {
53*890232f2SAndroid Build Coastguard Worker        _add(value[i]);
54*890232f2SAndroid Build Coastguard Worker      }
55*890232f2SAndroid Build Coastguard Worker      end();
56*890232f2SAndroid Build Coastguard Worker    } else if (value is Map<String, dynamic>) {
57*890232f2SAndroid Build Coastguard Worker      startMap();
58*890232f2SAndroid Build Coastguard Worker      value.forEach((key, value) {
59*890232f2SAndroid Build Coastguard Worker        addKey(key);
60*890232f2SAndroid Build Coastguard Worker        _add(value);
61*890232f2SAndroid Build Coastguard Worker      });
62*890232f2SAndroid Build Coastguard Worker      end();
63*890232f2SAndroid Build Coastguard Worker    } else {
64*890232f2SAndroid Build Coastguard Worker      throw UnsupportedError('Value of unexpected type: $value');
65*890232f2SAndroid Build Coastguard Worker    }
66*890232f2SAndroid Build Coastguard Worker  }
67*890232f2SAndroid Build Coastguard Worker
68*890232f2SAndroid Build Coastguard Worker  /// Use this method if you want to store a null value.
69*890232f2SAndroid Build Coastguard Worker  ///
70*890232f2SAndroid Build Coastguard Worker  /// Specifically useful when building up a vector where values can be null.
71*890232f2SAndroid Build Coastguard Worker  void addNull() {
72*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
73*890232f2SAndroid Build Coastguard Worker    _stack.add(_StackValue.withNull());
74*890232f2SAndroid Build Coastguard Worker  }
75*890232f2SAndroid Build Coastguard Worker
76*890232f2SAndroid Build Coastguard Worker  /// Adds a string value.
77*890232f2SAndroid Build Coastguard Worker  void addInt(int value) {
78*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
79*890232f2SAndroid Build Coastguard Worker    _stack.add(_StackValue.withInt(value));
80*890232f2SAndroid Build Coastguard Worker  }
81*890232f2SAndroid Build Coastguard Worker
82*890232f2SAndroid Build Coastguard Worker  /// Adds a bool value.
83*890232f2SAndroid Build Coastguard Worker  void addBool(bool value) {
84*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
85*890232f2SAndroid Build Coastguard Worker    _stack.add(_StackValue.withBool(value));
86*890232f2SAndroid Build Coastguard Worker  }
87*890232f2SAndroid Build Coastguard Worker
88*890232f2SAndroid Build Coastguard Worker  /// Adds a double value.
89*890232f2SAndroid Build Coastguard Worker  void addDouble(double value) {
90*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
91*890232f2SAndroid Build Coastguard Worker    _stack.add(_StackValue.withDouble(value));
92*890232f2SAndroid Build Coastguard Worker  }
93*890232f2SAndroid Build Coastguard Worker
94*890232f2SAndroid Build Coastguard Worker  /// Adds a string value.
95*890232f2SAndroid Build Coastguard Worker  void addString(String value) {
96*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
97*890232f2SAndroid Build Coastguard Worker    if (_stringCache.containsKey(value)) {
98*890232f2SAndroid Build Coastguard Worker      _stack.add(_stringCache[value]!);
99*890232f2SAndroid Build Coastguard Worker      return;
100*890232f2SAndroid Build Coastguard Worker    }
101*890232f2SAndroid Build Coastguard Worker    final utf8String = utf8.encode(value);
102*890232f2SAndroid Build Coastguard Worker    final length = utf8String.length;
103*890232f2SAndroid Build Coastguard Worker    final bitWidth = BitWidthUtil.uwidth(length);
104*890232f2SAndroid Build Coastguard Worker    final byteWidth = _align(bitWidth);
105*890232f2SAndroid Build Coastguard Worker    _writeUInt(length, byteWidth);
106*890232f2SAndroid Build Coastguard Worker    final stringOffset = _offset;
107*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(length + 1);
108*890232f2SAndroid Build Coastguard Worker    _pushBuffer(utf8String);
109*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
110*890232f2SAndroid Build Coastguard Worker    final stackValue =
111*890232f2SAndroid Build Coastguard Worker        _StackValue.withOffset(stringOffset, ValueType.String, bitWidth);
112*890232f2SAndroid Build Coastguard Worker    _stack.add(stackValue);
113*890232f2SAndroid Build Coastguard Worker    _stringCache[value] = stackValue;
114*890232f2SAndroid Build Coastguard Worker  }
115*890232f2SAndroid Build Coastguard Worker
116*890232f2SAndroid Build Coastguard Worker  /// This methods adds a key to a map and should be followed by an add... value call.
117*890232f2SAndroid Build Coastguard Worker  ///
118*890232f2SAndroid Build Coastguard Worker  /// It also implies that you call this method only after you called [startMap].
119*890232f2SAndroid Build Coastguard Worker  void addKey(String value) {
120*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnKeyAddition();
121*890232f2SAndroid Build Coastguard Worker    if (_keyCache.containsKey(value)) {
122*890232f2SAndroid Build Coastguard Worker      _stack.add(_keyCache[value]!);
123*890232f2SAndroid Build Coastguard Worker      return;
124*890232f2SAndroid Build Coastguard Worker    }
125*890232f2SAndroid Build Coastguard Worker    final utf8String = utf8.encode(value);
126*890232f2SAndroid Build Coastguard Worker    final length = utf8String.length;
127*890232f2SAndroid Build Coastguard Worker    final keyOffset = _offset;
128*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(length + 1);
129*890232f2SAndroid Build Coastguard Worker    _pushBuffer(utf8String);
130*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
131*890232f2SAndroid Build Coastguard Worker    final stackValue =
132*890232f2SAndroid Build Coastguard Worker        _StackValue.withOffset(keyOffset, ValueType.Key, BitWidth.width8);
133*890232f2SAndroid Build Coastguard Worker    _stack.add(stackValue);
134*890232f2SAndroid Build Coastguard Worker    _keyCache[value] = stackValue;
135*890232f2SAndroid Build Coastguard Worker  }
136*890232f2SAndroid Build Coastguard Worker
137*890232f2SAndroid Build Coastguard Worker  /// Adds a byte array.
138*890232f2SAndroid Build Coastguard Worker  ///
139*890232f2SAndroid Build Coastguard Worker  /// This method can be used to store any generic BLOB.
140*890232f2SAndroid Build Coastguard Worker  void addBlob(ByteBuffer value) {
141*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
142*890232f2SAndroid Build Coastguard Worker    final length = value.lengthInBytes;
143*890232f2SAndroid Build Coastguard Worker    final bitWidth = BitWidthUtil.uwidth(length);
144*890232f2SAndroid Build Coastguard Worker    final byteWidth = _align(bitWidth);
145*890232f2SAndroid Build Coastguard Worker    _writeUInt(length, byteWidth);
146*890232f2SAndroid Build Coastguard Worker    final blobOffset = _offset;
147*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(length);
148*890232f2SAndroid Build Coastguard Worker    _pushBuffer(value.asUint8List());
149*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
150*890232f2SAndroid Build Coastguard Worker    final stackValue =
151*890232f2SAndroid Build Coastguard Worker        _StackValue.withOffset(blobOffset, ValueType.Blob, bitWidth);
152*890232f2SAndroid Build Coastguard Worker    _stack.add(stackValue);
153*890232f2SAndroid Build Coastguard Worker  }
154*890232f2SAndroid Build Coastguard Worker
155*890232f2SAndroid Build Coastguard Worker  /// Stores int value indirectly in the buffer.
156*890232f2SAndroid Build Coastguard Worker  ///
157*890232f2SAndroid Build Coastguard Worker  /// Adding large integer values indirectly might be beneficial if those values suppose to be store in a vector together with small integer values.
158*890232f2SAndroid Build Coastguard Worker  /// This is due to the fact that FlexBuffers will add padding to small integer values, if they are stored together with large integer values.
159*890232f2SAndroid Build Coastguard Worker  /// When we add integer indirectly the vector of ints will contain not the value itself, but only the relative offset to the value.
160*890232f2SAndroid Build Coastguard Worker  /// By setting the [cache] parameter to true, you make sure that the builder tracks added int value and performs deduplication.
161*890232f2SAndroid Build Coastguard Worker  void addIntIndirectly(int value, {bool cache = false}) {
162*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
163*890232f2SAndroid Build Coastguard Worker    if (_indirectIntCache.containsKey(value)) {
164*890232f2SAndroid Build Coastguard Worker      _stack.add(_indirectIntCache[value]!);
165*890232f2SAndroid Build Coastguard Worker      return;
166*890232f2SAndroid Build Coastguard Worker    }
167*890232f2SAndroid Build Coastguard Worker    final stackValue = _StackValue.withInt(value);
168*890232f2SAndroid Build Coastguard Worker    final byteWidth = _align(stackValue.width);
169*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(byteWidth);
170*890232f2SAndroid Build Coastguard Worker    final valueOffset = _offset;
171*890232f2SAndroid Build Coastguard Worker    _pushBuffer(stackValue.asU8List(stackValue.width));
172*890232f2SAndroid Build Coastguard Worker    final stackOffset = _StackValue.withOffset(
173*890232f2SAndroid Build Coastguard Worker        valueOffset, ValueType.IndirectInt, stackValue.width);
174*890232f2SAndroid Build Coastguard Worker    _stack.add(stackOffset);
175*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
176*890232f2SAndroid Build Coastguard Worker    if (cache) {
177*890232f2SAndroid Build Coastguard Worker      _indirectIntCache[value] = stackOffset;
178*890232f2SAndroid Build Coastguard Worker    }
179*890232f2SAndroid Build Coastguard Worker  }
180*890232f2SAndroid Build Coastguard Worker
181*890232f2SAndroid Build Coastguard Worker  /// Stores double value indirectly in the buffer.
182*890232f2SAndroid Build Coastguard Worker  ///
183*890232f2SAndroid Build Coastguard Worker  /// Double are stored as 8 or 4 byte values in FlexBuffers. If they are stored in a mixed vector, values which are smaller than 4 / 8 bytes will be padded.
184*890232f2SAndroid Build Coastguard Worker  /// When we add double indirectly, the vector will contain not the value itself, but only the relative offset to the value. Which could occupy only 1 or 2 bytes, reducing the odds for unnecessary padding.
185*890232f2SAndroid Build Coastguard Worker  /// By setting the [cache] parameter to true, you make sure that the builder tracks already added double value and performs deduplication.
186*890232f2SAndroid Build Coastguard Worker  void addDoubleIndirectly(double value, {bool cache = false}) {
187*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
188*890232f2SAndroid Build Coastguard Worker    if (cache && _indirectDoubleCache.containsKey(value)) {
189*890232f2SAndroid Build Coastguard Worker      _stack.add(_indirectDoubleCache[value]!);
190*890232f2SAndroid Build Coastguard Worker      return;
191*890232f2SAndroid Build Coastguard Worker    }
192*890232f2SAndroid Build Coastguard Worker    final stackValue = _StackValue.withDouble(value);
193*890232f2SAndroid Build Coastguard Worker    final byteWidth = _align(stackValue.width);
194*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(byteWidth);
195*890232f2SAndroid Build Coastguard Worker    final valueOffset = _offset;
196*890232f2SAndroid Build Coastguard Worker    _pushBuffer(stackValue.asU8List(stackValue.width));
197*890232f2SAndroid Build Coastguard Worker    final stackOffset = _StackValue.withOffset(
198*890232f2SAndroid Build Coastguard Worker        valueOffset, ValueType.IndirectFloat, stackValue.width);
199*890232f2SAndroid Build Coastguard Worker    _stack.add(stackOffset);
200*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
201*890232f2SAndroid Build Coastguard Worker    if (cache) {
202*890232f2SAndroid Build Coastguard Worker      _indirectDoubleCache[value] = stackOffset;
203*890232f2SAndroid Build Coastguard Worker    }
204*890232f2SAndroid Build Coastguard Worker  }
205*890232f2SAndroid Build Coastguard Worker
206*890232f2SAndroid Build Coastguard Worker  /// This method starts a vector definition and needs to be followed by 0 to n add... value calls.
207*890232f2SAndroid Build Coastguard Worker  ///
208*890232f2SAndroid Build Coastguard Worker  /// The vector definition needs to be finished with an [end] call.
209*890232f2SAndroid Build Coastguard Worker  /// It is also possible to add nested vector or map by calling [startVector] / [startMap].
210*890232f2SAndroid Build Coastguard Worker  void startVector() {
211*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
212*890232f2SAndroid Build Coastguard Worker    _stackPointers.add(_StackPointer(_stack.length, true));
213*890232f2SAndroid Build Coastguard Worker  }
214*890232f2SAndroid Build Coastguard Worker
215*890232f2SAndroid Build Coastguard Worker  /// This method starts a map definition.
216*890232f2SAndroid Build Coastguard Worker  ///
217*890232f2SAndroid Build Coastguard Worker  /// This method call needs to be followed by 0 to n [addKey] +  add... value calls.
218*890232f2SAndroid Build Coastguard Worker  /// The map definition needs to be finished with an [end] call.
219*890232f2SAndroid Build Coastguard Worker  /// It is also possible to add nested vector or map by calling [startVector] / [startMap] after calling [addKey].
220*890232f2SAndroid Build Coastguard Worker  void startMap() {
221*890232f2SAndroid Build Coastguard Worker    _integrityCheckOnValueAddition();
222*890232f2SAndroid Build Coastguard Worker    _stackPointers.add(_StackPointer(_stack.length, false));
223*890232f2SAndroid Build Coastguard Worker  }
224*890232f2SAndroid Build Coastguard Worker
225*890232f2SAndroid Build Coastguard Worker  /// Marks that the addition of values to the last vector, or map have ended.
226*890232f2SAndroid Build Coastguard Worker  void end() {
227*890232f2SAndroid Build Coastguard Worker    final pointer = _stackPointers.removeLast();
228*890232f2SAndroid Build Coastguard Worker    if (pointer.isVector) {
229*890232f2SAndroid Build Coastguard Worker      _endVector(pointer);
230*890232f2SAndroid Build Coastguard Worker    } else {
231*890232f2SAndroid Build Coastguard Worker      _sortKeysAndEndMap(pointer);
232*890232f2SAndroid Build Coastguard Worker    }
233*890232f2SAndroid Build Coastguard Worker  }
234*890232f2SAndroid Build Coastguard Worker
235*890232f2SAndroid Build Coastguard Worker  /// Finish building the FlatBuffer and return array of bytes.
236*890232f2SAndroid Build Coastguard Worker  ///
237*890232f2SAndroid Build Coastguard Worker  /// Can be called multiple times, to get the array of bytes.
238*890232f2SAndroid Build Coastguard Worker  /// After the first call, adding values, or starting vectors / maps will result in an exception.
239*890232f2SAndroid Build Coastguard Worker  Uint8List finish() {
240*890232f2SAndroid Build Coastguard Worker    if (_finished == false) {
241*890232f2SAndroid Build Coastguard Worker      _finish();
242*890232f2SAndroid Build Coastguard Worker    }
243*890232f2SAndroid Build Coastguard Worker    return _buffer.buffer.asUint8List(0, _offset);
244*890232f2SAndroid Build Coastguard Worker  }
245*890232f2SAndroid Build Coastguard Worker
246*890232f2SAndroid Build Coastguard Worker  /// Builds a FlatBuffer with current state without finishing the builder.
247*890232f2SAndroid Build Coastguard Worker  ///
248*890232f2SAndroid Build Coastguard Worker  /// Creates an internal temporary copy of current builder and finishes the copy.
249*890232f2SAndroid Build Coastguard Worker  /// Use this method, when the state of a long lasting builder need to be persisted periodically.
250*890232f2SAndroid Build Coastguard Worker  ByteBuffer snapshot() {
251*890232f2SAndroid Build Coastguard Worker    final tmp = Builder(size: _offset + 200);
252*890232f2SAndroid Build Coastguard Worker    tmp._offset = _offset;
253*890232f2SAndroid Build Coastguard Worker    tmp._stack = List.from(_stack);
254*890232f2SAndroid Build Coastguard Worker    tmp._stackPointers = List.from(_stackPointers);
255*890232f2SAndroid Build Coastguard Worker    tmp._buffer.buffer
256*890232f2SAndroid Build Coastguard Worker        .asUint8List()
257*890232f2SAndroid Build Coastguard Worker        .setAll(0, _buffer.buffer.asUint8List(0, _offset));
258*890232f2SAndroid Build Coastguard Worker    for (var i = 0; i < tmp._stackPointers.length; i++) {
259*890232f2SAndroid Build Coastguard Worker      tmp.end();
260*890232f2SAndroid Build Coastguard Worker    }
261*890232f2SAndroid Build Coastguard Worker    final buffer = tmp.finish();
262*890232f2SAndroid Build Coastguard Worker    final bd = ByteData(buffer.lengthInBytes);
263*890232f2SAndroid Build Coastguard Worker    bd.buffer.asUint8List().setAll(0, buffer);
264*890232f2SAndroid Build Coastguard Worker    return bd.buffer;
265*890232f2SAndroid Build Coastguard Worker  }
266*890232f2SAndroid Build Coastguard Worker
267*890232f2SAndroid Build Coastguard Worker  void _integrityCheckOnValueAddition() {
268*890232f2SAndroid Build Coastguard Worker    if (_finished) {
269*890232f2SAndroid Build Coastguard Worker      throw StateError('Adding values after finish is prohibited');
270*890232f2SAndroid Build Coastguard Worker    }
271*890232f2SAndroid Build Coastguard Worker    if (_stackPointers.isNotEmpty && _stackPointers.last.isVector == false) {
272*890232f2SAndroid Build Coastguard Worker      if (_stack.last.type != ValueType.Key) {
273*890232f2SAndroid Build Coastguard Worker        throw StateError(
274*890232f2SAndroid Build Coastguard Worker            'Adding value to a map before adding a key is prohibited');
275*890232f2SAndroid Build Coastguard Worker      }
276*890232f2SAndroid Build Coastguard Worker    }
277*890232f2SAndroid Build Coastguard Worker  }
278*890232f2SAndroid Build Coastguard Worker
279*890232f2SAndroid Build Coastguard Worker  void _integrityCheckOnKeyAddition() {
280*890232f2SAndroid Build Coastguard Worker    if (_finished) {
281*890232f2SAndroid Build Coastguard Worker      throw StateError('Adding values after finish is prohibited');
282*890232f2SAndroid Build Coastguard Worker    }
283*890232f2SAndroid Build Coastguard Worker    if (_stackPointers.isEmpty || _stackPointers.last.isVector) {
284*890232f2SAndroid Build Coastguard Worker      throw StateError('Adding key before staring a map is prohibited');
285*890232f2SAndroid Build Coastguard Worker    }
286*890232f2SAndroid Build Coastguard Worker  }
287*890232f2SAndroid Build Coastguard Worker
288*890232f2SAndroid Build Coastguard Worker  void _finish() {
289*890232f2SAndroid Build Coastguard Worker    if (_stack.length != 1) {
290*890232f2SAndroid Build Coastguard Worker      throw StateError(
291*890232f2SAndroid Build Coastguard Worker          'Stack has to be exactly 1, but is ${_stack.length}. You have to end all started vectors and maps, before calling [finish]');
292*890232f2SAndroid Build Coastguard Worker    }
293*890232f2SAndroid Build Coastguard Worker    final value = _stack[0];
294*890232f2SAndroid Build Coastguard Worker    final byteWidth = _align(value.elementWidth(_offset, 0));
295*890232f2SAndroid Build Coastguard Worker    _writeStackValue(value, byteWidth);
296*890232f2SAndroid Build Coastguard Worker    _writeUInt(value.storedPackedType(), 1);
297*890232f2SAndroid Build Coastguard Worker    _writeUInt(byteWidth, 1);
298*890232f2SAndroid Build Coastguard Worker    _finished = true;
299*890232f2SAndroid Build Coastguard Worker  }
300*890232f2SAndroid Build Coastguard Worker
301*890232f2SAndroid Build Coastguard Worker  _StackValue _createVector(int start, int vecLength, int step,
302*890232f2SAndroid Build Coastguard Worker      [_StackValue? keys]) {
303*890232f2SAndroid Build Coastguard Worker    var bitWidth = BitWidthUtil.uwidth(vecLength);
304*890232f2SAndroid Build Coastguard Worker    var prefixElements = 1;
305*890232f2SAndroid Build Coastguard Worker    if (keys != null) {
306*890232f2SAndroid Build Coastguard Worker      var elemWidth = keys.elementWidth(_offset, 0);
307*890232f2SAndroid Build Coastguard Worker      if (elemWidth.index > bitWidth.index) {
308*890232f2SAndroid Build Coastguard Worker        bitWidth = elemWidth;
309*890232f2SAndroid Build Coastguard Worker      }
310*890232f2SAndroid Build Coastguard Worker      prefixElements += 2;
311*890232f2SAndroid Build Coastguard Worker    }
312*890232f2SAndroid Build Coastguard Worker    var vectorType = ValueType.Key;
313*890232f2SAndroid Build Coastguard Worker    var typed = keys == null;
314*890232f2SAndroid Build Coastguard Worker    for (var i = start; i < _stack.length; i += step) {
315*890232f2SAndroid Build Coastguard Worker      final elemWidth = _stack[i].elementWidth(_offset, i + prefixElements);
316*890232f2SAndroid Build Coastguard Worker      if (elemWidth.index > bitWidth.index) {
317*890232f2SAndroid Build Coastguard Worker        bitWidth = elemWidth;
318*890232f2SAndroid Build Coastguard Worker      }
319*890232f2SAndroid Build Coastguard Worker      if (i == start) {
320*890232f2SAndroid Build Coastguard Worker        vectorType = _stack[i].type;
321*890232f2SAndroid Build Coastguard Worker        typed &= ValueTypeUtils.isTypedVectorElement(vectorType);
322*890232f2SAndroid Build Coastguard Worker      } else {
323*890232f2SAndroid Build Coastguard Worker        if (vectorType != _stack[i].type) {
324*890232f2SAndroid Build Coastguard Worker          typed = false;
325*890232f2SAndroid Build Coastguard Worker        }
326*890232f2SAndroid Build Coastguard Worker      }
327*890232f2SAndroid Build Coastguard Worker    }
328*890232f2SAndroid Build Coastguard Worker    final byteWidth = _align(bitWidth);
329*890232f2SAndroid Build Coastguard Worker    final fix = typed & ValueTypeUtils.isNumber(vectorType) &&
330*890232f2SAndroid Build Coastguard Worker        vecLength >= 2 &&
331*890232f2SAndroid Build Coastguard Worker        vecLength <= 4;
332*890232f2SAndroid Build Coastguard Worker    if (keys != null) {
333*890232f2SAndroid Build Coastguard Worker      _writeStackValue(keys, byteWidth);
334*890232f2SAndroid Build Coastguard Worker      _writeUInt(1 << keys.width.index, byteWidth);
335*890232f2SAndroid Build Coastguard Worker    }
336*890232f2SAndroid Build Coastguard Worker    if (fix == false) {
337*890232f2SAndroid Build Coastguard Worker      _writeUInt(vecLength, byteWidth);
338*890232f2SAndroid Build Coastguard Worker    }
339*890232f2SAndroid Build Coastguard Worker    final vecOffset = _offset;
340*890232f2SAndroid Build Coastguard Worker    for (var i = start; i < _stack.length; i += step) {
341*890232f2SAndroid Build Coastguard Worker      _writeStackValue(_stack[i], byteWidth);
342*890232f2SAndroid Build Coastguard Worker    }
343*890232f2SAndroid Build Coastguard Worker    if (typed == false) {
344*890232f2SAndroid Build Coastguard Worker      for (var i = start; i < _stack.length; i += step) {
345*890232f2SAndroid Build Coastguard Worker        _writeUInt(_stack[i].storedPackedType(), 1);
346*890232f2SAndroid Build Coastguard Worker      }
347*890232f2SAndroid Build Coastguard Worker    }
348*890232f2SAndroid Build Coastguard Worker    if (keys != null) {
349*890232f2SAndroid Build Coastguard Worker      return _StackValue.withOffset(vecOffset, ValueType.Map, bitWidth);
350*890232f2SAndroid Build Coastguard Worker    }
351*890232f2SAndroid Build Coastguard Worker    if (typed) {
352*890232f2SAndroid Build Coastguard Worker      final vType =
353*890232f2SAndroid Build Coastguard Worker          ValueTypeUtils.toTypedVector(vectorType, fix ? vecLength : 0);
354*890232f2SAndroid Build Coastguard Worker      return _StackValue.withOffset(vecOffset, vType, bitWidth);
355*890232f2SAndroid Build Coastguard Worker    }
356*890232f2SAndroid Build Coastguard Worker    return _StackValue.withOffset(vecOffset, ValueType.Vector, bitWidth);
357*890232f2SAndroid Build Coastguard Worker  }
358*890232f2SAndroid Build Coastguard Worker
359*890232f2SAndroid Build Coastguard Worker  void _endVector(_StackPointer pointer) {
360*890232f2SAndroid Build Coastguard Worker    final vecLength = _stack.length - pointer.stackPosition;
361*890232f2SAndroid Build Coastguard Worker    final vec = _createVector(pointer.stackPosition, vecLength, 1);
362*890232f2SAndroid Build Coastguard Worker    _stack.removeRange(pointer.stackPosition, _stack.length);
363*890232f2SAndroid Build Coastguard Worker    _stack.add(vec);
364*890232f2SAndroid Build Coastguard Worker  }
365*890232f2SAndroid Build Coastguard Worker
366*890232f2SAndroid Build Coastguard Worker  void _sortKeysAndEndMap(_StackPointer pointer) {
367*890232f2SAndroid Build Coastguard Worker    if (((_stack.length - pointer.stackPosition) & 1) == 1) {
368*890232f2SAndroid Build Coastguard Worker      throw StateError(
369*890232f2SAndroid Build Coastguard Worker          'The stack needs to hold key value pairs (even number of elements). Check if you combined [addKey] with add... method calls properly.');
370*890232f2SAndroid Build Coastguard Worker    }
371*890232f2SAndroid Build Coastguard Worker
372*890232f2SAndroid Build Coastguard Worker    var sorted = true;
373*890232f2SAndroid Build Coastguard Worker    for (var i = pointer.stackPosition; i < _stack.length - 2; i += 2) {
374*890232f2SAndroid Build Coastguard Worker      if (_shouldFlip(_stack[i], _stack[i + 2])) {
375*890232f2SAndroid Build Coastguard Worker        sorted = false;
376*890232f2SAndroid Build Coastguard Worker        break;
377*890232f2SAndroid Build Coastguard Worker      }
378*890232f2SAndroid Build Coastguard Worker    }
379*890232f2SAndroid Build Coastguard Worker
380*890232f2SAndroid Build Coastguard Worker    if (sorted == false) {
381*890232f2SAndroid Build Coastguard Worker      for (var i = pointer.stackPosition; i < _stack.length; i += 2) {
382*890232f2SAndroid Build Coastguard Worker        var flipIndex = i;
383*890232f2SAndroid Build Coastguard Worker        for (var j = i + 2; j < _stack.length; j += 2) {
384*890232f2SAndroid Build Coastguard Worker          if (_shouldFlip(_stack[flipIndex], _stack[j])) {
385*890232f2SAndroid Build Coastguard Worker            flipIndex = j;
386*890232f2SAndroid Build Coastguard Worker          }
387*890232f2SAndroid Build Coastguard Worker        }
388*890232f2SAndroid Build Coastguard Worker        if (flipIndex != i) {
389*890232f2SAndroid Build Coastguard Worker          var k = _stack[flipIndex];
390*890232f2SAndroid Build Coastguard Worker          var v = _stack[flipIndex + 1];
391*890232f2SAndroid Build Coastguard Worker          _stack[flipIndex] = _stack[i];
392*890232f2SAndroid Build Coastguard Worker          _stack[flipIndex + 1] = _stack[i + 1];
393*890232f2SAndroid Build Coastguard Worker          _stack[i] = k;
394*890232f2SAndroid Build Coastguard Worker          _stack[i + 1] = v;
395*890232f2SAndroid Build Coastguard Worker        }
396*890232f2SAndroid Build Coastguard Worker      }
397*890232f2SAndroid Build Coastguard Worker    }
398*890232f2SAndroid Build Coastguard Worker    _endMap(pointer);
399*890232f2SAndroid Build Coastguard Worker  }
400*890232f2SAndroid Build Coastguard Worker
401*890232f2SAndroid Build Coastguard Worker  void _endMap(_StackPointer pointer) {
402*890232f2SAndroid Build Coastguard Worker    final vecLength = (_stack.length - pointer.stackPosition) >> 1;
403*890232f2SAndroid Build Coastguard Worker    final offsets = <int>[];
404*890232f2SAndroid Build Coastguard Worker    for (var i = pointer.stackPosition; i < _stack.length; i += 2) {
405*890232f2SAndroid Build Coastguard Worker      offsets.add(_stack[i].offset!);
406*890232f2SAndroid Build Coastguard Worker    }
407*890232f2SAndroid Build Coastguard Worker    final keysHash = _KeysHash(offsets);
408*890232f2SAndroid Build Coastguard Worker    _StackValue? keysStackValue;
409*890232f2SAndroid Build Coastguard Worker    if (_keyVectorCache.containsKey(keysHash)) {
410*890232f2SAndroid Build Coastguard Worker      keysStackValue = _keyVectorCache[keysHash];
411*890232f2SAndroid Build Coastguard Worker    } else {
412*890232f2SAndroid Build Coastguard Worker      keysStackValue = _createVector(pointer.stackPosition, vecLength, 2);
413*890232f2SAndroid Build Coastguard Worker      _keyVectorCache[keysHash] = keysStackValue;
414*890232f2SAndroid Build Coastguard Worker    }
415*890232f2SAndroid Build Coastguard Worker    final vec =
416*890232f2SAndroid Build Coastguard Worker        _createVector(pointer.stackPosition + 1, vecLength, 2, keysStackValue);
417*890232f2SAndroid Build Coastguard Worker    _stack.removeRange(pointer.stackPosition, _stack.length);
418*890232f2SAndroid Build Coastguard Worker    _stack.add(vec);
419*890232f2SAndroid Build Coastguard Worker  }
420*890232f2SAndroid Build Coastguard Worker
421*890232f2SAndroid Build Coastguard Worker  bool _shouldFlip(_StackValue v1, _StackValue v2) {
422*890232f2SAndroid Build Coastguard Worker    if (v1.type != ValueType.Key || v2.type != ValueType.Key) {
423*890232f2SAndroid Build Coastguard Worker      throw StateError(
424*890232f2SAndroid Build Coastguard Worker          'Stack values are not keys $v1 | $v2. Check if you combined [addKey] with add... method calls properly.');
425*890232f2SAndroid Build Coastguard Worker    }
426*890232f2SAndroid Build Coastguard Worker
427*890232f2SAndroid Build Coastguard Worker    late int c1, c2;
428*890232f2SAndroid Build Coastguard Worker    var index = 0;
429*890232f2SAndroid Build Coastguard Worker    do {
430*890232f2SAndroid Build Coastguard Worker      c1 = _buffer.getUint8(v1.offset! + index);
431*890232f2SAndroid Build Coastguard Worker      c2 = _buffer.getUint8(v2.offset! + index);
432*890232f2SAndroid Build Coastguard Worker      if (c2 < c1) return true;
433*890232f2SAndroid Build Coastguard Worker      if (c1 < c2) return false;
434*890232f2SAndroid Build Coastguard Worker      index += 1;
435*890232f2SAndroid Build Coastguard Worker    } while (c1 != 0 && c2 != 0);
436*890232f2SAndroid Build Coastguard Worker    return false;
437*890232f2SAndroid Build Coastguard Worker  }
438*890232f2SAndroid Build Coastguard Worker
439*890232f2SAndroid Build Coastguard Worker  int _align(BitWidth width) {
440*890232f2SAndroid Build Coastguard Worker    final byteWidth = BitWidthUtil.toByteWidth(width);
441*890232f2SAndroid Build Coastguard Worker    _offset += BitWidthUtil.paddingSize(_offset, byteWidth);
442*890232f2SAndroid Build Coastguard Worker    return byteWidth;
443*890232f2SAndroid Build Coastguard Worker  }
444*890232f2SAndroid Build Coastguard Worker
445*890232f2SAndroid Build Coastguard Worker  void _writeStackValue(_StackValue value, int byteWidth) {
446*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(byteWidth);
447*890232f2SAndroid Build Coastguard Worker    if (value.isOffset) {
448*890232f2SAndroid Build Coastguard Worker      final relativeOffset = _offset - value.offset!;
449*890232f2SAndroid Build Coastguard Worker      if (byteWidth == 8 || relativeOffset < (1 << (byteWidth * 8))) {
450*890232f2SAndroid Build Coastguard Worker        _writeUInt(relativeOffset, byteWidth);
451*890232f2SAndroid Build Coastguard Worker      } else {
452*890232f2SAndroid Build Coastguard Worker        throw StateError(
453*890232f2SAndroid Build Coastguard Worker            'Unexpected size $byteWidth. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new');
454*890232f2SAndroid Build Coastguard Worker      }
455*890232f2SAndroid Build Coastguard Worker    } else {
456*890232f2SAndroid Build Coastguard Worker      _pushBuffer(value.asU8List(BitWidthUtil.fromByteWidth(byteWidth)));
457*890232f2SAndroid Build Coastguard Worker    }
458*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
459*890232f2SAndroid Build Coastguard Worker  }
460*890232f2SAndroid Build Coastguard Worker
461*890232f2SAndroid Build Coastguard Worker  void _writeUInt(int value, int byteWidth) {
462*890232f2SAndroid Build Coastguard Worker    final newOffset = _newOffset(byteWidth);
463*890232f2SAndroid Build Coastguard Worker    _pushUInt(value, BitWidthUtil.fromByteWidth(byteWidth));
464*890232f2SAndroid Build Coastguard Worker    _offset = newOffset;
465*890232f2SAndroid Build Coastguard Worker  }
466*890232f2SAndroid Build Coastguard Worker
467*890232f2SAndroid Build Coastguard Worker  int _newOffset(int newValueSize) {
468*890232f2SAndroid Build Coastguard Worker    final newOffset = _offset + newValueSize;
469*890232f2SAndroid Build Coastguard Worker    var size = _buffer.lengthInBytes;
470*890232f2SAndroid Build Coastguard Worker    final prevSize = size;
471*890232f2SAndroid Build Coastguard Worker    while (size < newOffset) {
472*890232f2SAndroid Build Coastguard Worker      size <<= 1;
473*890232f2SAndroid Build Coastguard Worker    }
474*890232f2SAndroid Build Coastguard Worker    if (prevSize < size) {
475*890232f2SAndroid Build Coastguard Worker      final newBuf = ByteData(size);
476*890232f2SAndroid Build Coastguard Worker      newBuf.buffer.asUint8List().setAll(0, _buffer.buffer.asUint8List());
477*890232f2SAndroid Build Coastguard Worker    }
478*890232f2SAndroid Build Coastguard Worker    return newOffset;
479*890232f2SAndroid Build Coastguard Worker  }
480*890232f2SAndroid Build Coastguard Worker
481*890232f2SAndroid Build Coastguard Worker  void _pushInt(int value, BitWidth width) {
482*890232f2SAndroid Build Coastguard Worker    switch (width) {
483*890232f2SAndroid Build Coastguard Worker      case BitWidth.width8:
484*890232f2SAndroid Build Coastguard Worker        _buffer.setInt8(_offset, value);
485*890232f2SAndroid Build Coastguard Worker        break;
486*890232f2SAndroid Build Coastguard Worker      case BitWidth.width16:
487*890232f2SAndroid Build Coastguard Worker        _buffer.setInt16(_offset, value, Endian.little);
488*890232f2SAndroid Build Coastguard Worker        break;
489*890232f2SAndroid Build Coastguard Worker      case BitWidth.width32:
490*890232f2SAndroid Build Coastguard Worker        _buffer.setInt32(_offset, value, Endian.little);
491*890232f2SAndroid Build Coastguard Worker        break;
492*890232f2SAndroid Build Coastguard Worker      case BitWidth.width64:
493*890232f2SAndroid Build Coastguard Worker        _buffer.setInt64(_offset, value, Endian.little);
494*890232f2SAndroid Build Coastguard Worker        break;
495*890232f2SAndroid Build Coastguard Worker    }
496*890232f2SAndroid Build Coastguard Worker  }
497*890232f2SAndroid Build Coastguard Worker
498*890232f2SAndroid Build Coastguard Worker  void _pushUInt(int value, BitWidth width) {
499*890232f2SAndroid Build Coastguard Worker    switch (width) {
500*890232f2SAndroid Build Coastguard Worker      case BitWidth.width8:
501*890232f2SAndroid Build Coastguard Worker        _buffer.setUint8(_offset, value);
502*890232f2SAndroid Build Coastguard Worker        break;
503*890232f2SAndroid Build Coastguard Worker      case BitWidth.width16:
504*890232f2SAndroid Build Coastguard Worker        _buffer.setUint16(_offset, value, Endian.little);
505*890232f2SAndroid Build Coastguard Worker        break;
506*890232f2SAndroid Build Coastguard Worker      case BitWidth.width32:
507*890232f2SAndroid Build Coastguard Worker        _buffer.setUint32(_offset, value, Endian.little);
508*890232f2SAndroid Build Coastguard Worker        break;
509*890232f2SAndroid Build Coastguard Worker      case BitWidth.width64:
510*890232f2SAndroid Build Coastguard Worker        _buffer.setUint64(_offset, value, Endian.little);
511*890232f2SAndroid Build Coastguard Worker        break;
512*890232f2SAndroid Build Coastguard Worker    }
513*890232f2SAndroid Build Coastguard Worker  }
514*890232f2SAndroid Build Coastguard Worker
515*890232f2SAndroid Build Coastguard Worker  void _pushBuffer(List<int> value) {
516*890232f2SAndroid Build Coastguard Worker    _buffer.buffer.asUint8List().setAll(_offset, value);
517*890232f2SAndroid Build Coastguard Worker  }
518*890232f2SAndroid Build Coastguard Worker}
519*890232f2SAndroid Build Coastguard Worker
520*890232f2SAndroid Build Coastguard Workerclass _StackValue {
521*890232f2SAndroid Build Coastguard Worker  late Object _value;
522*890232f2SAndroid Build Coastguard Worker  int? _offset;
523*890232f2SAndroid Build Coastguard Worker  final ValueType _type;
524*890232f2SAndroid Build Coastguard Worker  final BitWidth _width;
525*890232f2SAndroid Build Coastguard Worker
526*890232f2SAndroid Build Coastguard Worker  _StackValue.withNull()
527*890232f2SAndroid Build Coastguard Worker      : _type = ValueType.Null,
528*890232f2SAndroid Build Coastguard Worker        _width = BitWidth.width8;
529*890232f2SAndroid Build Coastguard Worker
530*890232f2SAndroid Build Coastguard Worker  _StackValue.withInt(int value)
531*890232f2SAndroid Build Coastguard Worker      : _type = ValueType.Int,
532*890232f2SAndroid Build Coastguard Worker        _width = BitWidthUtil.width(value),
533*890232f2SAndroid Build Coastguard Worker        _value = value;
534*890232f2SAndroid Build Coastguard Worker
535*890232f2SAndroid Build Coastguard Worker  _StackValue.withBool(bool value)
536*890232f2SAndroid Build Coastguard Worker      : _type = ValueType.Bool,
537*890232f2SAndroid Build Coastguard Worker        _width = BitWidth.width8,
538*890232f2SAndroid Build Coastguard Worker        _value = value;
539*890232f2SAndroid Build Coastguard Worker
540*890232f2SAndroid Build Coastguard Worker  _StackValue.withDouble(double value)
541*890232f2SAndroid Build Coastguard Worker      : _type = ValueType.Float,
542*890232f2SAndroid Build Coastguard Worker        _width = BitWidthUtil.width(value),
543*890232f2SAndroid Build Coastguard Worker        _value = value;
544*890232f2SAndroid Build Coastguard Worker
545*890232f2SAndroid Build Coastguard Worker  _StackValue.withOffset(int value, ValueType type, BitWidth width)
546*890232f2SAndroid Build Coastguard Worker      : _offset = value,
547*890232f2SAndroid Build Coastguard Worker        _type = type,
548*890232f2SAndroid Build Coastguard Worker        _width = width;
549*890232f2SAndroid Build Coastguard Worker
550*890232f2SAndroid Build Coastguard Worker  BitWidth storedWidth({BitWidth width = BitWidth.width8}) {
551*890232f2SAndroid Build Coastguard Worker    return ValueTypeUtils.isInline(_type)
552*890232f2SAndroid Build Coastguard Worker        ? BitWidthUtil.max(_width, width)
553*890232f2SAndroid Build Coastguard Worker        : _width;
554*890232f2SAndroid Build Coastguard Worker  }
555*890232f2SAndroid Build Coastguard Worker
556*890232f2SAndroid Build Coastguard Worker  int storedPackedType({BitWidth width = BitWidth.width8}) {
557*890232f2SAndroid Build Coastguard Worker    return ValueTypeUtils.packedType(_type, storedWidth(width: width));
558*890232f2SAndroid Build Coastguard Worker  }
559*890232f2SAndroid Build Coastguard Worker
560*890232f2SAndroid Build Coastguard Worker  BitWidth elementWidth(int size, int index) {
561*890232f2SAndroid Build Coastguard Worker    if (ValueTypeUtils.isInline(_type)) return _width;
562*890232f2SAndroid Build Coastguard Worker    final offset = _offset!;
563*890232f2SAndroid Build Coastguard Worker    for (var i = 0; i < 4; i++) {
564*890232f2SAndroid Build Coastguard Worker      final width = 1 << i;
565*890232f2SAndroid Build Coastguard Worker      final bitWidth = BitWidthUtil.uwidth(size +
566*890232f2SAndroid Build Coastguard Worker          BitWidthUtil.paddingSize(size, width) +
567*890232f2SAndroid Build Coastguard Worker          index * width -
568*890232f2SAndroid Build Coastguard Worker          offset);
569*890232f2SAndroid Build Coastguard Worker      if (1 << bitWidth.index == width) {
570*890232f2SAndroid Build Coastguard Worker        return bitWidth;
571*890232f2SAndroid Build Coastguard Worker      }
572*890232f2SAndroid Build Coastguard Worker    }
573*890232f2SAndroid Build Coastguard Worker    throw StateError(
574*890232f2SAndroid Build Coastguard Worker        'Element is of unknown. Size: $size at index: $index. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new');
575*890232f2SAndroid Build Coastguard Worker  }
576*890232f2SAndroid Build Coastguard Worker
577*890232f2SAndroid Build Coastguard Worker  List<int> asU8List(BitWidth width) {
578*890232f2SAndroid Build Coastguard Worker    if (ValueTypeUtils.isNumber(_type)) {
579*890232f2SAndroid Build Coastguard Worker      if (_type == ValueType.Float) {
580*890232f2SAndroid Build Coastguard Worker        if (width == BitWidth.width32) {
581*890232f2SAndroid Build Coastguard Worker          final result = ByteData(4);
582*890232f2SAndroid Build Coastguard Worker          result.setFloat32(0, _value as double, Endian.little);
583*890232f2SAndroid Build Coastguard Worker          return result.buffer.asUint8List();
584*890232f2SAndroid Build Coastguard Worker        } else {
585*890232f2SAndroid Build Coastguard Worker          final result = ByteData(8);
586*890232f2SAndroid Build Coastguard Worker          result.setFloat64(0, _value as double, Endian.little);
587*890232f2SAndroid Build Coastguard Worker          return result.buffer.asUint8List();
588*890232f2SAndroid Build Coastguard Worker        }
589*890232f2SAndroid Build Coastguard Worker      } else {
590*890232f2SAndroid Build Coastguard Worker        switch (width) {
591*890232f2SAndroid Build Coastguard Worker          case BitWidth.width8:
592*890232f2SAndroid Build Coastguard Worker            final result = ByteData(1);
593*890232f2SAndroid Build Coastguard Worker            result.setInt8(0, _value as int);
594*890232f2SAndroid Build Coastguard Worker            return result.buffer.asUint8List();
595*890232f2SAndroid Build Coastguard Worker          case BitWidth.width16:
596*890232f2SAndroid Build Coastguard Worker            final result = ByteData(2);
597*890232f2SAndroid Build Coastguard Worker            result.setInt16(0, _value as int, Endian.little);
598*890232f2SAndroid Build Coastguard Worker            return result.buffer.asUint8List();
599*890232f2SAndroid Build Coastguard Worker          case BitWidth.width32:
600*890232f2SAndroid Build Coastguard Worker            final result = ByteData(4);
601*890232f2SAndroid Build Coastguard Worker            result.setInt32(0, _value as int, Endian.little);
602*890232f2SAndroid Build Coastguard Worker            return result.buffer.asUint8List();
603*890232f2SAndroid Build Coastguard Worker          case BitWidth.width64:
604*890232f2SAndroid Build Coastguard Worker            final result = ByteData(8);
605*890232f2SAndroid Build Coastguard Worker            result.setInt64(0, _value as int, Endian.little);
606*890232f2SAndroid Build Coastguard Worker            return result.buffer.asUint8List();
607*890232f2SAndroid Build Coastguard Worker        }
608*890232f2SAndroid Build Coastguard Worker      }
609*890232f2SAndroid Build Coastguard Worker    }
610*890232f2SAndroid Build Coastguard Worker    if (_type == ValueType.Null) {
611*890232f2SAndroid Build Coastguard Worker      final result = ByteData(1);
612*890232f2SAndroid Build Coastguard Worker      result.setInt8(0, 0);
613*890232f2SAndroid Build Coastguard Worker      return result.buffer.asUint8List();
614*890232f2SAndroid Build Coastguard Worker    }
615*890232f2SAndroid Build Coastguard Worker    if (_type == ValueType.Bool) {
616*890232f2SAndroid Build Coastguard Worker      final result = ByteData(1);
617*890232f2SAndroid Build Coastguard Worker      result.setInt8(0, _value as bool ? 1 : 0);
618*890232f2SAndroid Build Coastguard Worker      return result.buffer.asUint8List();
619*890232f2SAndroid Build Coastguard Worker    }
620*890232f2SAndroid Build Coastguard Worker
621*890232f2SAndroid Build Coastguard Worker    throw StateError(
622*890232f2SAndroid Build Coastguard Worker        'Unexpected type: $_type. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new');
623*890232f2SAndroid Build Coastguard Worker  }
624*890232f2SAndroid Build Coastguard Worker
625*890232f2SAndroid Build Coastguard Worker  ValueType get type {
626*890232f2SAndroid Build Coastguard Worker    return _type;
627*890232f2SAndroid Build Coastguard Worker  }
628*890232f2SAndroid Build Coastguard Worker
629*890232f2SAndroid Build Coastguard Worker  BitWidth get width {
630*890232f2SAndroid Build Coastguard Worker    return _width;
631*890232f2SAndroid Build Coastguard Worker  }
632*890232f2SAndroid Build Coastguard Worker
633*890232f2SAndroid Build Coastguard Worker  bool get isOffset {
634*890232f2SAndroid Build Coastguard Worker    return !ValueTypeUtils.isInline(_type);
635*890232f2SAndroid Build Coastguard Worker  }
636*890232f2SAndroid Build Coastguard Worker
637*890232f2SAndroid Build Coastguard Worker  int? get offset => _offset;
638*890232f2SAndroid Build Coastguard Worker
639*890232f2SAndroid Build Coastguard Worker  bool get isFloat32 {
640*890232f2SAndroid Build Coastguard Worker    return _type == ValueType.Float && _width == BitWidth.width32;
641*890232f2SAndroid Build Coastguard Worker  }
642*890232f2SAndroid Build Coastguard Worker}
643*890232f2SAndroid Build Coastguard Worker
644*890232f2SAndroid Build Coastguard Workerclass _StackPointer {
645*890232f2SAndroid Build Coastguard Worker  int stackPosition;
646*890232f2SAndroid Build Coastguard Worker  bool isVector;
647*890232f2SAndroid Build Coastguard Worker
648*890232f2SAndroid Build Coastguard Worker  _StackPointer(this.stackPosition, this.isVector);
649*890232f2SAndroid Build Coastguard Worker}
650*890232f2SAndroid Build Coastguard Worker
651*890232f2SAndroid Build Coastguard Workerclass _KeysHash {
652*890232f2SAndroid Build Coastguard Worker  final List<int> keys;
653*890232f2SAndroid Build Coastguard Worker
654*890232f2SAndroid Build Coastguard Worker  const _KeysHash(this.keys);
655*890232f2SAndroid Build Coastguard Worker
656*890232f2SAndroid Build Coastguard Worker  @override
657*890232f2SAndroid Build Coastguard Worker  bool operator ==(Object other) {
658*890232f2SAndroid Build Coastguard Worker    if (other is _KeysHash) {
659*890232f2SAndroid Build Coastguard Worker      if (keys.length != other.keys.length) return false;
660*890232f2SAndroid Build Coastguard Worker      for (var i = 0; i < keys.length; i++) {
661*890232f2SAndroid Build Coastguard Worker        if (keys[i] != other.keys[i]) return false;
662*890232f2SAndroid Build Coastguard Worker      }
663*890232f2SAndroid Build Coastguard Worker      return true;
664*890232f2SAndroid Build Coastguard Worker    }
665*890232f2SAndroid Build Coastguard Worker    return false;
666*890232f2SAndroid Build Coastguard Worker  }
667*890232f2SAndroid Build Coastguard Worker
668*890232f2SAndroid Build Coastguard Worker  @override
669*890232f2SAndroid Build Coastguard Worker  int get hashCode {
670*890232f2SAndroid Build Coastguard Worker    var result = 17;
671*890232f2SAndroid Build Coastguard Worker    for (var i = 0; i < keys.length; i++) {
672*890232f2SAndroid Build Coastguard Worker      result = result * 23 + keys[i];
673*890232f2SAndroid Build Coastguard Worker    }
674*890232f2SAndroid Build Coastguard Worker    return result;
675*890232f2SAndroid Build Coastguard Worker  }
676*890232f2SAndroid Build Coastguard Worker}
677