Skip to content

Instantly share code, notes, and snippets.

@copy
Last active February 4, 2016 17:20
Show Gist options
  • Save copy/0f4669bc5636d698faa0 to your computer and use it in GitHub Desktop.
Save copy/0f4669bc5636d698faa0 to your computer and use it in GitHub Desktop.
"use strict";
// Format
//
// 000 1 bit integer
// 001 4 bit integer
// 002 8 bit integer
// 003 16 bit integer
// 004 32 bit integer
// 005 32 bit float
// 006 start of array
// 007 end of array
var TYPE_FLOAT = 5;
var TYPE_ARRAY_START = 6;
var TYPE_ARRAY_END = 7;
var USE_NODE_BUFFER = typeof Buffer !== "undefined" && !!Buffer.prototype.readInt32LE;
var NODE_BUFFER_NO_ASSERT = false;
var byson = {};
typeof module === "undefined" || (module.exports = byson);
byson._float32 = new Float32Array(1);
byson._int32 = new Int32Array(byson._float32.buffer);
/** @constructor */
byson.ParseError = function(message)
{
this.name = "ParseError";
this.message = message;
};
byson.ParseError.prototype = Error.prototype;
byson.encode = function(data)
{
//console.log("encode", data);
var state = {
data: [],
bit: 0,
};
byson._encode(state, data);
if(USE_NODE_BUFFER)
{
var b = new Buffer(state.data.length << 2);
for(var i = 0; i < state.data.length; i++)
{
b.writeInt32LE(state.data[i], i << 2, NODE_BUFFER_NO_ASSERT);
}
return b;
}
else
{
return new Int32Array(state.data).buffer;
}
};
byson._encode = function(state, data)
{
if(typeof data === "object") {
byson._encodeArray(state, data);
}
else if(typeof data === "number") {
byson._encodeNumber(state, data);
}
else {
byson._encodeBoolean(state, data);
}
};
byson._encodeNumber = function(state, data)
{
if((data | 0) === data) {
byson._encodeInt(state, data);
}
else {
byson._encodeFloat(state, data);
}
};
byson._encodeBoolean = function(state, bool)
{
byson._write(state, 4, bool << 3);
};
byson._encodeFloat = function(state, number)
{
byson._float32[0] = number;
byson._write(state, 3, TYPE_FLOAT);
byson._write(state, 32, byson._int32[0]);
};
byson._encodeInt = function(state, number)
{
if(number < 0)
{
if(-8 <= number) {
byson._write(state, 3+4, 1 | (number & 0xF) << 3);
}
else if(-0x80 <= number) {
byson._write(state, 3+8, 2 | (number & 0xFF) << 3);
}
else if(-0x8000 <= number) {
byson._write(state, 3+16, 3 | (number & 0xFFFF) << 3);
}
else {
byson._write(state, 3, 4);
byson._write(state, 32, number);
}
}
else
{
if(number < 2) {
byson._write(state, 3+1, number << 3);
}
else if(number < 8) {
byson._write(state, 3+4, 1 | number << 3);
}
else if(number < 0x80) {
byson._write(state, 3+8, 2 | number << 3);
}
else if(number < 0x8000) {
byson._write(state, 3+16, 3 | number << 3);
}
else {
byson._write(state, 3, 4);
byson._write(state, 32, number);
}
}
};
byson._encodeArray = function(state, array)
{
//console.assert(typeof array === "object" &&
// typeof array.length === "number", "Got invalid object: " + array);
// array
byson._write(state, 3, TYPE_ARRAY_START);
//console.log("array [");
for(var i = 0; i < array.length; i++) {
byson._encode(state, array[i]);
}
byson._write(state, 3, TYPE_ARRAY_END);
//console.log("]");
};
byson._write = function(state, len, value) {
var bit = state.bit;
//console.assert(len <= 32);
//console.assert(bit <= 32);
//console.log("write bit=" + bit + " len=" + len + " value=" + value);
if(bit === 0) {
state.data.push(value);
}
else {
var lastIndex = state.data.length - 1;
state.data[lastIndex] |= value << bit;
//console.log(state.data[lastIndex].toString(16));
if((bit + len) > 32) {
state.data.push(value >>> -bit);
//console.log(state.data[lastIndex+1].toString(16));
}
}
state.bit = bit + len & 31;
};
byson.decode = function(data)
{
//console.assert(data instanceof ArrayBuffer);
//console.assert((data.byteLength & 3) === 0); // may be changed later
//console.log(data, typeof data, data.constructor);
var state = {
data: USE_NODE_BUFFER ? data : new Int32Array(data),
byteLength: USE_NODE_BUFFER ? data.length : data.byteLength,
bit: 0,
};
var decoded = byson._decode(state);
if(decoded === undefined) {
throw new byson.ParseError();
}
//console.log("decode", decoded);
return decoded;
};
/** @this {byson} */
byson._decode = function(state)
{
var id = this._read(state, 3);
//console.log("id:", id);
if(id === TYPE_FLOAT) {
return this._decodeFloat(state);
}
else if(id === TYPE_ARRAY_START) {
return this._decodeArray(state);
}
else if(id === TYPE_ARRAY_END) {
// end of array
return undefined;
}
else {
return this._decodeInt(id, state);
}
};
byson._decodeFloat = function(state)
{
var data = this._read(state, 32);
byson._int32[0] = data;
//console.log("float", byson._float32[0]);
return byson._float32[0];
};
byson._decodeArray = function(state)
{
var result = [];
var lastBit = state.byteLength << 5;
//console.log("array [");
while(true) {
var p = this._decode(state);
if(p === undefined) {
break;
}
if(state.bit >= lastBit) {
throw new byson.ParseError();
}
result.push(p);
}
//console.log("]", result);
return result;
};
byson._decodeInt = function(id, state)
{
//console.assert(id !== undefined);
var result;
if(id === 0) {
result = this._read(state, 1);
}
else {
var len = 2 << id;
result = this._read(state, len) << -len >> -len;
}
//console.log("int type=" + type, result);
return result;
};
byson._read = function(state, len)
{
var offset = state.bit;
var bit = offset & 31;
var index = offset >>> 5;
var result;
if(bit === 0)
{
result = byson._readInt32s(state.data, index);
}
else
{
result = byson._readInt32s(state.data, index) >>> bit;
if((bit + len) > 32) {
result |= byson._readInt32s(state.data, index + 1) << -bit;
}
}
if(len !== 32) {
result &= (1 << len) - 1;
}
state.bit += len;
return result;
};
if(USE_NODE_BUFFER)
{
byson._readInt32s = function(data, offset)
{
return data.readInt32LE(offset << 2, NODE_BUFFER_NO_ASSERT);
};
}
else
{
byson._readInt32s = function(data, offset)
{
return data[offset];
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment