From f31b576209f9462a6cf4f3770a0ff55e0aef9dbd Mon Sep 17 00:00:00 2001
From: Maxwell Talbot <talbot.maxwell@gmail.com>
Date: Tue, 17 May 2022 09:54:05 +0100
Subject: [PATCH] update binary writer to deal with large file sizes better

---
 .../binary_buffer/binary_writer.dart          |   7 +-
 test/binary_buffer_test.dart                  | 255 ++++++++++++++++++
 2 files changed, 261 insertions(+), 1 deletion(-)
 create mode 100644 test/binary_buffer_test.dart

diff --git a/lib/src/utilities/binary_buffer/binary_writer.dart b/lib/src/utilities/binary_buffer/binary_writer.dart
index 7a2312e..53e4f66 100644
--- a/lib/src/utilities/binary_buffer/binary_writer.dart
+++ b/lib/src/utilities/binary_buffer/binary_writer.dart
@@ -28,10 +28,15 @@ class BinaryWriter {
     _byteData = ByteData.view(_buffer.buffer);
   }
 
+  /// Return the smallest multiple of [_alignment] that fits data of [length]
+  int nextAlignment(int length) {
+    return (length / alignment).ceil() * alignment;
+  }
+
   void _ensureAvailable(int byteLength) {
     if (_writeIndex + byteLength > _buffer.length) {
       do {
-        _buffer = Uint8List(_buffer.length + _alignment)
+        _buffer = Uint8List(_buffer.length + nextAlignment(byteLength))
           ..setRange(0, _buffer.length, _buffer);
       } while (_writeIndex + byteLength > _buffer.length);
       _byteData = ByteData.view(_buffer.buffer);
diff --git a/test/binary_buffer_test.dart b/test/binary_buffer_test.dart
new file mode 100644
index 0000000..3044aa2
--- /dev/null
+++ b/test/binary_buffer_test.dart
@@ -0,0 +1,255 @@
+import 'dart:typed_data';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rive/src/utilities/binary_buffer/binary_reader.dart';
+import 'package:rive/src/utilities/binary_buffer/binary_writer.dart';
+
+void main() {
+  test('float32', () {
+    var writer = BinaryWriter();
+    writer.writeFloat32(1.5);
+    writer.writeFloat32(3.1449999809265137);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readFloat32(), 1.5);
+    expect(reader.readFloat32(), 3.1449999809265137);
+  });
+
+  test('float64', () {
+    var writer = BinaryWriter();
+    writer.writeFloat32(2.222222328186035);
+    writer.writeFloat32(3.1456665992736816);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readFloat32(), 2.222222328186035);
+    expect(reader.readFloat32(), 3.1456665992736816);
+  });
+
+  test('uint8', () {
+    var writer = BinaryWriter();
+    writer.writeUint8(7);
+    writer.writeUint8(31);
+    writer.writeUint8(222);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readUint8(), 7);
+    expect(reader.readUint8(), 31);
+    expect(reader.readUint8(), 222);
+  });
+
+  test('int8', () {
+    var writer = BinaryWriter();
+    writer.writeInt8(7);
+    writer.writeInt8(31);
+    writer.writeInt8(-127);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readInt8(), 7);
+    expect(reader.readInt8(), 31);
+    expect(reader.readInt8(), -127);
+  });
+
+  test('uint8', () {
+    var writer = BinaryWriter();
+    writer.writeUint8(7);
+    writer.writeUint8(31);
+    writer.writeUint8(222);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readUint8(), 7);
+    expect(reader.readUint8(), 31);
+    expect(reader.readUint8(), 222);
+  });
+
+  test('int16', () {
+    var writer = BinaryWriter();
+    writer.writeInt16(7);
+    writer.writeInt16(31);
+    writer.writeInt16(1982);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readInt16(), 7);
+    expect(reader.readInt16(), 31);
+    expect(reader.readInt16(), 1982);
+
+    writer = BinaryWriter(endian: Endian.big);
+    writer.writeInt16(7);
+    writer.writeInt16(31);
+    writer.writeInt16(1982);
+
+    reader = BinaryReader(writer.buffer, endian: Endian.big);
+    expect(reader.readInt16(), 7);
+    expect(reader.readInt16(), 31);
+    expect(reader.readInt16(), 1982);
+  });
+
+  test('uint16', () {
+    var writer = BinaryWriter();
+    writer.writeUint16(7);
+    writer.writeUint16(31);
+    writer.writeUint16(1982);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readUint16(), 7);
+    expect(reader.readUint16(), 31);
+    expect(reader.readUint16(), 1982);
+
+    writer = BinaryWriter(endian: Endian.big);
+    writer.writeUint16(7);
+    writer.writeUint16(31);
+    writer.writeUint16(1982);
+
+    reader = BinaryReader(writer.buffer, endian: Endian.big);
+    expect(reader.readUint16(), 7);
+    expect(reader.readUint16(), 31);
+    expect(reader.readUint16(), 1982);
+  });
+
+  test('int32', () {
+    var writer = BinaryWriter();
+    writer.writeInt32(7);
+    writer.writeInt32(31);
+    writer.writeInt32(1982);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readInt32(), 7);
+    expect(reader.readInt32(), 31);
+    expect(reader.readInt32(), 1982);
+
+    writer = BinaryWriter(endian: Endian.big);
+    writer.writeInt32(7);
+    writer.writeInt32(31);
+    writer.writeInt32(1982);
+
+    reader = BinaryReader(writer.buffer, endian: Endian.big);
+    expect(reader.readInt32(), 7);
+    expect(reader.readInt32(), 31);
+    expect(reader.readInt32(), 1982);
+  });
+
+  test('uint32', () {
+    var writer = BinaryWriter();
+    writer.writeUint32(7);
+    writer.writeUint32(31);
+    writer.writeUint32(4294967295);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readUint32(), 7);
+    expect(reader.readUint32(), 31);
+    expect(reader.readUint32(), 4294967295);
+
+    writer = BinaryWriter(endian: Endian.big);
+    writer.writeUint32(7);
+    writer.writeUint32(31);
+    writer.writeUint32(4294967295);
+
+    reader = BinaryReader(writer.buffer, endian: Endian.big);
+    expect(reader.readUint32(), 7);
+    expect(reader.readUint32(), 31);
+    expect(reader.readUint32(), 4294967295);
+  });
+
+  test('int64', () {
+    var writer = BinaryWriter();
+    writer.writeInt64(7);
+    writer.writeInt64(31);
+    writer.writeInt64(-9223372036854775807);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readInt64(), 7);
+    expect(reader.readInt64(), 31);
+    expect(reader.readInt64(), -9223372036854775807);
+
+    writer = BinaryWriter(endian: Endian.big);
+    writer.writeInt64(7);
+    writer.writeInt64(31);
+    writer.writeInt64(-9223372036854775807);
+
+    reader = BinaryReader(writer.buffer, endian: Endian.big);
+    expect(reader.readInt64(), 7);
+    expect(reader.readInt64(), 31);
+    expect(reader.readInt64(), -9223372036854775807);
+  });
+
+  test('uint64', () {
+    var writer = BinaryWriter();
+    writer.writeUint64(7);
+    writer.writeUint64(31);
+    writer.writeUint64(9223372036854775807);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readUint64(), 7);
+    expect(reader.readUint64(), 31);
+    expect(reader.readUint64(), 9223372036854775807);
+
+    writer = BinaryWriter(endian: Endian.big);
+    writer.writeUint64(7);
+    writer.writeUint64(31);
+    writer.writeUint64(9223372036854775807);
+
+    reader = BinaryReader(writer.buffer, endian: Endian.big);
+    expect(reader.readUint64(), 7);
+    expect(reader.readUint64(), 31);
+    expect(reader.readUint64(), 9223372036854775807);
+  });
+
+  test('varuint', () {
+    var writer = BinaryWriter();
+    writer.writeVarUint(7);
+    writer.writeVarUint(127);
+    writer.writeVarUint(8192);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readVarUint(), 7);
+    expect(reader.readVarUint(), 127);
+    expect(reader.readVarUint(), 8192);
+  });
+
+  test('string', () {
+    var writer = BinaryWriter();
+    writer.writeString("Node");
+    writer.writeString("Artboard");
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readString(), "Node");
+    expect(reader.readString(), "Artboard");
+  });
+
+  test('mixed', () {
+    var writer = BinaryWriter();
+    writer.writeVarUint(10);
+    writer.writeString("Node");
+    writer.writeFloat32(22.100000381469727);
+    writer.writeFloat32(129.3000030517578);
+    writer.writeInt32(1920);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.readVarUint(), 10);
+    expect(reader.readString(), "Node");
+    expect(reader.readFloat32(), 22.100000381469727);
+    expect(reader.readFloat32(), 129.3000030517578);
+    expect(reader.readInt32(), 1920);
+  });
+
+  test('bytes', () {
+    var writer = BinaryWriter();
+    writer.write(Uint8List.fromList([7, 31, 1982]));
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.read(3), Uint8List.fromList([7, 31, 1982]));
+  });
+
+  test('a lot of bytes', () async {
+    var writer = BinaryWriter();
+    final bytesToWrite = Uint8List.fromList(
+      // ~ 5MB, takes like 22 seconds on my mac the old way vs 0.15 the new
+      List<int>.generate(1024 * 1024 * 5, (int index) => index % 128,
+          growable: true),
+    );
+    writer.write(bytesToWrite);
+
+    var reader = BinaryReader(writer.buffer);
+    expect(reader.read(bytesToWrite.length), bytesToWrite);
+    // required to trigger timeout error
+    await Future<void>.delayed(const Duration(milliseconds: 1));
+  }, timeout: const Timeout(Duration(seconds: 5)));
+}