Skip to content

Instantly share code, notes, and snippets.

@Jxck
Created August 14, 2012 07:40
Show Gist options
  • Save Jxck/3347255 to your computer and use it in GitHub Desktop.
Save Jxck/3347255 to your computer and use it in GitHub Desktop.
binary parse for spdy
var log = console.log.bind(console);
function choiceReadLength(length) {
if (length <= 8) return 8;
if (length <= 16) return 16;
if (length <= 32) return 32;
throw new Error('too large length');
}
function choiceReadMethod(readLength) {
var flg = ~[8, 16, 32].indexOf(readLength)
if(!flg) {
throw new Error('length should be 8 or 16 or 32');
}
if (readLength != 8) readLength += 'BE';
return 'readUInt' + readLength;
}
function generateBinStr(length) {
if(length < 0) {
throw new Error('length should be grater than 0');
}
return (new Array(length + 1))
.slice()
.join('1');
}
function binStr2hexStr(bin) {
return eval('0x' + parseInt(bin, 2).toString(16));
}
function parse(buf, pos) {
var ans = {};
for (var i in pos) {
var p = pos[i];
var start = p[0];
var length = p[1];
var offset = Math.floor(start / 8);
var readLength = choiceReadLength(length);
var method = choiceReadMethod(readLength);
var mask = start - offset * 8;
var shift = readLength - mask - length;
//log(i, p, offset, readLength, mask, shift);
var temp = buf[method](offset);
var maskLength = readLength;
if (mask) {
maskLength -= mask;
}
var bin = generateBinStr(maskLength);
var hex = binStr2hexStr(bin);
temp = temp & hex;
if (shift) {
temp = temp >>> shift;
}
ans[i] = temp;
}
return ans;
}
function padding(buf, length) {
var str = buf.toString(2);
while (str.length < 8) {
str = '0' + str;
}
return str;
}
function showbuffer(buffers) {
for (i = 0; i < buffers.length; i++) {
log(padding(buffers[i], 8)
, buffers.readUInt8(i));
}
}
exports.choiceReadLength = choiceReadLength;
exports.choiceReadMethod = choiceReadMethod;
exports.generateBinStr = generateBinStr;
exports.binStr2hexStr = binStr2hexStr;
exports.parse = parse;
exports.showbuffer = showbuffer;
exports.padding = padding;
var log = console.log.bind(console);
var binparser = require('./binparser')
, assert = require('assert')
;
var parse = binparser.parse
, showbuf = binparser.showbuffer
, hex = binparser.binStr2hexStr
;
function case1() {
log('case1');
/* protocol
01234567 01234567 01234567 01234567
abcd---- e------- f--g---- --------
h------- -i------ ----j--- --------
k------- -------- -------- --------
*/
/* ex
11100111 00000011 10100001 10000111
00000001 00001000 00000000 11110100
00000000 00000000 00000000 00010101
a = 1, b = 1, c = 1, d = 7, e = 3
f = 5, g = 391, h = 2, i = 128
j = 244, k = 21
*/
var pos = {
a: [0, 1]
, b: [1, 1]
, c: [2, 1]
, d: [3, 5]
, e: [8, 8]
, f: [16, 3]
, g: [19, 13]
, h: [32, 9]
, i: [41, 11]
, j: [52, 12]
, k: [64, 32]
};
var buf = new Buffer(12);
buf.writeUInt8(hex('11100111'), 0);
buf.writeUInt8(hex('00000011'), 1);
buf.writeUInt8(hex('10100001'), 2);
buf.writeUInt8(hex('10000111'), 3);
buf.writeUInt8(hex('00000001'), 4);
buf.writeUInt8(hex('00001000'), 5);
buf.writeUInt8(hex('00000000'), 6);
buf.writeUInt8(hex('11110100'), 7);
buf.writeUInt8(hex('00000000'), 8);
buf.writeUInt8(hex('00000000'), 9);
buf.writeUInt8(hex('00000000'), 10);
buf.writeUInt8(hex('00010101'), 11);
var actual = parse(buf, pos);
// a: [0, 1]
// offset 0/8 => 0
// length 1 => readInt8
// 0-0 = 0 => 0xff
// 8-0-1 = 7 => >>> 7
var a = (buf.readUInt8(0) & 0xff) >>> 7;
assert.equal(actual['a'], a);
// b: [1, 1]
// offset 1/8 => 0
// length 1 => readInt8
// 1-0*8 = 1 => '01111111' 0x7f
// 8-1-1 = 6 => >>> 6
var b = (buf.readUInt8(0) & 0x7f) >>> 6;
assert.equal(actual['b'], b);
// c: [2, 1]
// offset 2/8 => 0
// length 1 => readInt8
// 2-0*8 = 2 => '00111111' 0x3f
// 8-2-1 = 5 => >>> 5
var c = (buf.readUInt8(0) & 0x3f) >>> 5;
assert.equal(actual['c'], c);
// d: [3, 5]
// offset 3/8 => 0
// length 1 => readInt8
// 3-0*8 = 3 => '00011111' 0x1f
// 8-3-5 = 0 => none
var d = (buf.readUInt8(0) & 0x1f);
assert.equal(actual['d'], d);
// e: [8, 8]
// offset 8/8 => 1
// length 8 => readInt8
// 8-1*8 = 0 => none
// 8-0-8 = 0 => none
var e = buf.readUInt8(1);
assert.equal(actual['e'], e);
// f: [16, 3]
// offset 16/8 => 2
// length 3 => readInt8
// 16-2*8 = 0 => 0xff
// 8-0-3 = 5 => >>> 5
var f = (buf.readInt8(2) & 0xff) >>> 5;
assert.equal(actual['f'], f);
// g: [19, 13]
// offset 19/8 => 2
// length 13 => readInt16BE
// 19-2*8 = 3 => '0001111111111111' 0x1fff
// 16-3-13 = 0 => none
var g = (buf.readInt16BE(2) & 0x1fff);
assert.equal(actual['g'], g);
// h: [32, 9]
// offset 32/8 => 4
// length 9 => readInt16BE
// 32-4*8 = 0 => 0xffff
// 16-0-9 = 7 => >>> 7
var h = (buf.readInt16BE(4) & 0xffff) >>> 7;
assert.equal(actual['h'], h);
// i: [41, 11]
// pos 41 は 41/8 = 5 なので、offset 5 を読む
// length 11 を読むので readInt16BE で足りる
// offset 5 は 5*8=40 から始まるので
// 41 から読んだら前の 41-40=1 ついらない
// 16-1=15 '111111111111111' => 0x7fff
// 11 しか読まないので 上の残り 後の 15-11=4 つがいらない
// >>> 5
var i = (buf.readInt16BE(5) & 0x7fff) >>> 4;
assert.equal(actual['i'], i);
// j: [52, 12]
// pos 52 は 52/8 = 6 なので、offset 6 を読む
// length 12 を読むので readInt16BE で足りる
// offset 6 は 6*8=48 から始まるので
// 52 から読んだら前の 52-48=4 ついらない
// 16-4=12 '111111111111' => 0xfff
// 12 しか読まないので 上の残り 後の 16-4-12=0 つがいらない
// >>> 0
var j = buf.readUInt16BE(6) & 0xfff;
assert.equal(actual['j'], j);
// k: [64, 32]
// offset 64/8 => 8
// length 32 => readInt32BE
// 64-8*8 = 0 => 0xffff
// 32-32-0 = 0 => none
var k = buf.readInt32BE(8) & 0xffff;
assert.equal(actual['k'], k);
}
function case2() {
log('case2');
/* protocol
01234567 01234567 01234567 01234567
ab------ -------- c------- --------
d------- e------- -------- --------
f------- -------- -------- --------
*/
/* ex
10000000 00000011 00000000 00000001
00000001 00000000 00000000 11110100
00000000 00000000 00000000 00000000
a = 1, b = 3, c = 1, d = 1, e = 244
f = 0
*/
var pos = {
a: [0, 1]
, b: [1, 15]
, c: [16, 16]
, d: [32, 8]
, e: [40, 24]
, f: [64, 32]
};
var buf = new Buffer(12);
buf.writeUInt8(0x80, 0);
buf.writeUInt8(0x3, 1);
buf.writeUInt8(0, 2);
buf.writeUInt8(0x1, 3);
buf.writeUInt8(0x1, 4);
buf.writeUInt8(0, 5);
buf.writeUInt8(0, 6);
buf.writeUInt8(0xf4, 7);
buf.writeUInt8(0, 8);
buf.writeUInt8(0, 9);
buf.writeUInt8(0, 10);
buf.writeUInt8(0, 11);
var actual = parse(buf, pos);
assert.equal(actual['a'], 1);
assert.equal(actual['b'], 3);
assert.equal(actual['c'], 1);
assert.equal(actual['d'], 1);
assert.equal(actual['e'], 244);
assert.equal(actual['f'], 0);
}
function utiltest() {
log('choiceReadLength');
var rl = binparser.choiceReadLength;
assert.equal(rl(0), 8);
assert.equal(rl(1), 8);
assert.equal(rl(8), 8);
assert.equal(rl(16), 16);
assert.equal(rl(17), 32);
assert.equal(rl(32), 32);
assert.throws(
function() { rl(33); },
/too large length/
);
log('choiceReadMethod');
var rm = binparser.choiceReadMethod;
assert.equal(rm(8), 'readUInt8');
assert.equal(rm(16), 'readUInt16BE');
assert.equal(rm(32), 'readUInt32BE');
assert.throws(
function() { rm(33); },
/length should be 8 or 16 or 32/
);
log('generateBinStr');
var gbs = binparser.generateBinStr;
assert.equal(gbs(0), '');
assert.equal(gbs(1), '1');
assert.equal(gbs(10), '1111111111');
assert.throws(
function() { gbs(-1); },
/length should be grater than 0/
);
log('binStr2bexStr');
var b2h = binparser.binStr2hexStr;
assert.equal(b2h('0'), 0x0);
assert.equal(b2h('1'), 0x1);
assert.equal(b2h('101'), 0x5);
assert.equal(b2h('001'), 0x1);
assert.equal(b2h('110010001'), 0x191);
}
(function() {
case1();
case2();
utiltest();
log('done');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment