High-performance binary serialization for Dart. Optimized for speed, zero-copy reads, and Protocol Buffers-compatible encoding.
- Zero-Copy Reads: Operations return
Uint8Listviews without allocation. - One-Pass Strings: Optimized
writeVarStringwith optimistic shift (30% faster). - Smart Buffering: Exponential growth (×1.5) and object pooling.
- Compact Encoding: VarInt & ZigZag support
- Stream Parsing:
StreamBinaryReaderandBinaryStreamTransformerfor async data. - Universal: Supports Native & Web (WASM/JS) with consistent API.
- Modern API: Leverages Dart Extension Types for zero-overhead abstractions.
dependencies:
pro_binary: ^4.0.0import 'package:pro_binary/pro_binary.dart';
// Serialize
final writer = BinaryWriter()
..writeUint32(42)
..writeVarString('Dart 🚀')
..writeBool(true);
final bytes = writer.takeBytes();
// Deserialize
final reader = BinaryReader(bytes);
print(reader.readUint32()); // 42
print(reader.readVarString()); // Dart 🚀
print(reader.readBool()); // true
// From List<int>
final bytesList = <int>[0x01, 0x02, 0x03, 0x04];
final reader2 = BinaryReader.fromList(bytesList);class User {
final int id;
final String name;
User(this.id, this.name);
void encode(BinaryWriter w) => w
..writeVarUint(id)
..writeVarString(name);
factory User.decode(BinaryReader r) =>
User(
r.readVarUint(),
r.readVarString(),
);
}Avoid GC pressure by reusing writer instances.
Recommended (Safe & Concise):
final data = BinaryWriterPool.withWriter((writer) {
writer.writeUint32(1);
writer.writeVarString('Dart Rocks!');
return writer.toBytes(); // View of the buffer
});Low-level API:
final writer = BinaryWriterPool.acquire();
try {
writer.writeUint32(1);
writer.writeVarString('Dart Rocks!');
final data = writer.toBytes();
} finally {
BinaryWriterPool.release(writer);
}Process binary data arriving in chunks over a stream.
Custom Transformer:
class MessageParser extends BinaryStreamTransformer<Message> {
@override
Message? parse(StreamBinaryReader reader) {
// Return null when not enough data yet
if (!reader.hasBytes(4)) return null;
final id = reader.readUint32();
final name = reader.readVarString();
return Message(id, name);
}
}
// Usage:
stream.transform(MessageParser()).listen((msg) => print(msg));Manual Chunk Reading:
final reader = StreamBinaryReader();
reader.addChunk(chunk1);
reader.addChunk(chunk2);
reader.bookmark();
try {
final id = reader.readUint32();
final name = reader.readVarString();
reader.commit(); // Success — consumed
} on NotEnoughDataException {
reader.rollback(); // Wait for more data
}final reader = BinaryReader(bytes);
final type = reader[0]; // Absolute peek via operator []
reader.skip(1);
if (reader.hasBytes(4)) {
final payload = reader(4); // Concise call syntax for readBytes.
}Full API documentation: https://pub.dev/documentation/pro_binary/latest/pro_binary/
| Class | Description |
|---|---|
| BinaryWriter | Encode data: fixed types, VarInt/ZigZag, strings, bytes. Supports takeBytes(), toBytes(), reset(), seek(). |
| BinaryReader | Decode data: all fixed/variable types, navigation (skip, seek, rewind, peek), rebind() for reuse. |
| StreamBinaryReader | Async streaming: chunk-based reading with bookmark/rollback/commit transactional model. |
| BinaryStreamTransformer<T> | Stream parser: extend and implement parse() to process binary streams. |
| BinaryWriterPool | Object pool: acquire()/release() or withWriter() for high-frequency writes. |
| getUtf8Length | Utility: calculate UTF-8 byte length without encoding. |
Run benchmarks to see it in action:
dart run benchmark_harness:bench --flavor aot --target test/performance/serialization_bench.dartMIT License. See LICENSE for details.