kyubuns / hxbitmini

Haxe Binary serialization library, serialized data size is smaller than HxBit

Home Page:https://lib.haxe.org/p/hxbitmini/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

HxBitMini

HxBitMini is a binary serialization library for Haxe.
Forked from HxBit.
Serialized data size is smaller than HxBit.

Comparison with HxBit

  • UID has been removed.
    • The data generated by serialization will be smaller. (Mini!)
    • Objects with the same value will have the same serialization result.
  • Add null safety support
    • add -D hxbitmini_nullsafety to build flag
  • Network functions have been removed.
  • vdom has been removed.

Before (HxBit)

class Sample {
    static function main() {
        var target = new ElementList([new Element(1, 2), new Element(3, 4)]);
        var element = (new hxbit.Serializer()).serialize(target);
        trace(element.toHex());
    }
}

class ElementList implements hxbit.Serializable {
    public function new(a: Array<Element>) {
        this.a = a;
    }

    @:s public var a: Array<Element>;
}

class Element implements hxbit.Serializable {
    public function new(a: Int, b: Int) {
        this.a = a;
        this.b = b;
    }

    @:s public var a: Int;
    @:s public var b: Int;
}

0303010102020304 -> 8bytes (UID, ArrayLength, UID, a, b, UID, a, b)

After (HxBitMini)

class Sample {
    static function main() {
        var target = new ElementList([new Element(1, 2), new Element(3, 4)]);
        var element = (new hxbitmini.Serializer()).serialize(target);
        trace(element.toHex());
    }
}

class ElementList implements hxbitmini.Serializable {
    public function new(a: Array<Element>) {
        this.a = a;
    }

    @:s public var a: Array<Element>;
}

class Element implements hxbitmini.Serializable {
    public function new(a: Int, b: Int) {
        this.a = a;
        this.b = b;
    }

    @:s public var a: Int;
    @:s public var b: Int;
}

0301020304 -> 5bytes (ArrayLength, a, b, a, b)
The difference is more pronounced when many classes are included.

HxBit to HxBitMini

Just replace hxbit to hxbitmini.

Compatible with hxbit

HxBitMini 1.0.0 is forked from code newer than HxBit 1.5.0. (The latest version that is now available to the public)
Please check Network graph.


Installation

Install through haxelib with haxelib install hxbitmini and use -lib hxbitmini to use it.

Serialization

You can serialize objects by implementing the hxbitmini.Serializable interface. You need to specify which fields you want to serialize by using the @:s metadata:

class User implements hxbitmini.Serializable {
    @:s public var name : String;
    @:s public var age : Int;
    @:s public var friends : Array<User>;
    ...
}

This will automatically add a few methods and variables to your User class:

/**
  These fields are automatically generated when implementing the interface.
**/
interface Serializable {
	/** Returns the unique class id for this object **/
	public function getCLID() : Int;
	/** Serialize the object id and fields using this Serializer **/
	public function serialize( ctx : hxbitmini.Serializer ) : Void;
	/** Unserialize object fields using this Serializer **/  
	public function unserialize( ctx : hxbitmini.Serializer ) : Void;
	/** Returns the object data schema **/  
	public function getSerializeSchema() : hxbitmini.Schema;
}

This allows you to serialize/unserialize using this code:

var s = new hxbitmini.Serializer();
var bytes = s.serialize(user);
....
var u = new hxbitmini.Serializer();
var user = u.unserialize(bytes, User);
....

Comparison with haxe.Serializer/Unserializer

Haxe standard library serialization works by doing runtime type checking, which is slower. However it can serialize any value even if it's not been marked as Serializable.

HxBit serialization uses macro to generate strictly typed serialization code that allows very fast I/O. OTOH this increase code size and is using a less readable binary format instead of Haxe standard serialization which uses a text representation.

Supported types

The following types are supported:

  • Int / haxe.UInt : stored as either 1 byte (0-254) or 5 bytes (0xFF + 32 bits)
  • Float : stored as single precision 32 bits IEEE float
  • Bool : stored as single by (0 or 1)
  • String : stored as size+1 prefix, then utf8 bytes (0 prefix = null)
  • any Enum value : stored as index byte + args values
  • haxe.io.Bytes : stored as size+1 prefix + raw bytes (0 prefix = null)
  • Array<T> and haxe.ds.Vector<T> : stored as size+1 prefix, then T list (0 prefix = null)
  • Map<K,V> : stored as size+1 prefix, then (K,V) pairs (0 prefix = null)
  • Null<T> : stored as a byte 0 for null, 1 followed by T either
  • Serializable (any other serializable instance) : store @:s values
  • Strutures { field : T... } : optimized to store a bit field of not null values, then only defined fields values

Default values

When unserializing, the class constructor is not called. If you want to have some non-serialized field already initialized before starting unserialization, you can set the default value using Haxe initializers:

class User implements hxbitmini.Serializable {
    ...
    // when unserializing, someOtherField will be set to [] instead of null
    var someOtherField : Array<Int> = []; 
}

Unsupported types

If you want to serialize unsupported types, you could implement your own serialization through the optional methods customSerialize and customUnserialize.

class Float32ArrayContainer implements hxbitmini.Serializable {

    public var value:Float32Array;

    ...

    @:keep
    public function customSerialize(ctx : hxbitmini.Serializer) {
        ctx.addInt(value.length);
        for(i in 0...value.length)
            ctx.addFloat(value[i]);
    }

    @:keep
    public function customUnserialize(ctx : hxbitmini.Serializer) {
        var length = ctx.getInt();
        var tempArray = new Array<Float>();
        for(i in 0...length)
            tempArray.push(ctx.getFloat());

        value = new Float32Array(tempArray);
    }
}

Versioning

HxBit serialization is capable of performing versioning, by storing in serialized data the schema of each serialized class, then comparing it to the current schema when unserializing, making sure that data is not corrupted.

In order to save some data with versioning, use the following:

var s = new hxbitmini.Serializer();
s.beginSave();
// ... serialize your data...
var bytes = s.endSave();

And in order to load versionned data, use:

var s = new hxbitmini.Serializer();
s.beginLoad(bytes);
// .. unserializer your data

Versioned data is slightly larger than unversioned one since it contains the Schema data of each serialized class.

Currently versioning handles:

  • removing fields (previous data is ignored)
  • adding fields (they are set to default value: 0 for Int/Float, false for Bool, empty Array/Map/etc.)

More convertions can be easily added in Serializer.convertValue, including custom ones if you extends Serializer.

About

Haxe Binary serialization library, serialized data size is smaller than HxBit

https://lib.haxe.org/p/hxbitmini/


Languages

Language:Haxe 100.0%