From 04baf381dfd78ba440cfea3f176fa5eac90019ab Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sat, 11 Dec 2021 09:46:13 +0100 Subject: [PATCH 1/4] refactor: replace flatbuffers fork with 2.0.5 --- generator/lib/src/code_chunks.dart | 63 +- objectbox/lib/flatbuffers/LICENSE | 233 --- objectbox/lib/flatbuffers/README.md | 2 - objectbox/lib/flatbuffers/flat_buffers.dart | 1421 ----------------- .../lib/src/modelinfo/entity_definition.dart | 3 +- .../lib/src/native/bindings/flatbuffers.dart | 2 +- objectbox/pubspec.yaml | 5 +- objectbox/test/flatbuffers_test.dart | 17 +- 8 files changed, 38 insertions(+), 1708 deletions(-) delete mode 100644 objectbox/lib/flatbuffers/LICENSE delete mode 100644 objectbox/lib/flatbuffers/README.md delete mode 100644 objectbox/lib/flatbuffers/flat_buffers.dart diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 1f2d92ec7..6624cc23b 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -10,37 +10,37 @@ class CodeChunks { ModelInfo model, List imports, Pubspec? pubspec) => """ // GENERATED CODE - DO NOT MODIFY BY HAND - + // ignore_for_file: camel_case_types - + import 'dart:typed_data'; - - import 'package:objectbox/flatbuffers/flat_buffers.dart' as fb; + + import 'package:flat_buffers/flat_buffers.dart' as fb; import 'package:objectbox/internal.dart'; // generated code can access "internal" functionality import 'package:objectbox/objectbox.dart';${pubspec?.obxFlutterImport} - + import '${sorted(imports).join("';\n import '")}'; - + export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file - + final _entities = [ ${model.entities.map(createModelEntity).join(',')} ]; - + /// Open an ObjectBox store with the model declared in this file. ${openStore(model, pubspec)} - - /// ObjectBox model definition, pass it to [Store] - Store(getObjectBoxModel()) + + /// ObjectBox model definition, pass it to [Store] - Store(getObjectBoxModel()) ModelDefinition getObjectBoxModel() { ${defineModel(model)} - + final bindings = { ${model.entities.mapIndexed((i, entity) => "${entity.name}: ${entityBinding(i, entity)}").join(",\n")} }; - + return ModelDefinition(model, bindings); } - + ${model.entities.mapIndexed(_metaClass).join("\n")} """; @@ -94,16 +94,16 @@ class CodeChunks { static String createModelEntity(ModelEntity entity) { return ''' ModelEntity( - id: ${createIdUid(entity.id)}, - name: '${entity.name}', - lastPropertyId: ${createIdUid(entity.lastPropertyId)}, - flags: ${entity.flags}, + id: ${createIdUid(entity.id)}, + name: '${entity.name}', + lastPropertyId: ${createIdUid(entity.lastPropertyId)}, + flags: ${entity.flags}, properties: [ ${entity.properties.map(createModelProperty).join(',')} - ], + ], relations: [ ${entity.relations.map(createModelRelation).join(',')} - ], + ], backlinks: [ ${entity.backlinks.map(createModelBacklink).join(',')} ] @@ -122,9 +122,9 @@ class CodeChunks { } return ''' ModelProperty( - id: ${createIdUid(property.id)}, - name: '${property.name}', - type: ${property.type}, + id: ${createIdUid(property.id)}, + name: '${property.name}', + type: ${property.type}, flags: ${property.flags} $additionalArgs ) @@ -134,8 +134,8 @@ class CodeChunks { static String createModelRelation(ModelRelation relation) { return ''' ModelRelation( - id: ${createIdUid(relation.id)}, - name: '${relation.name}', + id: ${createIdUid(relation.id)}, + name: '${relation.name}', targetId: ${createIdUid(relation.targetId)} ) '''; @@ -144,8 +144,8 @@ class CodeChunks { static String createModelBacklink(ModelBacklink backlink) { return ''' ModelBacklink( - name: '${backlink.name}', - srcEntity: '${backlink.srcEntity}', + name: '${backlink.name}', + srcEntity: '${backlink.srcEntity}', srcField: '${backlink.srcField}' ) '''; @@ -187,9 +187,9 @@ class CodeChunks { // Such ID must already be set, i.e. it could not have been assigned. return '''{ if (object.${propertyFieldName(entity.idProperty)} != id) { - throw ArgumentError('Field ${entity.name}.${propertyFieldName(entity.idProperty)} is read-only ' + throw ArgumentError('Field ${entity.name}.${propertyFieldName(entity.idProperty)} is read-only ' '(final or getter-only) and it was declared to be self-assigned. ' - 'However, the currently inserted object (.${propertyFieldName(entity.idProperty)}=\${object.${propertyFieldName(entity.idProperty)}}) ' + 'However, the currently inserted object (.${propertyFieldName(entity.idProperty)}=\${object.${propertyFieldName(entity.idProperty)}}) ' "doesn't match the inserted ID (ID \$id). " 'You must assign an ID before calling [box.put()].'); } @@ -348,7 +348,10 @@ class CodeChunks { fbReader = 'fb.${_propertyFlatBuffersType[p.type]}Reader()'; return readFieldNonNull('0'); case OBXPropertyType.StringVector: - fbReader = 'fb.ListReader(fb.StringReader(), lazy: false)'; + // TODO `asciiOptimization: true` is for keeping the same behavior as the previous FB fork. + // Check if it still makes sense with the latest Dart/Flutter. + fbReader = + 'fb.ListReader(fb.StringReader(asciiOptimization: true), lazy: false)'; break; default: fbReader = 'fb.${_propertyFlatBuffersType[p.type]}Reader()'; @@ -607,7 +610,7 @@ class CodeChunks { } return ''' - /// [${entity.name}] entity fields to define ObjectBox queries. + /// [${entity.name}] entity fields to define ObjectBox queries. class ${entity.name}_ {${fields.join()}} '''; } diff --git a/objectbox/lib/flatbuffers/LICENSE b/objectbox/lib/flatbuffers/LICENSE deleted file mode 100644 index b2ae013b7..000000000 --- a/objectbox/lib/flatbuffers/LICENSE +++ /dev/null @@ -1,233 +0,0 @@ -The code in lib/flat_buffers.dart is based on code that was releases under the -following license: - -Copyright 2012, the Dart project authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -To the extent permissible, the changes to that code and the other assets in -this package are licensed under the Apache2 license: - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2014 Google Inc. - - 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 - - http://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. diff --git a/objectbox/lib/flatbuffers/README.md b/objectbox/lib/flatbuffers/README.md deleted file mode 100644 index 6e1f5506c..000000000 --- a/objectbox/lib/flatbuffers/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This lib is based on official [FlatBuffers for Dart](https://github.com/google/flatbuffers), -with `_buf/_buffer` backed by C memory (`ffi.Pointer`) to avoid copying. \ No newline at end of file diff --git a/objectbox/lib/flatbuffers/flat_buffers.dart b/objectbox/lib/flatbuffers/flat_buffers.dart deleted file mode 100644 index 500228576..000000000 --- a/objectbox/lib/flatbuffers/flat_buffers.dart +++ /dev/null @@ -1,1421 +0,0 @@ -// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:collection'; -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; - -const int _sizeofUint8 = 1; -const int _sizeofUint16 = 2; -const int _sizeofUint32 = 4; -const int _sizeofUint64 = 8; -const int _sizeofInt8 = 1; -const int _sizeofInt16 = 2; -const int _sizeofInt32 = 4; -const int _sizeofInt64 = 8; -const int _sizeofFloat32 = 4; -const int _sizeofFloat64 = 8; - -/// Callback used to invoke a struct builder's finish method. -/// -/// This callback is used by other struct's `finish` methods to write the nested -/// struct's fields inline. -typedef void StructBuilder(); - -/// Buffer with data and some context about it. -class BufferContext { - final ByteData _buffer; - - ByteData get buffer => _buffer; - - /// Create from a FlatBuffer represented by a list of bytes (uint8). - factory BufferContext.fromBytes(List byteList) => - BufferContext(byteList is Uint8List - ? byteList.buffer.asByteData(byteList.offsetInBytes) - : ByteData.view(Uint8List.fromList(byteList).buffer)); - - /// Create from a FlatBuffer represented by ByteData. - BufferContext(this._buffer); - - @pragma('vm:prefer-inline') - int derefObject(int offset) => offset + _getUint32(offset); - - @pragma('vm:prefer-inline') - Uint8List _asUint8List(int offset, int length) => - _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length); - - @pragma('vm:prefer-inline') - double _getFloat64(int offset) => _buffer.getFloat64(offset, Endian.little); - - @pragma('vm:prefer-inline') - double _getFloat32(int offset) => _buffer.getFloat32(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getInt64(int offset) => _buffer.getInt64(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getInt16(int offset) => _buffer.getInt16(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getInt8(int offset) => _buffer.getInt8(offset); - - @pragma('vm:prefer-inline') - int _getUint64(int offset) => _buffer.getUint64(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getUint32(int offset) => _buffer.getUint32(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little); - - @pragma('vm:prefer-inline') - int _getUint8(int offset) => _buffer.getUint8(offset); -} - -/// Class implemented by typed builders generated by flatc. -abstract class ObjectBuilder { - int? _firstOffset; - - /// Can be used to write the data represented by this builder to the [Builder] - /// and reuse the offset created in multiple tables. - /// - /// Note that this method assumes you call it using the same [Builder] instance - /// every time. The returned offset is only good for the [Builder] used in the - /// first call to this method. - int getOrCreateOffset(Builder fbBuilder) { - _firstOffset ??= finish(fbBuilder); - return _firstOffset!; - } - - /// Writes the data in this helper to the [Builder]. - int finish(Builder fbBuilder); - - /// Convenience method that will create a new [Builder], [finish]es the data, - /// and returns the buffer as a [Uint8List] of bytes. - Uint8List toBytes(); -} - -/// Class that helps building flat buffers. -class Builder { - bool _finished = false; - - final int initialSize; - - /// The list of existing VTable(s). - final List _vTables; - - final bool deduplicateTables; - - ByteData _buf; - - final Allocator _allocator; - - /// The maximum alignment that has been seen so far. If [_buf] has to be - /// reallocated in the future (to insert room at its start for more bytes) the - /// reallocation will need to be a multiple of this many bytes. - int _maxAlign = 1; - - /// The number of bytes that have been written to the buffer so far. The - /// most recently written byte is this many bytes from the end of [_buf]. - int _tail = 0; - - /// The location of the end of the current table, measured in bytes from the - /// end of [_buf]. - int _currentTableEndTail = 0; - - _VTable? _currentVTable; - - /// Map containing all strings that have been written so far. This allows us - /// to avoid duplicating strings. - /// - /// Allocated only if `internStrings` is set to true on the constructor. - Map? _strings; - - /// Creates a new FlatBuffers Builder. - /// - /// `initialSize` is the initial array size in bytes. The [Builder] will - /// automatically grow the array if/as needed. `internStrings`, if set to - /// true, will cause [writeString] to pool strings in the buffer so that - /// identical strings will always use the same offset in tables. - Builder({ - this.initialSize: 1024, - bool internStrings = false, - Allocator allocator = const DefaultAllocator(), - this.deduplicateTables = true, - }) : _allocator = allocator, - _buf = allocator.allocate(initialSize), - _vTables = deduplicateTables ? [] : const [] { - if (internStrings) { - _strings = {}; - } - } - - /// Calculate the finished buffer size (aligned). - @pragma('vm:prefer-inline') - int size() => _tail + ((-_tail) & (_maxAlign - 1)); - - /// Add the [field] with the given boolean [value]. The field is not added if - /// the [value] is equal to [def]. Booleans are stored as 8-bit fields with - /// `0` for `false` and `1` for `true`. - void addBool(int field, bool? value, [bool? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofUint8, 1); - _trackField(field); - _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0); - } - } - - /// Add the [field] with the given 32-bit signed integer [value]. The field is - /// not added if the [value] is equal to [def]. - void addInt32(int field, int? value, [int? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofInt32, 1); - _trackField(field); - _setInt32AtTail(_tail, value); - } - } - - /// Add the [field] with the given 32-bit signed integer [value]. The field is - /// not added if the [value] is equal to [def]. - void addInt16(int field, int? value, [int? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofInt16, 1); - _trackField(field); - _setInt16AtTail(_tail, value); - } - } - - /// Add the [field] with the given 8-bit signed integer [value]. The field is - /// not added if the [value] is equal to [def]. - void addInt8(int field, int? value, [int? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofInt8, 1); - _trackField(field); - _setInt8AtTail(_tail, value); - } - } - - void addStruct(int field, int offset) { - assert(_inVTable); - _trackField(field); - _currentVTable!.addField(field, offset); - } - - /// Add the [field] referencing an object with the given [offset]. - void addOffset(int field, int? offset) { - assert(_inVTable); - if (offset != null) { - _prepare(_sizeofUint32, 1); - _trackField(field); - _setUint32AtTail(_tail, _tail - offset); - } - } - - /// Add the [field] with the given 32-bit unsigned integer [value]. The field - /// is not added if the [value] is equal to [def]. - void addUint32(int field, int? value, [int? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofUint32, 1); - _trackField(field); - _setUint32AtTail(_tail, value); - } - } - - /// Add the [field] with the given 32-bit unsigned integer [value]. The field - /// is not added if the [value] is equal to [def]. - void addUint16(int field, int? value, [int? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofUint16, 1); - _trackField(field); - _setUint16AtTail(_tail, value); - } - } - - /// Add the [field] with the given 8-bit unsigned integer [value]. The field - /// is not added if the [value] is equal to [def]. - void addUint8(int field, int? value, [int? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofUint8, 1); - _trackField(field); - _setUint8AtTail(_tail, value); - } - } - - /// Add the [field] with the given 32-bit float [value]. The field - /// is not added if the [value] is equal to [def]. - void addFloat32(int field, double? value, [double? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofFloat32, 1); - _trackField(field); - _setFloat32AtTail(_tail, value); - } - } - - /// Add the [field] with the given 64-bit double [value]. The field - /// is not added if the [value] is equal to [def]. - void addFloat64(int field, double? value, [double? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofFloat64, 1); - _trackField(field); - _setFloat64AtTail(_tail, value); - } - } - - /// Add the [field] with the given 64-bit unsigned integer [value]. The field - /// is not added if the [value] is equal to [def]. - void addUint64(int field, int? value, [double? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofUint64, 1); - _trackField(field); - _setUint64AtTail(_tail, value); - } - } - - /// Add the [field] with the given 64-bit unsigned integer [value]. The field - /// is not added if the [value] is equal to [def]. - void addInt64(int field, int? value, [double? def]) { - assert(_inVTable); - if (value != null && value != def) { - _prepare(_sizeofInt64, 1); - _trackField(field); - _setInt64AtTail(_tail, value); - } - } - - /// End the current table and return its offset. - int endTable() { - assert(_inVTable); - // Prepare for writing the VTable. - _prepare(_sizeofInt32, 1); - int tableTail = _tail; - // Prepare the size of the current table. - final currentVTable = _currentVTable!; - currentVTable.tableSize = tableTail - _currentTableEndTail; - // Prepare the VTable to use for the current table. - int? vTableTail; - { - currentVTable.computeFieldOffsets(tableTail); - - // Try to find an existing compatible VTable. - if (deduplicateTables) { - // Search backward - more likely to have recently used one - for (int i = _vTables.length - 1; i >= 0; i--) { - final int vt2Offset = _vTables[i]; - final int vt2Start = _buf.lengthInBytes - vt2Offset; - final int vt2Size = _buf.getUint16(vt2Start, Endian.little); - - if (currentVTable._vTableSize == vt2Size && - currentVTable._offsetsMatch(vt2Start, _buf)) { - vTableTail = vt2Offset; - break; - } - } - } - - // Write a new VTable. - if (vTableTail == null) { - _prepare(_sizeofUint16, _currentVTable!.numOfUint16); - vTableTail = _tail; - currentVTable.tail = vTableTail; - currentVTable.output(_buf, _buf.lengthInBytes - _tail); - if (deduplicateTables) _vTables.add(currentVTable.tail); - } - } - // Set the VTable offset. - _setInt32AtTail(tableTail, vTableTail - tableTail); - // Done with this table. - _currentVTable = null; - return tableTail; - } - - /// Returns the finished buffer. You must call [finish] before accessing this. - @pragma('vm:prefer-inline') - Uint8List get buffer { - assert(_finished); - final finishedSize = size(); - return _buf.buffer - .asUint8List(_buf.lengthInBytes - finishedSize, finishedSize); - } - - /// Finish off the creation of the buffer. The given [offset] is used as the - /// root object offset, and usually references directly or indirectly every - /// written object. If [fileIdentifier] is specified (and not `null`), it is - /// interpreted as a 4-byte Latin-1 encoded string that should be placed at - /// bytes 4-7 of the file. - void finish(int offset, [String? fileIdentifier]) { - final sizeBeforePadding = size(); - final requiredBytes = _sizeofUint32 * (fileIdentifier == null ? 1 : 2); - _prepare(max(requiredBytes, _maxAlign), 1); - final finishedSize = size(); - _setUint32AtTail(finishedSize, finishedSize - offset); - if (fileIdentifier != null) { - for (int i = 0; i < 4; i++) { - _setUint8AtTail( - finishedSize - _sizeofUint32 - i, fileIdentifier.codeUnitAt(i)); - } - } - - // zero out the added padding - for (var i = sizeBeforePadding + 1; - i <= finishedSize - requiredBytes; - i++) { - _setUint8AtTail(i, 0); - } - _finished = true; - } - - /// Writes a Float64 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putFloat64(double value) { - _prepare(_sizeofFloat64, 1); - _setFloat32AtTail(_tail, value); - } - - /// Writes a Float32 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putFloat32(double value) { - _prepare(_sizeofFloat32, 1); - _setFloat32AtTail(_tail, value); - } - - /// Writes a Int64 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putInt64(int value) { - _prepare(_sizeofInt64, 1); - _setInt64AtTail(_tail, value); - } - - /// Writes a Uint32 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putInt32(int value) { - _prepare(_sizeofInt32, 1); - _setInt32AtTail(_tail, value); - } - - /// Writes a Uint16 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putInt16(int value) { - _prepare(_sizeofInt16, 1); - _setInt16AtTail(_tail, value); - } - - /// Writes a Uint8 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putInt8(int value) { - _prepare(_sizeofInt8, 1); - _buf.setInt8(_buf.lengthInBytes - _tail, value); - } - - /// Writes a Uint64 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putUint64(int value) { - _prepare(_sizeofUint64, 1); - _setUint64AtTail(_tail, value); - } - - /// Writes a Uint32 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putUint32(int value) { - _prepare(_sizeofUint32, 1); - _setUint32AtTail(_tail, value); - } - - /// Writes a Uint16 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putUint16(int value) { - _prepare(_sizeofUint16, 1); - _setUint16AtTail(_tail, value); - } - - /// Writes a Uint8 to the tail of the buffer after preparing space for it. - /// - /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. - void putUint8(int value) { - _prepare(_sizeofUint8, 1); - _buf.setUint8(_buf.lengthInBytes - _tail, value); - } - - /// Reset the builder and make it ready for filling a new buffer. - void reset() { - _finished = false; - _maxAlign = 1; - _tail = 0; - _currentVTable = null; - if (deduplicateTables) _vTables.clear(); - if (_strings != null) { - _strings = {}; - } - } - - /// Start a new table. Must be finished with [endTable] invocation. - void startTable(int numFields) { - assert(!_inVTable); // Inline tables are not supported. - _currentVTable = _VTable(numFields); - _currentTableEndTail = _tail; - } - - /// Finish a Struct vector. Most callers should preferto use [writeListOfStructs]. - /// - /// Most callers should prefer [writeListOfStructs]. - int endStructVector(int count) { - putUint32(count); - return _tail; - } - - /// Writes a list of Structs to the buffer, returning the offset - int writeListOfStructs(List structBuilders) { - assert(!_inVTable); - for (int i = structBuilders.length - 1; i >= 0; i--) { - structBuilders[i].finish(this); - } - return endStructVector(structBuilders.length); - } - - /// Write the given list of [values]. - int writeList(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1 + values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setUint32AtTail(tail, tail - value); - tail -= _sizeofUint32; - } - return result; - } - - /// Write the given list of 64-bit float [values]. - int writeListFloat64(List values) { - assert(!_inVTable); - _prepare(_sizeofFloat64, values.length, additionalBytes: _sizeofUint32); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (double value in values) { - _setFloat64AtTail(tail, value); - tail -= _sizeofFloat64; - } - return result; - } - - /// Write the given list of 32-bit float [values]. - int writeListFloat32(List values) { - assert(!_inVTable); - _prepare(_sizeofFloat32, 1 + values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (double value in values) { - _setFloat32AtTail(tail, value); - tail -= _sizeofFloat32; - } - return result; - } - - /// Write the given list of signed 64-bit integer [values]. - int writeListInt64(List values) { - assert(!_inVTable); - _prepare(_sizeofInt64, values.length, additionalBytes: _sizeofUint32); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setInt64AtTail(tail, value); - tail -= _sizeofInt64; - } - return result; - } - - /// Write the given list of signed 64-bit integer [values]. - int writeListUint64(List values) { - assert(!_inVTable); - _prepare(_sizeofUint64, values.length, additionalBytes: _sizeofUint32); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setUint64AtTail(tail, value); - tail -= _sizeofUint64; - } - return result; - } - - /// Write the given list of signed 32-bit integer [values]. - int writeListInt32(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1 + values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setInt32AtTail(tail, value); - tail -= _sizeofInt32; - } - return result; - } - - /// Write the given list of unsigned 32-bit integer [values]. - int writeListUint32(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1 + values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setUint32AtTail(tail, value); - tail -= _sizeofUint32; - } - return result; - } - - /// Write the given list of signed 16-bit integer [values]. - int writeListInt16(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setInt16AtTail(tail, value); - tail -= _sizeofInt16; - } - return result; - } - - /// Write the given list of unsigned 16-bit integer [values]. - int writeListUint16(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setUint16AtTail(tail, value); - tail -= _sizeofUint16; - } - return result; - } - - /// Write the given list of bools as unsigend 8-bit integer [values]. - int writeListBool(List values) { - return writeListUint8(values.map((b) => b ? 1 : 0).toList()); - } - - /// Write the given list of signed 8-bit integer [values]. - int writeListInt8(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1, additionalBytes: values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setInt8AtTail(tail, value); - tail -= _sizeofUint8; - } - return result; - } - - /// Write the given list of unsigned 8-bit integer [values]. - int writeListUint8(List values) { - assert(!_inVTable); - _prepare(_sizeofUint32, 1, additionalBytes: values.length); - final int result = _tail; - int tail = _tail; - _setUint32AtTail(tail, values.length); - tail -= _sizeofUint32; - for (int value in values) { - _setUint8AtTail(tail, value); - tail -= _sizeofUint8; - } - return result; - } - - /// Write the given string [value] and return its offset. - /// - /// Dart strings are UTF-16 but must be stored as UTF-8 in FlatBuffers. - /// If the given string consists only of ASCII characters, you can indicate - /// enable [asciiOptimization]. In this mode, [writeString()] first tries to - /// copy the ASCII string directly to the output buffer and if that fails - /// (because there are no-ASCII characters in the string) it falls back and to - /// the default UTF-16 -> UTF-8 conversion (with slight performance penalty). - int writeString(String value, {bool asciiOptimization = false}) { - assert(!_inVTable); - if (_strings != null) { - return _strings! - .putIfAbsent(value, () => _writeString(value, asciiOptimization)); - } else { - return _writeString(value, asciiOptimization); - } - } - - int _writeString(String value, bool asciiOptimization) { - if (asciiOptimization) { - // [utf8.encode()] is slow (up to at least Dart SDK 2.13). If the given - // string is ASCII we can just write it directly, without any conversion. - final originalTail = _tail; - if (_tryWriteASCIIString(value)) return _tail; - // if non-ASCII: reset the output buffer position for [_writeUTFString()] - _tail = originalTail; - } - _writeUTFString(value); - return _tail; - } - - // Try to write the string as ASCII, return false if there's a non-ascii char. - @pragma('vm:prefer-inline') - bool _tryWriteASCIIString(String value) { - _prepare(4, 1, additionalBytes: value.length + 1); - final length = value.length; - var offset = _buf.lengthInBytes - _tail + 4; - for (var i = 0; i < length; i++) { - // utf16 code unit, e.g. for '†' it's [0x20 0x20], which is 8224 decimal. - // ASCII characters go from 0x00 to 0x7F (which is 0 to 127 decimal). - final char = value.codeUnitAt(i); - if ((char & ~0x7F) != 0) { - return false; - } - _buf.setUint8(offset++, char); - } - _buf.setUint8(offset, 0); // trailing zero - _setUint32AtTail(_tail, value.length); - return true; - } - - @pragma('vm:prefer-inline') - void _writeUTFString(String value) { - final bytes = utf8.encode(value) as Uint8List; - final length = bytes.length; - _prepare(4, 1, additionalBytes: length + 1); - _setUint32AtTail(_tail, length); - var offset = _buf.lengthInBytes - _tail + 4; - for (int i = 0; i < length; i++) { - _buf.setUint8(offset++, bytes[i]); - } - _buf.setUint8(offset, 0); // trailing zero - } - - /// Used to assert whether a "Table" is currently being built. - /// - /// If you hit `assert(!_inVTable())`, you're trying to add table fields - /// without starting a table with [Builder.startTable()]. - /// - /// If you hit `assert(_inVTable())`, you're trying to construct a - /// Table/Vector/String during the construction of its parent table, - /// between the MyTableBuilder and [Builder.endTable()]. - /// Move the creation of these sub-objects to before the MyTableBuilder to - /// not get this assert. - @pragma('vm:prefer-inline') - bool get _inVTable => _currentVTable != null; - - /// The number of bytes that have been written to the buffer so far. The - /// most recently written byte is this many bytes from the end of the buffer. - @pragma('vm:prefer-inline') - int get offset => _tail; - - /// Zero-pads the buffer, which may be required for some struct layouts. - @pragma('vm:prefer-inline') - void pad(int howManyBytes) { - for (int i = 0; i < howManyBytes; i++) putUint8(0); - } - - /// Prepare for writing the given `count` of scalars of the given `size`. - /// Additionally allocate the specified `additionalBytes`. Update the current - /// tail pointer to point at the allocated space. - @pragma('vm:prefer-inline') - void _prepare(int size, int count, {int additionalBytes = 0}) { - assert(!_finished); - // Update the alignment. - if (_maxAlign < size) { - _maxAlign = size; - } - // Prepare amount of required space. - int dataSize = size * count + additionalBytes; - int alignDelta = (-(_tail + dataSize)) & (size - 1); - int bufSize = alignDelta + dataSize; - // Ensure that we have the required amount of space. - { - int oldCapacity = _buf.lengthInBytes; - if (_tail + bufSize > oldCapacity) { - int desiredNewCapacity = (oldCapacity + bufSize) * 2; - int deltaCapacity = desiredNewCapacity - oldCapacity; - deltaCapacity += (-deltaCapacity) & (_maxAlign - 1); - int newCapacity = oldCapacity + deltaCapacity; - _buf = _allocator.resize(_buf, newCapacity, _tail, 0); - } - } - - // zero out the added padding - for (var i = _tail + 1; i <= _tail + alignDelta; i++) { - _setUint8AtTail(i, 0); - } - - // Update the tail pointer. - _tail += bufSize; - } - - /// Record the offset of the given [field]. - @pragma('vm:prefer-inline') - void _trackField(int field) => _currentVTable!.addField(field, _tail); - - @pragma('vm:prefer-inline') - void _setFloat64AtTail(int tail, double x) => - _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setFloat32AtTail(int tail, double x) => - _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setUint64AtTail(int tail, int x) => - _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setInt64AtTail(int tail, int x) => - _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setInt32AtTail(int tail, int x) => - _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setUint32AtTail(int tail, int x) => - _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setInt16AtTail(int tail, int x) => - _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setUint16AtTail(int tail, int x) => - _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little); - - @pragma('vm:prefer-inline') - void _setInt8AtTail(int tail, int x) => - _buf.setInt8(_buf.lengthInBytes - tail, x); - - @pragma('vm:prefer-inline') - void _setUint8AtTail(int tail, int x) => - _buf.setUint8(_buf.lengthInBytes - tail, x); -} - -/// Reader of lists of boolean values. -/// -/// The returned unmodifiable lists lazily read values on access. -class BoolListReader extends Reader> { - const BoolListReader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint32; - - @override - @pragma('vm:prefer-inline') - List read(BufferContext bc, int offset) => - _FbBoolList(bc, bc.derefObject(offset)); -} - -/// The reader of booleans. -class BoolReader extends Reader { - const BoolReader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint8; - - @override - @pragma('vm:prefer-inline') - bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0; -} - -/// The reader of lists of 64-bit float values. -/// -/// The returned unmodifiable lists lazily read values on access. -class Float64ListReader extends Reader> { - const Float64ListReader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofFloat64; - - @override - @pragma('vm:prefer-inline') - List read(BufferContext bc, int offset) => - new _FbFloat64List(bc, bc.derefObject(offset)); -} - -class Float32ListReader extends Reader> { - const Float32ListReader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofFloat32; - - @override - @pragma('vm:prefer-inline') - List read(BufferContext bc, int offset) => - new _FbFloat32List(bc, bc.derefObject(offset)); -} - -class Float64Reader extends Reader { - const Float64Reader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofFloat64; - - @override - @pragma('vm:prefer-inline') - double read(BufferContext bc, int offset) => bc._getFloat64(offset); -} - -class Float32Reader extends Reader { - const Float32Reader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofFloat32; - - @override - @pragma('vm:prefer-inline') - double read(BufferContext bc, int offset) => bc._getFloat32(offset); -} - -class Int64Reader extends Reader { - const Int64Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofInt64; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getInt64(offset); -} - -/// The reader of signed 32-bit integers. -class Int32Reader extends Reader { - const Int32Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofInt32; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getInt32(offset); -} - -/// The reader of signed 32-bit integers. -class Int16Reader extends Reader { - const Int16Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofInt16; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getInt16(offset); -} - -/// The reader of 8-bit signed integers. -class Int8Reader extends Reader { - const Int8Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofInt8; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getInt8(offset); -} - -/// The reader of lists of objects. Lazy by default - see [lazy]. -class ListReader extends Reader> { - final Reader _elementReader; - - /// Enables lazy reading of the list - /// - /// If true, the returned unmodifiable list lazily reads objects on access. - /// Therefore, the underlying buffer must not change while accessing the list. - /// - /// If false, reads the whole list immediately on access. - final bool lazy; - - const ListReader(this._elementReader, {this.lazy = true}); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint32; - - @override - List read(BufferContext bc, int offset) { - final listOffset = bc.derefObject(offset); - return lazy - ? _FbGenericList(_elementReader, bc, listOffset) - : List.generate( - bc.buffer.getUint32(listOffset, Endian.little), - (int index) => _elementReader.read( - bc, listOffset + size + _elementReader.size * index), - growable: true); - } -} - -/// Object that can read a value at a [BufferContext]. -abstract class Reader { - const Reader(); - - /// The size of the value in bytes. - int get size; - - /// Read the value at the given [offset] in [bc]. - T read(BufferContext bc, int offset); - - /// Read the value of the given [field] in the given [object]. - @pragma('vm:prefer-inline') - T vTableGet(BufferContext object, int offset, int field, T defaultValue) { - int fieldOffset = _vTableFieldOffset(object, offset, field); - return fieldOffset == 0 ? defaultValue : read(object, offset + fieldOffset); - } - - /// Read the value of the given [field] in the given [object]. - @pragma('vm:prefer-inline') - T? vTableGetNullable(BufferContext object, int offset, int field) { - int fieldOffset = _vTableFieldOffset(object, offset, field); - return fieldOffset == 0 ? null : read(object, offset + fieldOffset); - } - - @pragma('vm:prefer-inline') - int _vTableFieldOffset(BufferContext object, int offset, int field) { - int vTableSOffset = object._getInt32(offset); - int vTableOffset = offset - vTableSOffset; - int vTableSize = object._getUint16(vTableOffset); - if (field >= vTableSize) return 0; - return object._getUint16(vTableOffset + field); - } -} - -/// The reader of string values. -class StringReader extends Reader { - const StringReader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => 4; - - @override - @pragma('vm:prefer-inline') - String read(BufferContext bc, int offset) { - int strOffset = bc.derefObject(offset); - int length = bc._getUint32(strOffset); - Uint8List bytes = bc._asUint8List(strOffset + 4, length); - if (_isLatin(bytes)) { - return new String.fromCharCodes(bytes); - } - return utf8.decode(bytes); - } - - @pragma('vm:prefer-inline') - static bool _isLatin(Uint8List bytes) { - int length = bytes.length; - for (int i = 0; i < length; i++) { - if (bytes[i] > 127) { - return false; - } - } - return true; - } -} - -/// An abstract reader for structs. -abstract class StructReader extends Reader { - const StructReader(); - - /// Return the object at `offset`. - T createObject(BufferContext bc, int offset); - - T read(BufferContext bp, int offset) { - return createObject(bp, offset); - } -} - -/// An abstract reader for tables. -abstract class TableReader extends Reader { - const TableReader(); - - @override - @pragma('vm:prefer-inline') - int get size => 4; - - /// Return the object at [offset]. - T createObject(BufferContext bc, int offset); - - @override - T read(BufferContext bp, int offset) { - int objectOffset = bp.derefObject(offset); - return createObject(bp, objectOffset); - } -} - -/// Reader of lists of unsigned 32-bit integer values. -/// -/// The returned unmodifiable lists lazily read values on access. -class Uint32ListReader extends Reader> { - const Uint32ListReader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint32; - - @override - @pragma('vm:prefer-inline') - List read(BufferContext bc, int offset) => - _FbUint32List(bc, bc.derefObject(offset)); -} - -/// The reader of unsigned 64-bit integers. -/// -/// WARNING: May have compatibility issues with JavaScript -class Uint64Reader extends Reader { - const Uint64Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint64; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getUint64(offset); -} - -/// The reader of unsigned 32-bit integers. -class Uint32Reader extends Reader { - const Uint32Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint32; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getUint32(offset); -} - -/// Reader of lists of unsigned 32-bit integer values. -/// -/// The returned unmodifiable lists lazily read values on access. -class Uint16ListReader extends Reader> { - const Uint16ListReader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint32; - - @override - @pragma('vm:prefer-inline') - List read(BufferContext bc, int offset) => - _FbUint16List(bc, bc.derefObject(offset)); -} - -/// The reader of unsigned 32-bit integers. -class Uint16Reader extends Reader { - const Uint16Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint16; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getUint16(offset); -} - -/// Reader of lists of unsigned 8-bit integer values. -/// -/// The returned unmodifiable lists lazily read values on access. -class Uint8ListReader extends Reader> { - const Uint8ListReader(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint32; - - @override - @pragma('vm:prefer-inline') - List read(BufferContext bc, int offset) => - _FbUint8List(bc, bc.derefObject(offset)); -} - -/// The reader of unsigned 8-bit integers. -class Uint8Reader extends Reader { - const Uint8Reader() : super(); - - @override - @pragma('vm:prefer-inline') - int get size => _sizeofUint8; - - @override - @pragma('vm:prefer-inline') - int read(BufferContext bc, int offset) => bc._getUint8(offset); -} - -/// The list backed by 64-bit values - Uint64 length and Float64. -class _FbFloat64List extends _FbList { - _FbFloat64List(BufferContext bc, int offset) : super(bc, offset); - - @override - @pragma('vm:prefer-inline') - double operator [](int i) => bc._getFloat64(offset + 4 + 8 * i); -} - -/// The list backed by 32-bit values - Float32. -class _FbFloat32List extends _FbList { - _FbFloat32List(BufferContext bc, int offset) : super(bc, offset); - - @override - @pragma('vm:prefer-inline') - double operator [](int i) => bc._getFloat32(offset + 4 + 4 * i); -} - -/// List backed by a generic object which may have any size. -class _FbGenericList extends _FbList { - final Reader elementReader; - - List? _items; - - _FbGenericList(this.elementReader, BufferContext bp, int offset) - : super(bp, offset); - - @override - @pragma('vm:prefer-inline') - E operator [](int i) { - _items ??= List.filled(length, null); - E? item = _items![i]; - if (item == null) { - item = elementReader.read(bc, offset + 4 + elementReader.size * i); - _items![i] = item; - } - return item!; - } -} - -/// The base class for immutable lists read from flat buffers. -abstract class _FbList extends Object with ListMixin implements List { - final BufferContext bc; - final int offset; - int? _length; - - _FbList(this.bc, this.offset); - - @override - @pragma('vm:prefer-inline') - int get length => _length ??= bc._getUint32(offset); - - @override - void set length(int i) => - throw new StateError('Attempt to modify immutable list'); - - @override - void operator []=(int i, E e) => - throw new StateError('Attempt to modify immutable list'); -} - -/// List backed by 32-bit unsigned integers. -class _FbUint32List extends _FbList { - _FbUint32List(BufferContext bc, int offset) : super(bc, offset); - - @override - @pragma('vm:prefer-inline') - int operator [](int i) => bc._getUint32(offset + 4 + 4 * i); -} - -/// List backed by 16-bit unsigned integers. -class _FbUint16List extends _FbList { - _FbUint16List(BufferContext bc, int offset) : super(bc, offset); - - @override - @pragma('vm:prefer-inline') - int operator [](int i) => bc._getUint16(offset + 4 + 2 * i); -} - -/// List backed by 8-bit unsigned integers. -class _FbUint8List extends _FbList { - _FbUint8List(BufferContext bc, int offset) : super(bc, offset); - - @override - @pragma('vm:prefer-inline') - int operator [](int i) => bc._getUint8(offset + 4 + i); -} - -/// List backed by 8-bit unsigned integers. -class _FbBoolList extends _FbList { - _FbBoolList(BufferContext bc, int offset) : super(bc, offset); - - @override - @pragma('vm:prefer-inline') - bool operator [](int i) => bc._getUint8(offset + 4 + i) == 1 ? true : false; -} - -/// Class that describes the structure of a table. -class _VTable { - static const int _metadataLength = 4; - - final int numFields; - - // Note: fieldOffsets start as "tail offsets" and are then transformed by - // [computeFieldOffsets()] to actual offsets when a table is finished. - final Uint32List fieldOffsets; - bool offsetsComputed = false; - - _VTable(this.numFields) : fieldOffsets = Uint32List(numFields); - - /// The size of the table that uses this VTable. - int tableSize = 0; - - /// The tail of this VTable. It is used to share the same VTable between - /// multiple tables of identical structure. - int tail = 0; - - int get _vTableSize => numOfUint16 * _sizeofUint16; - - int get numOfUint16 => 1 + 1 + numFields; - - @pragma('vm:prefer-inline') - void addField(int field, int offset) { - assert(!offsetsComputed); - assert(offset > 0); // it's impossible for field to start at the buffer end - assert(offset <= 4294967295); // uint32 max - fieldOffsets[field] = offset; - } - - @pragma('vm:prefer-inline') - bool _offsetsMatch(int vt2Start, ByteData buf) { - assert(offsetsComputed); - for (int i = 0; i < numFields; i++) { - if (fieldOffsets[i] != - buf.getUint16(vt2Start + _metadataLength + (2 * i), Endian.little)) { - return false; - } - } - return true; - } - - /// Fill the [fieldOffsets] field. - @pragma('vm:prefer-inline') - void computeFieldOffsets(int tableTail) { - assert(!offsetsComputed); - offsetsComputed = true; - for (var i = 0; i < numFields; i++) { - if (fieldOffsets[i] != 0) { - fieldOffsets[i] = tableTail - fieldOffsets[i]; - } - } - } - - /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit - /// and have at least [numOfUint16] 16-bit words available. - @pragma('vm:prefer-inline') - void output(ByteData buf, int bufOffset) { - assert(offsetsComputed); - // VTable size. - buf.setUint16(bufOffset, numOfUint16 * 2, Endian.little); - bufOffset += 2; - // Table size. - buf.setUint16(bufOffset, tableSize, Endian.little); - bufOffset += 2; - // Field offsets. - for (int i = 0; i < numFields; i++) { - buf.setUint16(bufOffset, fieldOffsets[i], Endian.little); - bufOffset += 2; - } - } -} - -/// The interface that [Builder] uses to allocate buffers for encoding. -abstract class Allocator { - const Allocator(); - - /// Allocate a [ByteData] buffer of a given size. - ByteData allocate(int size); - - /// Free the given [ByteData] buffer previously allocated by [allocate]. - void deallocate(ByteData data); - - /// Reallocate [newSize] bytes of memory, replacing the old [oldData]. This - /// grows downwards, and is intended specifically for use with [Builder]. - /// Params [inUseBack] and [inUseFront] indicate how much of [oldData] is - /// actually in use at each end, and needs to be copied. - ByteData resize( - ByteData oldData, int newSize, int inUseBack, int inUseFront) { - final newData = allocate(newSize); - _copyDownward(oldData, newData, inUseBack, inUseFront); - deallocate(oldData); - return newData; - } - - /// Called by [resize] to copy memory from [oldData] to [newData]. Only - /// memory of size [inUseFront] and [inUseBack] will be copied from the front - /// and back of the old memory allocation. - void _copyDownward( - ByteData oldData, ByteData newData, int inUseBack, int inUseFront) { - if (inUseBack != 0) { - newData.buffer.asUint8List().setAll( - newData.lengthInBytes - inUseBack, - oldData.buffer.asUint8List().getRange( - oldData.lengthInBytes - inUseBack, oldData.lengthInBytes)); - } - if (inUseFront != 0) { - newData.buffer - .asUint8List() - .setAll(0, oldData.buffer.asUint8List().getRange(0, inUseFront)); - } - } -} - -class DefaultAllocator extends Allocator { - const DefaultAllocator(); - - @override - ByteData allocate(int size) => ByteData(size); - - @override - void deallocate(ByteData _) { - // nothing to do, it's garbage-collected - } -} diff --git a/objectbox/lib/src/modelinfo/entity_definition.dart b/objectbox/lib/src/modelinfo/entity_definition.dart index 139b0445c..4902632c2 100644 --- a/objectbox/lib/src/modelinfo/entity_definition.dart +++ b/objectbox/lib/src/modelinfo/entity_definition.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; -import '../../flatbuffers/flat_buffers.dart' as fb; +import 'package:flat_buffers/flat_buffers.dart' as fb; + import '../relations/info.dart'; import '../relations/to_many.dart'; import '../relations/to_one.dart'; diff --git a/objectbox/lib/src/native/bindings/flatbuffers.dart b/objectbox/lib/src/native/bindings/flatbuffers.dart index ddd0d4cc7..8c5e8ddc9 100644 --- a/objectbox/lib/src/native/bindings/flatbuffers.dart +++ b/objectbox/lib/src/native/bindings/flatbuffers.dart @@ -2,8 +2,8 @@ import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; +import 'package:flat_buffers/flat_buffers.dart' as fb; -import '../../../flatbuffers/flat_buffers.dart' as fb; import 'nativemem.dart'; // ignore_for_file: public_member_api_docs diff --git a/objectbox/pubspec.yaml b/objectbox/pubspec.yaml index 8947fba24..5f60e46f9 100644 --- a/objectbox/pubspec.yaml +++ b/objectbox/pubspec.yaml @@ -12,8 +12,7 @@ environment: dependencies: collection: ^1.15.0 # take care updating flatbuffers - keep aligned with other bindings - # currently using a forked version in lib/flatbuffers, except for tests (see dev_dependencies) - # flat_buffers: 1.12.0 + flat_buffers: 2.0.5 ffi: ^1.0.0 meta: ^1.3.0 path: ^1.8.0 @@ -24,8 +23,6 @@ dev_dependencies: pedantic: ^1.11.0 test: ^1.16.5 ffigen: ^2.4.2 - # No null-safety compatible version yet and we only need it in tests. - # flat_buffers: 1.12.0 # NOTE: remove before publishing dependency_overrides: diff --git a/objectbox/test/flatbuffers_test.dart b/objectbox/test/flatbuffers_test.dart index b664905e3..4d01bf340 100644 --- a/objectbox/test/flatbuffers_test.dart +++ b/objectbox/test/flatbuffers_test.dart @@ -1,9 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; -import 'package:objectbox/flatbuffers/flat_buffers.dart' as fb; -// Note: upstream flatbuffers currently doesn't have a null-safe version -// import 'package:flat_buffers/flat_buffers.dart' as fb_upstream; +import 'package:flat_buffers/flat_buffers.dart' as fb; import 'package:objectbox/internal.dart'; import 'package:objectbox/src/native/bindings/flatbuffers.dart'; import 'package:objectbox/src/native/bindings/nativemem.dart'; @@ -21,13 +19,6 @@ Uint8List addFbData(fb.Builder fbb) { return fbb.buffer; } -// Uint8List addFbDataUpstream(fb_upstream.Builder fbb) { -// fbb.startTable(); -// fbb.addInt32(0, 24); -// fbb.addInt64(1, 42); -// return fbb.finish(fbb.endTable()); -// } - void main() { test('custom flatbuffers builder', () { [1024, 1].forEach((initialSize) { @@ -37,14 +28,8 @@ void main() { final list1a = addFbData(fb1.fbb); final list1b = fb1.bufPtr.cast().asTypedList(fb1.fbb.size()); - // final fb2 = fb_upstream.Builder(initialSize: initialSize); - // final list2 = addFbDataUpstream(fb2); - printOnFailure(list1a.toString()); printOnFailure(list1b.toString()); - // printOnFailure(list2.toString()); - // expect(list1a, equals(list2)); - // expect(list1b, equals(list2)); // test resetting fb1.fbb.reset(); From 5bc9c78d7bfe4702ff05024e3c47aaf61f05c199 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 12 Dec 2021 10:11:25 +0100 Subject: [PATCH 2/4] perf: use the new optimized FB binary-list readers --- generator/lib/src/code_chunks.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 6624cc23b..44619dc3f 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -207,6 +207,10 @@ class CodeChunks { return "''"; case 'List': return '[]'; + case 'Int8List': + return 'Int8List(0)'; + case 'Uint8List': + return 'Uint8List(0)'; default: throw InvalidGenerationSourceError( 'Cannot figure out default value for field: ${p.fieldType} ${p.name}'); @@ -331,15 +335,8 @@ class CodeChunks { switch (p.type) { case OBXPropertyType.ByteVector: if (['Int8List', 'Uint8List'].contains(p.fieldType)) { - // No need for the eager reader here. We need to call fromList() - // constructor anyway - there's no Int8List.generate() factory. - fbReader = 'fb.ListReader(fb.Int8Reader())'; - if (p.fieldIsNullable) { - preLines.add('final $valueVar = ${readFieldOrNull()};'); - return '$valueVar == null ? null : ${p.fieldType}.fromList($valueVar)'; - } else { - return '${p.fieldType}.fromList(${readFieldNonNull('[]')})'; - } + fbReader = 'fb.${p.fieldType}Reader(lazy: false)'; + return '${readField()} as ${p.fieldType}${p.fieldIsNullable ? "?" : ""}'; } else { fbReader = 'fb.ListReader(fb.Int8Reader(), lazy: false)'; } From 12fa0d93da2d95fc8a97f4a6c574af0ba0ddbafc Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sat, 18 Dec 2021 10:03:00 +0100 Subject: [PATCH 3/4] fix: perf regression after fb 2.0.5 update --- generator/lib/src/code_chunks.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 44619dc3f..20535d556 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -344,6 +344,11 @@ class CodeChunks { case OBXPropertyType.Relation: fbReader = 'fb.${_propertyFlatBuffersType[p.type]}Reader()'; return readFieldNonNull('0'); + case OBXPropertyType.String: + // TODO `asciiOptimization: true` is for keeping the same behavior as the previous FB fork. + // Check if it still makes sense with the latest Dart/Flutter. + fbReader = 'fb.StringReader(asciiOptimization: true)'; + break; case OBXPropertyType.StringVector: // TODO `asciiOptimization: true` is for keeping the same behavior as the previous FB fork. // Check if it still makes sense with the latest Dart/Flutter. From 2bc9c0de9a399e22b6155f301f2002bb2806b281 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 19 Dec 2021 09:59:49 +0100 Subject: [PATCH 4/4] fix: generate code for non-null-safe apps --- generator/lib/src/code_chunks.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 20535d556..8775a3f5d 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -336,7 +336,7 @@ class CodeChunks { case OBXPropertyType.ByteVector: if (['Int8List', 'Uint8List'].contains(p.fieldType)) { fbReader = 'fb.${p.fieldType}Reader(lazy: false)'; - return '${readField()} as ${p.fieldType}${p.fieldIsNullable ? "?" : ""}'; + return '${readField()} as ${p.fieldType}${p.fieldIsNullable && p.entity!.nullSafetyEnabled ? "?" : ""}'; } else { fbReader = 'fb.ListReader(fb.Int8Reader(), lazy: false)'; }