summaryrefslogtreecommitdiff
path: root/dart
diff options
context:
space:
mode:
authorIvan Dlugos <6349682+vaind@users.noreply.github.com>2021-07-08 22:02:09 +0200
committerGitHub <noreply@github.com>2021-07-08 13:02:09 -0700
commit8ab35b2a5fef8a93e666182d3f641146f5e68d24 (patch)
tree150f60954fc39258806d15a66ec26c3e4d66a7aa /dart
parentc0ba2870c909fb57f97efd7083b5d33d2311d7f9 (diff)
downloadflatbuffers-8ab35b2a5fef8a93e666182d3f641146f5e68d24.tar.gz
flatbuffers-8ab35b2a5fef8a93e666182d3f641146f5e68d24.tar.bz2
flatbuffers-8ab35b2a5fef8a93e666182d3f641146f5e68d24.zip
Dart - add custom allocator support (#6711)
* Dart - add custom allocator support * Dart - only copy written bytes during resize, not the whole old buffer.
Diffstat (limited to 'dart')
-rw-r--r--dart/lib/flat_buffers.dart82
-rw-r--r--dart/test/flat_buffers_test.dart20
2 files changed, 88 insertions, 14 deletions
diff --git a/dart/lib/flat_buffers.dart b/dart/lib/flat_buffers.dart
index 74c25943..726f3f0b 100644
--- a/dart/lib/flat_buffers.dart
+++ b/dart/lib/flat_buffers.dart
@@ -111,6 +111,8 @@ class Builder {
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.
@@ -138,9 +140,13 @@ class Builder {
/// 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})
- : _buf = ByteData(initialSize) {
- if (internStrings == true) {
+ Builder({
+ this.initialSize: 1024,
+ bool internStrings = false,
+ Allocator allocator = const DefaultAllocator(),
+ }) : _allocator = allocator,
+ _buf = allocator.allocate(initialSize) {
+ if (internStrings) {
_strings = new Map<String, int>();
}
}
@@ -330,12 +336,14 @@ class Builder {
return tableTail;
}
- /// This method low level method can be used to return a raw piece of the buffer
- /// after using the the put* methods.
+ /// This method low level method can be used to return a raw piece of the
+ /// buffer after using the put* methods.
///
/// Most clients should prefer calling [finish].
Uint8List lowFinish() {
- return _buf.buffer.asUint8List(_buf.lengthInBytes - size());
+ 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
@@ -353,7 +361,8 @@ class Builder {
fileIdentifier.codeUnitAt(i));
}
}
- return _buf.buffer.asUint8List(_buf.lengthInBytes - finishedSize);
+ return _buf.buffer
+ .asUint8List(_buf.lengthInBytes - finishedSize, finishedSize);
}
/// Writes a Float64 to the tail of the buffer after preparing space for it.
@@ -716,11 +725,7 @@ class Builder {
int deltaCapacity = desiredNewCapacity - oldCapacity;
deltaCapacity += (-deltaCapacity) % _maxAlign;
int newCapacity = oldCapacity + deltaCapacity;
- ByteData newBuf = new ByteData(newCapacity);
- newBuf.buffer
- .asUint8List()
- .setAll(deltaCapacity, _buf.buffer.asUint8List());
- _buf = newBuf;
+ _buf = _allocator.resize(_buf, newCapacity, _tail, 0);
}
}
// Update the tail pointer.
@@ -1243,3 +1248,56 @@ class _VTable {
}
}
}
+
+/// 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/dart/test/flat_buffers_test.dart b/dart/test/flat_buffers_test.dart
index c2121a0f..b536ecd8 100644
--- a/dart/test/flat_buffers_test.dart
+++ b/dart/test/flat_buffers_test.dart
@@ -133,6 +133,22 @@ class CheckOtherLangaugesData {
}
}
+/// Test a custom, fixed-memory allocator (no actual allocations performed)
+class CustomAllocator extends Allocator {
+ final _memory = ByteData(10 * 1024);
+
+ @override
+ ByteData allocate(int size) {
+ if (size > _memory.lengthInBytes) {
+ throw UnsupportedError('Trying to allocate too much');
+ }
+ return ByteData.sublistView(_memory, 0, size);
+ }
+
+ @override
+ void deallocate(ByteData _) {}
+}
+
@reflectiveTest
class BuilderTest {
void test_monsterBuilder([Builder? builder]) {
@@ -247,7 +263,7 @@ class BuilderTest {
}
void test_low() {
- Builder builder = new Builder(initialSize: 0);
+ final builder = Builder(initialSize: 0, allocator: CustomAllocator());
expect((builder..putUint8(1)).lowFinish(), [1]);
expect((builder..putUint32(2)).lowFinish(), [2, 0, 0, 0, 0, 0, 0, 1]);
expect((builder..putUint8(3)).lowFinish(),
@@ -263,7 +279,7 @@ class BuilderTest {
void test_table_default() {
List<int> byteList;
{
- Builder builder = new Builder(initialSize: 0);
+ final builder = Builder(initialSize: 0, allocator: CustomAllocator());
builder.startTable();
builder.addInt32(0, 10, 10);
builder.addInt32(1, 20, 10);