forked from BigfootDev/flatbuffers
Dart - add custom allocator support (#6711)
* Dart - add custom allocator support * Dart - only copy written bytes during resize, not the whole old buffer.
This commit is contained in:
@@ -111,6 +111,8 @@ class Builder {
|
|||||||
|
|
||||||
ByteData _buf;
|
ByteData _buf;
|
||||||
|
|
||||||
|
final Allocator _allocator;
|
||||||
|
|
||||||
/// The maximum alignment that has been seen so far. If [_buf] has to be
|
/// 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
|
/// 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.
|
/// 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
|
/// automatically grow the array if/as needed. `internStrings`, if set to
|
||||||
/// true, will cause [writeString] to pool strings in the buffer so that
|
/// true, will cause [writeString] to pool strings in the buffer so that
|
||||||
/// identical strings will always use the same offset in tables.
|
/// identical strings will always use the same offset in tables.
|
||||||
Builder({this.initialSize: 1024, bool internStrings = false})
|
Builder({
|
||||||
: _buf = ByteData(initialSize) {
|
this.initialSize: 1024,
|
||||||
if (internStrings == true) {
|
bool internStrings = false,
|
||||||
|
Allocator allocator = const DefaultAllocator(),
|
||||||
|
}) : _allocator = allocator,
|
||||||
|
_buf = allocator.allocate(initialSize) {
|
||||||
|
if (internStrings) {
|
||||||
_strings = new Map<String, int>();
|
_strings = new Map<String, int>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,12 +336,14 @@ class Builder {
|
|||||||
return tableTail;
|
return tableTail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method low level method can be used to return a raw piece of the buffer
|
/// This method low level method can be used to return a raw piece of the
|
||||||
/// after using the the put* methods.
|
/// buffer after using the put* methods.
|
||||||
///
|
///
|
||||||
/// Most clients should prefer calling [finish].
|
/// Most clients should prefer calling [finish].
|
||||||
Uint8List lowFinish() {
|
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
|
/// Finish off the creation of the buffer. The given [offset] is used as the
|
||||||
@@ -353,7 +361,8 @@ class Builder {
|
|||||||
fileIdentifier.codeUnitAt(i));
|
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.
|
/// Writes a Float64 to the tail of the buffer after preparing space for it.
|
||||||
@@ -716,11 +725,7 @@ class Builder {
|
|||||||
int deltaCapacity = desiredNewCapacity - oldCapacity;
|
int deltaCapacity = desiredNewCapacity - oldCapacity;
|
||||||
deltaCapacity += (-deltaCapacity) % _maxAlign;
|
deltaCapacity += (-deltaCapacity) % _maxAlign;
|
||||||
int newCapacity = oldCapacity + deltaCapacity;
|
int newCapacity = oldCapacity + deltaCapacity;
|
||||||
ByteData newBuf = new ByteData(newCapacity);
|
_buf = _allocator.resize(_buf, newCapacity, _tail, 0);
|
||||||
newBuf.buffer
|
|
||||||
.asUint8List()
|
|
||||||
.setAll(deltaCapacity, _buf.buffer.asUint8List());
|
|
||||||
_buf = newBuf;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update the tail pointer.
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
@reflectiveTest
|
||||||
class BuilderTest {
|
class BuilderTest {
|
||||||
void test_monsterBuilder([Builder? builder]) {
|
void test_monsterBuilder([Builder? builder]) {
|
||||||
@@ -247,7 +263,7 @@ class BuilderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_low() {
|
void test_low() {
|
||||||
Builder builder = new Builder(initialSize: 0);
|
final builder = Builder(initialSize: 0, allocator: CustomAllocator());
|
||||||
expect((builder..putUint8(1)).lowFinish(), [1]);
|
expect((builder..putUint8(1)).lowFinish(), [1]);
|
||||||
expect((builder..putUint32(2)).lowFinish(), [2, 0, 0, 0, 0, 0, 0, 1]);
|
expect((builder..putUint32(2)).lowFinish(), [2, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
expect((builder..putUint8(3)).lowFinish(),
|
expect((builder..putUint8(3)).lowFinish(),
|
||||||
@@ -263,7 +279,7 @@ class BuilderTest {
|
|||||||
void test_table_default() {
|
void test_table_default() {
|
||||||
List<int> byteList;
|
List<int> byteList;
|
||||||
{
|
{
|
||||||
Builder builder = new Builder(initialSize: 0);
|
final builder = Builder(initialSize: 0, allocator: CustomAllocator());
|
||||||
builder.startTable();
|
builder.startTable();
|
||||||
builder.addInt32(0, 10, 10);
|
builder.addInt32(0, 10, 10);
|
||||||
builder.addInt32(1, 20, 10);
|
builder.addInt32(1, 20, 10);
|
||||||
|
|||||||
Reference in New Issue
Block a user