binarize 1.5.0 copy "binarize: ^1.5.0" to clipboard
binarize: ^1.5.0 copied to clipboard

Binarize allows for a more streamlined and extendable binary creation experience.

Binarize #

plugin version coverage report pipeline status dependencies style: very good analysis

From Wiktionary:

Etymology #

binary + size

Verb #

binarize (third-person singular simple present binarizes, present participle binarizing, simple past and past participle binarized)

  1. (mathematics) To represent in binary (base 2) notation
  2. To convert (an image) to only black and white.
  3. (statistics) To dichotomize a variable.

Binarize is a package that wraps the ByteData functionality into a more streamlined and extendable payload system.

With Binarize you can easily create and read binary data into the correct value types without having to check for the correct byte offset or figure out which ByteData method you need to use.

This is especially useful for when you are implementing a file format specification, like the PSD File Format.

Installation #

Add binarize as a dependency to your pubspec.yaml file (what?).

Import Binarize:

import 'package:binarize/binarize.dart';

Docs & API #

The API Docs provide information about how to use Binarize.

If you want to see Binarize in practice, check the example. It showcases all the types that Binarize provides.

Usage #

Writing #

The Payload.write method returns a PayloadWriter that is used for writing values using different PayloadTypes:

import 'package:binarize/binarize.dart';

void main() {
  final writer = Payload.write()
    ..set(uint8, 16);
    ..set(string32, 'Hello World');
}

The binarize method accepts a PayloadWriter and converts the values to binary:

void main() {
  final writer = Payload.write();

  ...

  // Binarize the writer to a Uint8List.
  final bytes = binarize(writer);

  ...
}

Reading #

The Payload.read method returns a PayloadReader that reads a list of bytes. The data from that list can be retrieved by using different PayloadTypes:

import 'package:binarize/binarize.dart';

void main() {
  ...

  final reader = Payload.read(bytes);

  final aUint8 = reader.get(uint8);
  final aString = reader.get(string32);

  ...
}

Note: The order in which the values are written is also the order in which they have to be retrieved.

PayloadTypes #

PayloadTypes are the backbone of Binarize, they do all the heavy lifting of packing and unpacking bytes. Binarize provides all the basis types that the ByteData class has to offer. But in some cases a developer might want to create their own PayloadType. Thankfully Binarize's API allows for defining custom PayloadTypes.

Here is an example for a PayloadType that packs and unpacks the Vector2 class from the vector_math library:

import 'package:binarize/binarize.dart';
import 'package:vector_math/vector_math.dart';

// Define the PayloadType class, it is private as we only want to have a 
// single constant instance for it.
class _Vector2 extends PayloadType<Vector2> {
  const _Vector2();

  // The byte length, it should return the total byte length it will use for a 
  // given value. In this case we have two float32 values, each uses 4 bytes 
  // so our total length is 8.
  @override
  int length(Vector2 value) => 8; 

  // Called when the user reads data, the offset is the current read offset 
  // in the given data.
  @override
  Vector2 get(ByteData data, int offset) {
    return Vector2(
      // Read the x value at the current offset.
      data.getFloat32(offset),
      // Read the y value at the current offset + 4. Float32 has a default 
      // byte length of 4 so we have to take that into consideration so 
      // that we don't read the x value again.
      data.getFloat32(offset + 4),
    );
  }

  // Called when the user write data, the offset is the current write offset 
  // in the given byte data.
  @override
  void set(Vector2 value, ByteData data, int offset) {
    // Write the x value at the current offset.
    data.setFloat32(offset, value.x);
    // Write the y value at the current offset + 4. The 4 is needed as Float32
    // have a byte length of 4 so we have to take that into consideration so
    // we don't overwrite the x value.
    data.setFloat32(offset + 4, value.y);
  }
}

// And finally the user-facing variable that users can use to read and write 
// with.
const vector2 = _Vector2();

The vector2 can be used like any other PayloadType:

import 'package:binarize/binarize.dart';
import 'package:vector_math/vector_math.dart';

// Import the newly created vector2 PayloadType.
import './types/vector2.dart';

void main() {
  // Write a Vector2.
  final writer = Payload.write()..set(vector2, Vector2(100.5, 200));

  // Binarize the writer to a Uint8List.
  final bytes = binarize(writer);

  // Reading the bytes.
  final reader = Payload.read(bytes);

  // The x value will be 100.5 and the y value will be 200.
  final aVector2 = reader.get(vector2);
}

BinaryContract #

The BinaryContract allows developers to define a strongly typed binary representation of a Dart class. If your Dart class contract changes, so will the binary contract, ensuring you never forget to change how the data is being read or stored.

BinaryContract works best with classes that are immutable, because it will implement your class by overwriting the fields as getters. If your fields are not final Dart will complain that the setter was not implement. There are ways to work around this but it is not recommended.

An example of how you can implement a BinaryContract for your own classes:

class MyClass {
  const MyClass({
    required this.myProperty,
  });

  final int myProperty;
}

/// Defining the contract for the [MyClass] class. It should also implement the
/// class so the contract can access the fields.
class _MyClassContract extends BinaryContract<MyClass> implements MyClass {
  /// Pass a base line instance to the super constructor. This is used to build
  /// up the, contact, the values defined in the base line do not matter.
  const _MyClassContract()
      : super(const MyClass(myProperty: 1337));

  /// This method allows the [BinaryContract] to know the order of how the binary
  /// data should be read and stored.
  ///
  /// [contract] is always the [BinaryContract] instance.
  @override
  MyClass order(MyClass contract) {
    return MyClass(
      myProperty: contract.myProperty,
    );
  }

  /// The properties of the [MyClass] need to be implemented as getters.
  @override
  int get myProperty => type(uint16, (o) => o.myProperty);
}

// Create a constant instance of the contract.
const myClassContract = _MyClassContract();

void main() {
  // Create a new instance of MyClass.
  final myClass = MyClass(myProperty: 1337);

  // Store the instance using the contract.
  final writer = Payload.write()..set(myClassContract, myClass);
  final bytes = binarize(writer);
  
  // Read it back from binary.
  final reader = Payload.read(bytes);
  final myClassAgain = reader.get(myClassContract);

  print(myClassAgain.myProperty); // Returns 1337
}

The BinaryContract extends from PayloadType and can be used within other payloads, including itself.

47
likes
160
points
514
downloads

Publisher

verified publisherwolfenra.in

Weekly Downloads

Binarize allows for a more streamlined and extendable binary creation experience.

Repository (GitLab)
View/report issues
Contributing

Documentation

API reference

License

MIT (license)

More

Packages that depend on binarize