BinState
A lightweight Dart library for efficient binary serialization and deserialization of objects, with optional GZIP compression.
Features
- Compact binary serialization
- GZIP compression support
- Handles
int,double,bool,String,List<double>,List<String?>, and custom objects - Nullable fields
- Memory-efficient with
BytesBuilderpooling - Robust error handling and protocol versioning
Performance
For a RobotState with a List<double> of 10,000 constant values:
- Binary size: ~217 bytes (uncompressed)
- JSON size: ~50,146 bytes
- Binary is 99.57% smaller than JSON
- Save time: ~15,550 µs
- Load time: ~10,541 µs
Installation
Add to pubspec.yaml:
dependencies:
binstate: ^1.0.0
Run:
dart pub get
Usage
import 'package:binstate/binstate.dart';
class Point implements BinarySerializable {
final int x, y;
Point(this.x, this.y);
@override
void serialize(BinaryCodec codec) {
for (var desc in getPropertyDescriptors()) {
desc.writer(this, codec);
}
}
@override
List<PropertyDescriptor> getPropertyDescriptors() => [
PropertyDescriptor(
name: 'x',
type: int,
reader: (codec) => codec.readInt(),
writer: (obj, codec) => codec.writeInt((obj as Point).x),
),
PropertyDescriptor(
name: 'y',
type: int,
reader: (codec) => codec.readInt(),
writer: (obj, codec) => codec.writeInt((obj as Point).y),
),
];
static Point deserialize(BinaryCodec codec) {
final descriptors = Point(0, 0).getPropertyDescriptors();
final values = <String, dynamic>{};
for (var desc in descriptors) {
values[desc.name] = desc.reader(codec);
}
return Point(values['x'], values['y']);
}
}
class RobotState extends BinaryState {
final int id;
final double positionX, positionY;
final bool isActive;
final String? name;
final List<double> coordinates;
final List<String?> commandLog;
final Point point;
RobotState({
required this.id,
required this.positionX,
required this.positionY,
required this.isActive,
this.name,
required this.coordinates,
required this.commandLog,
required this.point,
});
@override
void serialize(BinaryCodec codec) {
for (var desc in getPropertyDescriptors()) {
desc.writer(this, codec);
}
}
@override
List<PropertyDescriptors> getPropertyDescriptors() => [
PropertyDescriptor(
name: 'id',
type: int,
reader: (codec) => codec.readInt(),
writer: (obj, codec) => codec.writeInt((obj as RobotState).id),
),
// Other descriptors...
];
static RobotState deserialize(BinaryCodec codec) {
final descriptors = RobotState(
id: 0,
positionX: 0,
positionY: 0,
isActive: false,
coordinates: [],
commandLog: [],
point: Point(0, 0),
).getPropertyDescriptors();
final values = <String, dynamic>{};
for (var desc in descriptors) {
values[desc.name] = desc.reader(codec);
}
return RobotState(
id: values['id'],
positionX: values['positionX'],
positionY: values['positionY'],
isActive: values['isActive'],
name: values['name'],
coordinates: values['coordinates'],
commandLog: values['commandLog'],
point: values['point'],
);
}
}
void main() async {
final robot = RobotState(
id: 1,
positionX: 10.5,
positionY: 20.3,
isActive: true,
name: null,
coordinates: List.generate(10000, (_) => 1.0),
commandLog: ["move_forward", null, "stop"],
point: Point(5, 10),
);
await robot.saveToFile('robot_state.bin', compress: true);
final loadedRobot = await BinaryState.loadFromFile('robot_state.bin', RobotState.deserialize, compress: true);
print('ID: ${loadedRobot.id}, Coordinates: ${loadedRobot.coordinates.length} items');
}
Notes
- Use
compress: falsefor faster processing with small or random data. - Best compression with repetitive data (e.g., constant lists).