From 50ea741b4cce3314f225351ec755771d00584d6e Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 19 Apr 2021 23:19:05 -0700 Subject: [PATCH] Speed up WriteBuffer by removing some runtime checks in release builds and removing a loop (#80588) --- .../lib/src/foundation/serialization.dart | 51 ++++++++++++------- .../test/foundation/serialization_test.dart | 5 ++ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/flutter/lib/src/foundation/serialization.dart b/packages/flutter/lib/src/foundation/serialization.dart index 187358291c..59722c82ad 100644 --- a/packages/flutter/lib/src/foundation/serialization.dart +++ b/packages/flutter/lib/src/foundation/serialization.dart @@ -9,92 +9,109 @@ import 'package:typed_data/typed_buffers.dart' show Uint8Buffer; /// Write-only buffer for incrementally building a [ByteData] instance. /// /// A WriteBuffer instance can be used only once. Attempts to reuse will result -/// in [NoSuchMethodError]s being thrown. +/// in [StateError]s being thrown. /// /// The byte order used is [Endian.host] throughout. class WriteBuffer { /// Creates an interface for incrementally building a [ByteData] instance. WriteBuffer() : _buffer = Uint8Buffer(), + _isDone = false, _eightBytes = ByteData(8) { _eightBytesAsList = _eightBytes.buffer.asUint8List(); } - Uint8Buffer? _buffer; + Uint8Buffer _buffer; + bool _isDone; final ByteData _eightBytes; late Uint8List _eightBytesAsList; + static final Uint8List _zeroBuffer = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0]); /// Write a Uint8 into the buffer. void putUint8(int byte) { - _buffer!.add(byte); + assert(!_isDone); + _buffer.add(byte); } /// Write a Uint16 into the buffer. void putUint16(int value, {Endian? endian}) { + assert(!_isDone); _eightBytes.setUint16(0, value, endian ?? Endian.host); - _buffer!.addAll(_eightBytesAsList, 0, 2); + _buffer.addAll(_eightBytesAsList, 0, 2); } /// Write a Uint32 into the buffer. void putUint32(int value, {Endian? endian}) { + assert(!_isDone); _eightBytes.setUint32(0, value, endian ?? Endian.host); - _buffer!.addAll(_eightBytesAsList, 0, 4); + _buffer.addAll(_eightBytesAsList, 0, 4); } /// Write an Int32 into the buffer. void putInt32(int value, {Endian? endian}) { + assert(!_isDone); _eightBytes.setInt32(0, value, endian ?? Endian.host); - _buffer!.addAll(_eightBytesAsList, 0, 4); + _buffer.addAll(_eightBytesAsList, 0, 4); } /// Write an Int64 into the buffer. void putInt64(int value, {Endian? endian}) { + assert(!_isDone); _eightBytes.setInt64(0, value, endian ?? Endian.host); - _buffer!.addAll(_eightBytesAsList, 0, 8); + _buffer.addAll(_eightBytesAsList, 0, 8); } /// Write an Float64 into the buffer. void putFloat64(double value, {Endian? endian}) { + assert(!_isDone); _alignTo(8); _eightBytes.setFloat64(0, value, endian ?? Endian.host); - _buffer!.addAll(_eightBytesAsList); + _buffer.addAll(_eightBytesAsList); } /// Write all the values from a [Uint8List] into the buffer. void putUint8List(Uint8List list) { - _buffer!.addAll(list); + assert(!_isDone); + _buffer.addAll(list); } /// Write all the values from an [Int32List] into the buffer. void putInt32List(Int32List list) { + assert(!_isDone); _alignTo(4); - _buffer!.addAll(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); + _buffer.addAll(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); } /// Write all the values from an [Int64List] into the buffer. void putInt64List(Int64List list) { + assert(!_isDone); _alignTo(8); - _buffer!.addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); + _buffer.addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); } /// Write all the values from a [Float64List] into the buffer. void putFloat64List(Float64List list) { + assert(!_isDone); _alignTo(8); - _buffer!.addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); + _buffer.addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); } void _alignTo(int alignment) { - final int mod = _buffer!.length % alignment; + assert(!_isDone); + final int mod = _buffer.length % alignment; if (mod != 0) { - for (int i = 0; i < alignment - mod; i++) - _buffer!.add(0); + _buffer.addAll(_zeroBuffer, 0, alignment - mod); } } /// Finalize and return the written [ByteData]. ByteData done() { - final ByteData result = _buffer!.buffer.asByteData(0, _buffer!.lengthInBytes); - _buffer = null; + if (_isDone) { + throw StateError('done() must not be called more than once on the same $runtimeType.'); + } + final ByteData result = _buffer.buffer.asByteData(0, _buffer.lengthInBytes); + _buffer = Uint8Buffer(); + _isDone = true; return result; } } diff --git a/packages/flutter/test/foundation/serialization_test.dart b/packages/flutter/test/foundation/serialization_test.dart index c51cd5db78..61dd3d81da 100644 --- a/packages/flutter/test/foundation/serialization_test.dart +++ b/packages/flutter/test/foundation/serialization_test.dart @@ -100,5 +100,10 @@ void main() { expect(readDoubles[0], equals(3.14)); expect(readDoubles[1], isNaN); }); + test('done twice', () { + final WriteBuffer write = WriteBuffer(); + write.done(); + expect(() => write.done(), throwsStateError); + }); }); }