bin 0.1.0 copy "bin: ^0.1.0" to clipboard
bin: ^0.1.0 copied to clipboard

Typed binary data structures for Dart (without code generation).

🗑️ Bin #

style: very good analysis Coverage Made in Minnesota License: MIT

Declare typed binary data structures in Dart (without code generation) using the excellent binarize package.

import 'package:bin/bin.dart';

/// A phone number category.
enum PhoneKind { home, mobile, work }

final class AddressBook with Binnable {
  final Bin<String> owner = Bin.utf8();
  final Bin<Contact?> emergencyContact = Bin.nullableObj(Contact.new);
  final Bin<List<Contact>> contacts = Bin.custom(list(obj(Contact.new)), []);
  final Bin<Map<String, List<List<int>>>> groups = Bin.custom(
    map(string.utf8(), list(list(uint32))),
    {},
  );

  @override
  Iterable<Binned> get bins => [owner, emergencyContact, contacts, groups];
}

final class Contact with Binnable { ... }

void main() {
  final addressBook = AddressBook()
    ..owner.value = 'Bob'
    ..emergencyContact.value = (Contact()
      ..id.value = 7
      ..name.value = 'Alice'
      ..phoneNumbers.value = {PhoneKind.mobile: '1-234-5678'})
    ..contacts.value = [
      Contact()
        ..id.value = 7
        ..name.value = 'Alice'
        ..phoneNumbers.value = {
          PhoneKind.home: '1-234-5678',
          PhoneKind.mobile: '1-234-5678',
          PhoneKind.work: '1-234-5678',
        }
        ..addressLines.value = [
          '1000 Home Street',
          'Shore Town, TS 12345',
        ],
      Contact()
        ..id.value = 12
        ..name.value = 'Eve'
        ..phoneNumbers.value = {PhoneKind.mobile: '1-234-5678'},
    ]
    ..groups.value = {
      'family': [
        [7],
        [12],
      ],
      'work': [
        [12],
      ],
    };

  final bytes = encode(addressBook);
  final decoded = decode(AddressBook.new, bytes);
}

The above was taken from the complete example.

📦 Installation #

dart pub add bin

📖 Usage #

Declaring Types #

Create a class with Binnable, define fields as Bin<T>, and return them in binary order.

final class Contact with Binnable {
  final Bin<int> id = Bin.uint32();
  final Bin<String> name = Bin.utf8();
  final Bin<Map<PhoneKind, String>> phoneNumbers = Bin.custom(
    map(enumeration(PhoneKind.values), string.utf8()),
    {},
  );

  @override
  Iterable<Binned> get bins => [id, name, phoneNumbers];
}

Bin comes with many constructors that allow you to create different types of primitive values:

final id = Bin.int32();
final weight = Bin.float64();
final name = Bin.utf8();

Bins can be composed to create composite types using lists:

final Bin<List<String>> names = Bin.custom(list(string.utf8()), <String>[]);

Maps can be used as well:

final Bin<Map<String, int>> lookup = Bin.custom(
  map(string.utf8(), uint32),
  <String, int>{},
);

You may nest lists and maps as much as you like.

Bin.custom enables you to use any Binarize PayloadType<T> as a field type, along with a default value.

You must implement a bins property that returns the fields in binary order.

@override
Iterable<Binned> get bins => [id, name, phoneNumbers];

Warning

The order of bins is the binary schema. Changing the order changes the bytes.

Binarize 101 #

Bin uses Binarize under the hood to perform serialization, and associates each class field with a Binarize PayloadType<T>.

final count = Bin.uint32();
final name = Bin.utf8();
final tags = Bin.custom(list(string.utf8()), <String>[]);
final kind = Bin.custom(enumeration(PhoneKind.values), PhoneKind.home);

Payload types nest inside each other. A payload describes one value, and collection payloads accept other payloads:

final ids = Bin.custom(list(uint32), <int>[]);
final rows = Bin.custom(list(list(uint32)), <List<int>>[]);
final lookup = Bin.custom(
  map(string.utf8(), list(uint32)),
  <String, List<int>>{},
);

Objects work the same way with obj:

final contact = Bin.obj(Contact.new);
final contacts = Bin.custom(list(obj(Contact.new)), <Contact>[]);
final pages = Bin.custom(list(list(obj(Contact.new))), <List<Contact>>[]);

Common payloads include:

  • integers: int8, int16, int32, int64, uint8, uint16, uint32, uint64
  • floats and booleans: float32, float64, boolean
  • strings: string.utf8(), string.ascii(), string.latin1()
  • collections: list(type), map(keyType, valueType), RawList(type, amount: n)
  • nullable values: nil(type)
  • enums: enumeration(MyEnum.values)
  • bytes and typed data: Bytes(n), uint8List(), byteData(), and other typed list payloads

Length-prefixed payloads use compact defaults, but most accept a lengthType when you need larger values:

final longNames = Bin.custom(
  list(string.utf8(), lengthType: uint32),
  <String>[],
);

Using Types #

Read and write field values through .value.

final contact = Contact()
  ..id.value = 7
  ..name.value = 'Alice'
  ..phoneNumbers.value = {PhoneKind.mobile: '1-234-5678'};

print(contact.name.value); // Alice

Nested objects are created from factories when decoded.

Encoding and Decoding #

Use encode and decode at the byte boundary.

final bytes = encode(contact);
final decoded = decode(Contact.new, bytes);

For streaming or embedded formats, use read(ByteReader) and write(ByteWriter) directly.

0
likes
160
points
36
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Typed binary data structures for Dart (without code generation).

Repository (GitHub)
View/report issues

Topics

#binary #serialization

License

MIT (license)

Dependencies

binarize

More

Packages that depend on bin