bison-types

Convert between json and binary

bison-types

  • Are you stuck with integrating with a system that only speaks binary?
  • Are you sick of manually decoding and encoding from json to binary?
  • Do you want an easy way to define your types in javascript?

If you answered yes to any of the above questions, then bison-types is for you

bison-types allows you to define custom types. With these custom types you can build up a message definition Pass these types and the buffer to bison-types and it will do the rest.

For example:

bison = require 'bison-types'
 
types = bison.preCompile
 
  # top-level type 
  'timeline': [
    {count: 'uint16'}
    {messages: 'message[count]'}
  ]
 
  # sub type 
  'message': [
    {id: 'uint8'}
    {timestamp: 'uint16'}
    {length: 'uint16'}
    {text: 'utf-8(length)'}
  ]
 
# time to read! 
 
buf = new Buffer [0x040x920x040x3b0xf40x2c...]
reader = new bison.Reader buftypes{bigEndian: false}
json = reader.read 'timeline'
 
# or write ! 
 
buf = new Buffer(1024)
writer = new bison.Writer buftypes{bigEndian: false}
writer.write 'timeline'{
  count: 1
  messages: [
    {
      id: 3
      date: new Date().getTime()
      length: 11
      text: 'hello world'
    }
  ]
}
 

Note: bison-types uses clever-buffer under the hood for all buffer manipulation.

The following types can be declared as a string, for example: {timestamp: 'uint16'}.

  • uint8 - unsigned 8 bit integer
  • uint16 - unsigned 16 bit integer
  • uint32 - unsigned 32 bit integer
  • uint64 - unsigned 64 bit integer
  • int8 - signed 8 bit integer
  • int16 - signed 16 bit integer
  • int32 - signed 32 bit integer
  • int64 - signed 64 bit integer
  • utf-8 - utf-8 encoded string
  • bool - boolean (stored as 8 bit integer)
  • skip - will skip specified bytes

The is also an enumeration type, which can be used to represent enums from arrays of objects:

# will store the index in the array 
levelIndex = bison.enumeration 'uint8'['admin''reader''writer']
 
# will store the value in the object 
levelCode = bison.enumeration 'uint16',
  'admin': 0xb8a3
  'reader': 0xb90a
  'writer': 0xf23c
 
bison.preCompile
  'levelIndex': levelIndex
  'levelCode': levelCode
  'user': [
    {id: 'uint8'}
    {levelX: levelIndex}
    {levelY: levelCode}
  ]

There are 2 different ways that you can define a custom type

  types = bison.preCompile
    my-other-type: [
      {a: 'uint8'}
      {b: 'uint16'}
    ]
    my-type: [
      {c: 'my-other-type'}
      {d: 'uint8'}
    ]

would create an object like

  myType = {c: {a: 12b: 123}d: 1234}

We expose the underlying clever-buffer as @buffer.

You can call any of its methods

  types = bison.preCompile
    multiply:
      _read:  (multiplier) ->
        @buffer.getUint8() * multiplier
      _write: (val, multiplier) ->
        @buffer.writeUInt8(val * multiplier)

would multiply the value read from the buffer before returning it when reading and multiply the value to be written when writing

You need to pass in a buffer to read from, and any custom types that you may have.

You can also pass in options, look at clever-buffer for a full list of options

    bison = require 'bison-types'
 
    buf = new Buffer [ 0x010x020x030x04 ]
    types = bison.preCompile
      my-type: [
        {a: 'uint8'}
        {b: 'uint8'}
        {c: 'uint8'}
        {d: 'uint8'}
      ]
    options = {bigEndian: false}
    reader = new bison.Reader buftypesoptions
    myType = reader.read('my-type') # myType = { a: 1,  b: 2, c: 3, d: 4 } 
    bison = require 'bison-types'
    
    buf = new Buffer [0x480x450x4C0x4C0x4F]
    types = bison.preCompile
      my-type: [
        a: 'utf-8(5)'
      ]
    options = {bigEndian: false}
    reader = new bison.Reader buftypesoptions
    myType = reader.read('my-type') # myType = { a: 'HELLO' } 

The power of bison-types is evident as you define more complex types

    bison = require 'bison-types'
 
    buf = new Buffer [ 0x010x030x04 ]
    types = bison.preCompile
      my-type: [
        {a: 'uint8'}
        {b: 'my-other-type'}
      ]
      my-other-type: [
        {c: 'uint8'}
        {d: 'uint8'}
      ]
    options = {bigEndian: false}
    reader = new bison.Reader buftypesoptions
    myType = reader.read('my-type') # myType = { a: 1,  b: { c: 3, d: 4 }} 

You can use previously resolved values as parameters to types The power of bison-types is evident as you define more complex types

    bison = require 'bison-types'
 
    buf = new Buffer [ 0x040x02 ]
    types = bison.preCompile
      mult:
        _read: (val) -> @buffer.getUInt8() * val
      my-type: [
        {a: 'uint8'}
        {b: 'mult(a)'}
      ]
    options = {bigEndian: false}
    reader = new bison.Reader buftypesoptions
    myType = reader.read('my-type') # myType = { a: 4,  b: 8} 

You can specify arrays in a similar matter

    bison = require 'bison-types'
    buf = new Buffer [ 0x030x010x020x03 ]
    types = bison.preCompile
      object: [
        c: 'uint8'
      ]
      my-type: [
        {a: 'uint8'}
        {b: 'object[a]'}
      ]
    options = {bigEndian: false}
    reader = new bison.Reader buftypesoptions
    myType = reader.read('my-type') 
    # myType = { a: 3, b:[{c:1},{c:2},{c:3}] } 

You need to pass in a buffer to write to, and any custom types that you may have.

You can also pass in options, look at clever-buffer for a full list of options

    bison = require 'bison-types'
 
    buf = new Buffer 4
    types = bison.preCompile
      my-type: [
        {a: 'uint8'}
        {b: 'uint8'}
        {c: 'uint8'}
        {d: 'uint8'}
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ a: 1,  b: 2c: 3d: 4 }
    # buf will equal [ 0x01, 0x02, 0x03, 0x04 ] 
    bison = require 'bison-types'
    
    buf = new Buffer 5
    types = bison.preCompile
      my-type: [
        a: 'utf-8'
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ a: 'HELLO' }
    # buf will equal [0x48, 0x45, 0x4C, 0x4C, 0x4F] 
    bison = require 'bison-types'
    
    buf = new Buffer 10
    types = bison.preCompile
      my-type: [
        a: 'utf-8(5)'
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ a: 'HELLOWORLD' }
    # buf will equal [0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00] 

The power of bison-types is evident as you define more complex types

    bison = require 'bison-types'
 
    buf = new Buffer 4
    types = bison.preCompile
      my-type: [
        {a: 'uint8'}
        {b: 'my-other-type'}
      ]
      my-other-type: [
        {c: 'uint8'}
        {d: 'uint8'}
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ a: 1,  b: { c: 3d: 4 }}
    # buf will equal [ 0x01, 0x03, 0x04 ] 

You can use other values as parameters to types The power of bison-types is evident as you define more complex types

    bison = require 'bison-types'
 
    buf = new Buffer 2
    types = bison.preCompile
      div:
        _write: (val, divider) -> @buffer.writeUInt8(val/divider)
      my-type: [
        {a: 'uint8'}
        {b: 'div(a)'}
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ a: 4,  b: 8}
    # buf will equal [ 0x04, 0x02 ] 

You can specify a specific value using the following syntax

    bison = require 'bison-types'
 
    buf = new Buffer 2
    types = bison.preCompile
      my-type: [
        {a: 'uint8=1'}
        {b: 'uint8=2'}
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{}
    # buf will equal [ 0x01, 0x02 ] 

You can specify arrays in a similar matter

    bison = require 'bison-types'
    buf = new Buffer 4 
    types = bison.preCompile
      object: [
        c: 'uint8'
      ]
      my-type: [
        {a: 'uint8'}
        {b: 'object[a]'}
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ a: 3b:[{c:1},{c:2},{c:3}}
    # buf will equal [ 0x03, 0x01, 0x02, 0x03 ] 

This is a shorthand of the above example

    bison = require 'bison-types'
    buf = new Buffer 4 
    types = bison.preCompile
      object: [
        c: 'uint8'
      ]
      my-type: [
        {a: 'uint8=b.length'}
        {b: 'object[b.length]'}
      ]
    options = {bigEndian: false}
    writer = new bison.Writer buftypesoptions
    writer.write 'my-type'{ b:[{c:1},{c:2},{c:3}}
    # buf will equal [ 0x03, 0x01, 0x02, 0x03 ] 
npm test