├── benchmark └── benchmark.js ├── test ├── mocha.opts └── basic.js ├── index.js ├── .travis.yml ├── examples ├── index.js ├── parse.js ├── structures.js └── serialize.js ├── lib ├── schema.js ├── datatypes.js └── cstruct.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /benchmark/benchmark.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --check-leaks 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/cstruct'); 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | require('./structures'); 2 | require('./serialize'); 3 | require('./parse'); 4 | -------------------------------------------------------------------------------- /lib/schema.js: -------------------------------------------------------------------------------- 1 | // translate the given data into actual schema 2 | // that the next function will understand 3 | 4 | function Schema(obj) { 5 | // translate eveything in the factory 6 | this.factory(obj); 7 | } 8 | 9 | Schema.prototype.factory = function(obj) { 10 | this.tree = obj; 11 | }; 12 | 13 | var schema = module.exports = exports = Schema; 14 | -------------------------------------------------------------------------------- /examples/parse.js: -------------------------------------------------------------------------------- 1 | var _ = require('../'); 2 | 3 | // buffer to object 4 | var obj = _.unpackSync('Player', new Buffer('0800466f6f62617200000000000000000000e8030038564c056477656c636f6d65007765656b6c7900017472617073206f66207468756e646572000000000000000000000000000000000fe20d3301020000000000000000000000000000000000000000000000000000000000000000000000000000666174616c20626c6f770000000000000000000000000000000000000000000000000000000367616c76616e6f20737472696b65000000000000000000000000000000000000000000000066005f01ffe0f5050000', 'hex')); 5 | 6 | console.log('------- parsing -------'); 7 | console.log(obj); 8 | -------------------------------------------------------------------------------- /examples/structures.js: -------------------------------------------------------------------------------- 1 | var _ = require('../'); 2 | 3 | var playerSchema = new _.Schema({ 4 | id: _.type.u16(8), 5 | name: _.type.string(16), 6 | hp: _.type.uint24, 7 | exp: _.type.uint32, 8 | status: _.type.uint8, 9 | motd: _.type.string(), // null-terminated 10 | motw: _.type.string(), // null-terminated 11 | skills: [{ 12 | id: _.type.uint8, 13 | name: _.type.string(32), 14 | secret: _.type.uint40 15 | }], 16 | position: { 17 | x: _.type.uint16, 18 | y: _.type.uint16 19 | }, 20 | hash: _.type.uint48 21 | }); 22 | 23 | // register 24 | _.register('Player', playerSchema); 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | -------------------------------------------------------------------------------- /examples/serialize.js: -------------------------------------------------------------------------------- 1 | var _ = require('../'); 2 | 3 | // object to buffer 4 | var buf = _.packSync('Player', { 5 | id: 1, 6 | name: 'Foobar', 7 | hp: 1000, 8 | exp: 88888888, 9 | status: 100, 10 | skills: [{ 11 | id: 1, 12 | name: 'traps of thunder', 13 | secret: 5151515151 14 | }, { 15 | id: 2, 16 | }, { 17 | name: 'fatal blow' 18 | }, { 19 | id: 3, 20 | name: 'galvano strike' 21 | }], 22 | position: { 23 | x: 102, 24 | y: 351 25 | }, 26 | motd: 'welcome', 27 | motw: 'weekly', 28 | hash: 99999999, 29 | }); 30 | 31 | console.log('------- serializing -------'); 32 | console.log(buf); 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c-struct", 3 | "version": "0.0.5", 4 | "description": "A C-struct library to manage binary structures with Node.JS", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "./node_modules/mocha/bin/mocha" 11 | }, 12 | "repository": "majimboo/c-struct", 13 | "keywords": [ 14 | "struct", 15 | "binary", 16 | "pack", 17 | "unpack", 18 | "parse", 19 | "serialize", 20 | "endianness", 21 | "buffer", 22 | "c", 23 | "packets", 24 | "structure" 25 | ], 26 | "author": "Majid Arif Siddiqui (http://blog.majidarif.com/)", 27 | "license": "MIT", 28 | "devDependencies": { 29 | "chai": "^4.1.2", 30 | "mocha": "^5.0.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Majid Arif Siddiqui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /lib/datatypes.js: -------------------------------------------------------------------------------- 1 | var types = module.exports = {}; 2 | 3 | types.string = function(size) { 4 | return size ? 'string:' + size : 'cstring'; 5 | }; 6 | 7 | types.boolean = 'boolean'; 8 | types.nibble = 'nibble'; 9 | 10 | types.uint8 = 'uint8'; 11 | types.u8 = function(def) { 12 | return types.uint8 + '-' + def; 13 | }; 14 | 15 | types.uint16 = 'uint16'; 16 | types.u16 = function(def) { 17 | return types.uint16 + '-' + def; 18 | }; 19 | 20 | types.uint24 = 'uint24'; 21 | types.u24 = function(def) { 22 | return types.uint24 + '-' + def; 23 | }; 24 | 25 | types.uint32 = 'uint32'; 26 | types.u32 = function(def) { 27 | return types.uint32 + '-' + def; 28 | }; 29 | 30 | types.uint40 = 'uint40'; 31 | types.u40 = function(def) { 32 | return types.uint40 + '-' + def; 33 | }; 34 | 35 | types.uint48 = 'uint48'; 36 | types.u48 = function(def) { 37 | return types.uint48 + '-' + def; 38 | }; 39 | 40 | types.uint56 = 'uint56'; 41 | types.u56 = function(def) { 42 | return types.uint56 + '-' + def; 43 | }; 44 | 45 | types.uint64 = 'uint64'; 46 | types.u64 = function(def) { 47 | return types.uint64 + '-' + def; 48 | }; 49 | 50 | types.int8 = 'int8'; 51 | types.int16 = 'int16'; 52 | types.int24 = 'int24'; 53 | types.int32 = 'int32'; 54 | types.int40 = 'int40'; 55 | types.int48 = 'int48'; 56 | types.int56 = 'int56'; 57 | types.int64 = 'int64'; 58 | 59 | types.double = 'double'; 60 | types.float = 'float'; 61 | 62 | types.le = 'l'; 63 | types.be = 'b'; 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | c-struct [![Build Status](https://travis-ci.org/majimboo/c-struct.svg?branch=master)](https://travis-ci.org/majimboo/c-struct) 2 | ======== 3 | 4 | [![NPM](https://nodei.co/npm/c-struct.png?downloads=true)](https://nodei.co/npm/c-struct/) 5 | 6 | A fast binary data packing & unpacking library for node.js designed for multiplayer games. 7 | 8 | What can it do? 9 | --------------- 10 | 11 | * 8, 16, 24, 32, 40, 48, (56 and 64*) bit unsigned integers. 12 | * String with length and null-terminated cstrings. 13 | * Boolean, nibble, float and double. 14 | * Big and little endianness. 15 | * Schema based packing and unpacking. 16 | * Unpack from buffer to object. 17 | * Pack from object to buffer. 18 | * Pack with default value if key isn't specified. 19 | 20 | More 21 | ---- 22 | * Available via [npm](https://npmjs.org/package/c-struct). 23 | * Zero production dependencies. 24 | 25 | Installation 26 | ------------ 27 | 28 | npm install c-struct --save 29 | 30 | > Execute `$ node examples/` to see the examples. 31 | 32 | Usage 33 | ----- 34 | 35 | #### Unpacking #### 36 | 37 | var _ = require('c-struct'); 38 | 39 | var playerSchema = new _.Schema({ 40 | id: _.type.uint16, 41 | name: _.type.string(16), 42 | hp: _.type.uint24, 43 | exp: _.type.uint32, 44 | status: _.type.uint8, 45 | motd: _.type.string(), // null-terminated if no length 46 | motw: _.type.string(), // cstring if no length 47 | skills: [{ 48 | id: _.type.uint8, 49 | name: _.type.string(32), 50 | secret: _.type.uint40 51 | }], 52 | position: { 53 | x: _.type.uint16, 54 | y: _.type.uint16 55 | }, 56 | hash: _.type.uint48 57 | }); 58 | 59 | // register to cache 60 | _.register('Player', playerSchema); 61 | 62 | // object to buffer | this can be on another file 63 | var buf = _.packSync('Player', { 64 | id: 1, 65 | name: 'Foobar', 66 | hp: 1000, 67 | exp: 88888888, 68 | status: 100, 69 | skills: [{ 70 | id: 1, 71 | name: 'traps of thunder', 72 | secret: 5151515151 73 | }, { 74 | id: 2, 75 | }, { 76 | name: 'fatal blow' 77 | }, { 78 | id: 3, 79 | name: 'galvano strike' 80 | }], 81 | position: { 82 | x: 102, 83 | y: 351 84 | }, 85 | motd: 'welcome', 86 | motw: 'weekly', 87 | hash: 99999999, 88 | }); 89 | 90 | #### Packing #### 91 | 92 | var _ = require('c-struct'); 93 | 94 | var playerSchema = new _.Schema({ 95 | id: _.type.uint16, 96 | name: _.type.string(16), 97 | hp: _.type.uint24, 98 | exp: _.type.uint32, 99 | status: _.type.uint8, 100 | motd: _.type.string(), // null-terminated if no length 101 | motw: _.type.string(), // cstring if no length 102 | skills: [{ 103 | id: _.type.uint8, 104 | name: _.type.string(32), 105 | secret: _.type.uint40 106 | }], 107 | position: { 108 | x: _.type.uint16, 109 | y: _.type.uint16 110 | }, 111 | hash: _.type.uint48 112 | }); 113 | 114 | // register to cache 115 | _.register('Player', playerSchema); 116 | 117 | // buffer to object | this can be on another file 118 | var obj = _.unpackSync('Player', BUFFER_HERE); 119 | 120 | 121 | API 122 | --- 123 | 124 | Currently only unsigned values in little endian are supported 125 | 126 | _.type.uint8 // unsigned char 127 | _.type.uint16 // unsigned short 128 | _.type.uint24 129 | _.type.uint32 // unsigned long 130 | _.type.uint40 131 | _.type.uint48 132 | _.type.uint56 133 | _.type.uint64 134 | 135 | You can also specify default values 136 | 137 | _.type.u8(9999) // default value 9999 138 | _.type.u16(999) // default value 999 139 | _.type.u24(888) // default value 888 140 | _.type.u32(777) // default value 777 141 | _.type.u40(666) // default value 666 142 | _.type.u48(555) // default value 555 143 | _.type.u56(444) // default value 444 144 | _.type.u64(333) // default value 333 145 | 146 | Configurations 147 | ---- 148 | 149 | > There is currently no configs. 150 | 151 | TODO 152 | ---- 153 | 154 | - add configurable endianness 155 | - add async methods 156 | - add signed 157 | - add boolean and nibble 158 | - add float and double 159 | - add benchmark 160 | - documentation 161 | - more testing 162 | 163 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var should = chai.should(); 3 | var expect = chai.expect; 4 | var _ = require('../'); 5 | 6 | describe('#c-struct', function () { 7 | it('should pack object to buffer with default values', function (done) { 8 | 9 | var playerSchema = new _.Schema({ 10 | id: _.type.u16(5), 11 | name: _.type.string(16), 12 | hp: _.type.u24(5000), 13 | exp: _.type.u32(99999), 14 | status: _.type.uint8, 15 | motd: _.type.string(), // null-terminated 16 | motw: _.type.string(), // null-terminated 17 | skills: [{ 18 | id: _.type.uint8, 19 | name: _.type.string(32), 20 | secret: _.type.uint40 21 | }], 22 | position: { 23 | x: _.type.uint16, 24 | y: _.type.uint16 25 | }, 26 | hash: _.type.uint48 27 | }); 28 | 29 | // register 30 | _.register('Player', playerSchema); 31 | 32 | // object to buffer 33 | var buf = _.packSync('Player', { 34 | name: 'Foobar', 35 | status: 100, 36 | skills: [{ 37 | id: 1, 38 | name: 'traps of thunder', 39 | secret: 5151515151 40 | }, { 41 | id: 2, 42 | }, { 43 | name: 'fatal blow' 44 | }, { 45 | id: 3, 46 | name: 'galvano strike' 47 | }], 48 | position: { 49 | x: 102, 50 | y: 351 51 | }, 52 | motd: 'welcome', 53 | motw: 'weekly', 54 | hash: 99999999, 55 | }); 56 | 57 | // buffer to object 58 | buf.toString('hex').should.equal('0500466f6f626172000000000000000000008813009f8601006477656c636f6d65007765656b6c7900017472617073206f66207468756e646572000000000000000000000000000000000fe20d3301020000000000000000000000000000000000000000000000000000000000000000000000000000666174616c20626c6f770000000000000000000000000000000000000000000000000000000367616c76616e6f20737472696b65000000000000000000000000000000000000000000000066005f01ffe0f5050000'); 59 | buf.toString('hex').should.not.equal('0100466f6f62617200000000000000000000e8030038564c056477656c636f6d65007765656b6c7900017472617073206f66207468756e646572000000000000000000000000000000000fe20d3301020000000000000000000000000000000000000000000000000000000000000000000000000000666174616c20626c6f770000000000000000000000000000000000000000000000000000000367616c76616e6f20737472696b65000000000000000000000000000000000000000000000066005f01ffe0f5050000'); 60 | done(); 61 | }); 62 | 63 | it ('uint16', () => { 64 | const orig = 21; 65 | 66 | let data = _.packers['uint16l'](orig); 67 | let buf = Buffer.from(data); 68 | buf.toString('hex').should.equal('1500'); 69 | 70 | data = _.packers['uint16b'](orig); 71 | buf = Buffer.from(data); 72 | buf.toString('hex').should.equal('0015'); 73 | const actual = _.unpackers['uint16b']({ offset: 0 },buf); 74 | actual.should.equal(orig); 75 | }); 76 | 77 | it ('uint24', () => { 78 | const orig = 21; 79 | 80 | let data = _.packers['uint24l'](orig); 81 | let buf = Buffer.from(data); 82 | buf.toString('hex').should.equal('150000'); 83 | 84 | data = _.packers['uint24b'](orig); 85 | buf = Buffer.from(data); 86 | buf.toString('hex').should.equal('000015'); 87 | const actual = _.unpackers['uint24b']({ offset: 0 },buf); 88 | actual.should.equal(orig); 89 | }); 90 | 91 | it ('uint32', () => { 92 | const orig = 21; 93 | 94 | let data = _.packers['uint32l'](orig); 95 | let buf = Buffer.from(data); 96 | buf.toString('hex').should.equal('15000000'); 97 | 98 | data = _.packers['uint32b'](orig); 99 | buf = Buffer.from(data); 100 | buf.toString('hex').should.equal('00000015'); 101 | const actual = _.unpackers['uint32b']({ offset: 0 },buf); 102 | actual.should.equal(orig); 103 | }); 104 | 105 | it ('uint40', () => { 106 | const orig = 21; 107 | 108 | let data = _.packers['uint40l'](orig); 109 | let buf = Buffer.from(data); 110 | buf.toString('hex').should.equal('1500000000'); 111 | 112 | data = _.packers['uint40b'](orig); 113 | buf = Buffer.from(data); 114 | buf.toString('hex').should.equal('0000000015'); 115 | const actual = _.unpackers['uint40b']({ offset: 0 },buf); 116 | actual.should.equal(orig); 117 | }); 118 | 119 | it ('uint48', () => { 120 | const orig = 21; 121 | 122 | let data = _.packers['uint48l'](orig); 123 | let buf = Buffer.from(data); 124 | buf.toString('hex').should.equal('150000000000'); 125 | 126 | data = _.packers['uint48b'](orig); 127 | buf = Buffer.from(data); 128 | buf.toString('hex').should.equal('000000000015'); 129 | const actual = _.unpackers['uint48b']({ offset: 0 },buf); 130 | actual.should.equal(orig); 131 | }); 132 | 133 | it ('uint56', () => { 134 | const orig = 21; 135 | 136 | let data = _.packers['uint56l'](orig); 137 | let buf = Buffer.from(data); 138 | buf.toString('hex').should.equal('15000000000000'); 139 | 140 | data = _.packers['uint56b'](orig); 141 | buf = Buffer.from(data); 142 | buf.toString('hex').should.equal('00000000000015'); 143 | const actual = _.unpackers['uint56b']({ offset: 0 },buf); 144 | actual.should.equal(orig); 145 | }); 146 | 147 | it ('uint64', () => { 148 | const orig = 21; 149 | 150 | let data = _.packers['uint64l'](orig); 151 | let buf = Buffer.from(data); 152 | buf.toString('hex').should.equal('1500000000000000'); 153 | 154 | data = _.packers['uint64b'](orig); 155 | buf = Buffer.from(data); 156 | buf.toString('hex').should.equal('0000000000000015'); 157 | const actual = _.unpackers['uint64b']({ offset: 0 },buf); 158 | actual.should.equal(orig); 159 | }); 160 | 161 | it('should unpack buffer to object', function (done) { 162 | 163 | var playerSchema = new _.Schema({ 164 | id: _.type.uint16, 165 | name: _.type.string(16), 166 | hp: _.type.uint24, 167 | exp: _.type.uint32, 168 | status: _.type.uint8, 169 | motd: _.type.string(), // null-terminated 170 | motw: _.type.string(), // null-terminated 171 | skills: [{ 172 | id: _.type.uint8, 173 | name: _.type.string(32), 174 | secret: _.type.uint40 175 | }], 176 | position: { 177 | x: _.type.uint16, 178 | y: _.type.uint16 179 | }, 180 | hash: _.type.uint48 181 | }); 182 | 183 | // register 184 | _.register('Player', playerSchema); 185 | 186 | // object to buffer 187 | var buf = _.packSync('Player', { 188 | id: 1, 189 | name: 'Foobar', 190 | hp: 1000, 191 | exp: 88888888, 192 | status: 100, 193 | skills: [{ 194 | id: 1, 195 | name: 'traps of thunder', 196 | secret: 5151515151 197 | }, { 198 | id: 2, 199 | }, { 200 | name: 'fatal blow' 201 | }, { 202 | id: 3, 203 | name: 'galvano strike' 204 | }], 205 | position: { 206 | x: 102, 207 | y: 351 208 | }, 209 | motd: 'welcome', 210 | motw: 'weekly', 211 | hash: 99999999, 212 | }); 213 | 214 | // buffer to object 215 | var obj = _.unpackSync('Player', buf); 216 | 217 | obj.should.have.property('id', 1); 218 | obj.should.have.property('name', 'Foobar'); 219 | obj.should.have.property('hp', 1000); 220 | obj.should.have.property('exp', 88888888); 221 | obj.should.have.property('status', 100); 222 | 223 | done(); 224 | }); 225 | 226 | it('should unpack array objects', function () { 227 | const schema = new _.Schema({ 228 | type: _.type.string(4), 229 | version: _.type.uint8, 230 | id: _.type.string(16), 231 | count: _.type.uint8, 232 | collection: [{ 233 | id: _.type.string(16), 234 | sequenceNumber: _.type.uint16 235 | }] 236 | }); 237 | _.register('Schema', schema); 238 | 239 | let data = { 240 | type: 'abcd', 241 | version: 1, 242 | id: 'abcdabcdabcdabcd', 243 | count: 3, 244 | collection: [{ 245 | id: 'efghefghefghefgh', 246 | sequenceNumber: 1, 247 | }, { 248 | id: 'hijkhijkhijkhijk', 249 | sequenceNumber: 2, 250 | }, { 251 | id: 'abcdefghijklmnop', 252 | sequenceNumber: 3, 253 | }] 254 | }; 255 | const packedData = _.packSync('Schema', data); 256 | const unpackedData = _.unpackSync('Schema', packedData); 257 | expect(unpackedData).to.deep.equal(data); 258 | }); 259 | }); 260 | -------------------------------------------------------------------------------- /lib/cstruct.js: -------------------------------------------------------------------------------- 1 | // Format | C Type | JavaScript Type | Size (octets) | Notes 2 | // ------------------------------------------------------------------- 3 | // A | char[] | Array | Length | (1) 4 | // x | pad byte | N/A | 1 | 5 | // c | char | string (length 1) | 1 | (2) 6 | // b | signed char | number | 1 | (3) 7 | // B | unsigned char | number | 1 | (3) 8 | // h | signed short | number | 2 | (3) 9 | // H | unsigned short | number | 2 | (3) 10 | // i | signed long | number | 4 | (3) 11 | // I | unsigned long | number | 4 | (3) 12 | // l | signed long | number | 4 | (3) 13 | // L | unsigned long | number | 4 | (3) 14 | // s | char[] | string | Length | (2) 15 | // f | float | number | 4 | (4) 16 | // d | double | number | 8 | (5) 17 | 18 | var Schema = require('./schema'); 19 | var DataTypes = require('./datatypes'); 20 | 21 | var cstruct = module.exports = {}; 22 | 23 | /** 24 | * Private 25 | */ 26 | var schemas = {}; 27 | 28 | /** 29 | * [register description] 30 | * @param {[type]} name [description] 31 | * @param {[type]} schema [description] 32 | * @return {[type]} [description] 33 | * 34 | * @api public 35 | */ 36 | cstruct.register = function (name, schema) { 37 | // cache schema 38 | schemas[name] = schema; 39 | 40 | return schemas[name]; 41 | }; 42 | 43 | /** 44 | * [unpackSync description] 45 | * @param {[type]} name [description] 46 | * @param {[type]} buffer [description] 47 | * @param {[type]} options [description] 48 | * @return {[type]} [description] 49 | * 50 | * @api public 51 | */ 52 | cstruct.unpackSync = function (name, buffer, options) { 53 | options = options || {}; 54 | var byteord = options.endian || DataTypes.le; 55 | var schema = schemas[name].tree; 56 | 57 | function next(pointer, buf, scheme) { 58 | var res = {}; 59 | 60 | if (arguments.length === 2) { 61 | scheme = buf; 62 | buf = pointer; 63 | pointer = { 64 | offset: 0 65 | }; 66 | } 67 | 68 | for (var el in scheme) { 69 | 70 | var s = scheme[el]; 71 | if (typeof s === 'string') { 72 | // string 73 | if (s.indexOf('string') > -1) { 74 | var xstr = s.split(':'); 75 | res[el] = unpacker[xstr[0]](pointer, buf, parseInt(xstr[1])); 76 | } 77 | // number 78 | else { 79 | if (s.indexOf('-') > -1) { 80 | var xnum = s.split('-'); 81 | res[el] = unpacker[xnum[0] + byteord](pointer, buf); 82 | } else { 83 | res[el] = unpacker[s + byteord](pointer, buf); 84 | } 85 | } 86 | } else if (Array.isArray(s)) { 87 | res[el] = list(pointer, buf, s[0]); 88 | } else { 89 | res[el] = next(pointer, buf, s); 90 | } 91 | } 92 | 93 | return res; 94 | } 95 | 96 | function list(pointer, buf, scheme) { 97 | var len = buf.length; 98 | var arlen = 0; 99 | var res = []; 100 | 101 | for (var el in scheme) { 102 | var s = scheme[el]; 103 | if (s === 'uint8') { 104 | arlen += 1; 105 | } 106 | if (s === 'uint16') { 107 | arlen += 2; 108 | } 109 | if (s === 'uint32') { 110 | arlen += 4; 111 | } 112 | if (s.indexOf('string') > -1) { 113 | arlen += parseInt(s.split(':')[1]); 114 | } 115 | } 116 | 117 | var m = pointer.offset + arlen; 118 | var count = 0; 119 | 120 | while (m <= len) { 121 | m += arlen; 122 | count++; 123 | } 124 | 125 | while (count--) { 126 | res.push(next(pointer, buf, scheme)); 127 | } 128 | 129 | return res; 130 | } 131 | 132 | return next(buffer, schema); 133 | }; 134 | 135 | /** 136 | * [packSync description] 137 | * @param {[type]} name [description] 138 | * @param {[type]} object [description] 139 | * @param {[type]} options [description] 140 | * @return {[type]} [description] 141 | * 142 | * @api public 143 | */ 144 | cstruct.packSync = function (name, object, options) { 145 | options = options || {}; 146 | var byteord = options.endian || DataTypes.le; 147 | var schema = schemas[name].tree; 148 | 149 | function next(obj, scheme) { 150 | var binary = []; 151 | 152 | for (var el in scheme) { 153 | var s = scheme[el]; 154 | if (typeof s === 'string') { 155 | // string 156 | if (s.indexOf('string') > -1) { 157 | var xstr = s.split(':'); 158 | binary.push(packer[xstr[0]](obj[el], parseInt(xstr[1]))); 159 | } 160 | // number 161 | else { 162 | if (s.indexOf('-') > -1) { 163 | var xnum = s.split('-'); 164 | binary.push(packer[xnum[0] + byteord](obj[el] || xnum[1])); 165 | } else { 166 | binary.push(packer[s + byteord](obj[el])); 167 | } 168 | } 169 | } else if (Array.isArray(s)) { 170 | binary.push(list(obj[el], s[0])); 171 | } else { 172 | binary.push(next(obj[el], s)); 173 | } 174 | } 175 | 176 | return [].concat.apply([], binary); 177 | } 178 | 179 | function list(val, scheme) { 180 | if (!Array.isArray(val)) 181 | throw Error('Value is not array.'); 182 | 183 | var res = []; 184 | var len = val.length; 185 | 186 | for (var i = 0; i < len; i++) { 187 | res.push(next(val[i], scheme)); 188 | } 189 | 190 | return [].concat.apply([], res); 191 | } 192 | 193 | return new Buffer(next(object, schema)); 194 | }; 195 | 196 | /** 197 | * Expose 198 | * 199 | * @api public 200 | */ 201 | cstruct.Schema = Schema; 202 | cstruct.type = DataTypes; 203 | 204 | /** 205 | * Packer Routines 206 | * 207 | * @api private 208 | */ 209 | var packer = {}; 210 | 211 | packer.string = function (val, sz) { 212 | var binary = []; 213 | 214 | for (var i = 0; i < sz; i++) { 215 | // hakish 216 | try { 217 | binary.push(val.charCodeAt(i) || 0); 218 | } catch (ex) { 219 | binary.push(0); 220 | } 221 | } 222 | 223 | return binary; 224 | }; 225 | 226 | packer.cstring = function (val, sz) { 227 | var binary = []; 228 | 229 | for (var i = 0; i < val.length; i++) { 230 | binary.push(val.charCodeAt(i)); 231 | } 232 | 233 | // append null-byte 234 | binary.push(0x00); 235 | 236 | return binary; 237 | }; 238 | 239 | packer.boolean = function (val) { 240 | return val ? 1 : 0; 241 | }; 242 | 243 | packer.nibble = function (val) { 244 | throw new Error('Not Yet Impelemented'); 245 | }; 246 | 247 | packer.uint8l = function (val) { 248 | return val & 0xFF; 249 | }; 250 | packer.uint8b = function (val) { 251 | return val & 0xFF; 252 | }; 253 | 254 | packer.uint16l = function (val) { 255 | var binary = []; 256 | binary[0] = val & 0xff; 257 | binary[1] = (val >> 8) & 0xff; 258 | return binary; 259 | }; 260 | packer.uint16b = function (val) { 261 | var binary = []; 262 | binary[0] = (val >> 8) & 0xff; 263 | binary[1] = (val) & 0xff; 264 | return binary; 265 | }; 266 | 267 | packer.uint24l = function (val) { 268 | var binary = []; 269 | binary[0] = val & 0xFF; 270 | binary[1] = (val >> 8) & 0xFF; 271 | binary[2] = (val >> 16) & 0xFF; 272 | return binary; 273 | }; 274 | packer.uint24b = function (val) { 275 | var binary = []; 276 | binary[0] = (val >> 16) & 0xFF; 277 | binary[1] = (val >> 8) & 0xFF; 278 | binary[2] = (val) & 0xFF; 279 | return binary; 280 | }; 281 | 282 | packer.uint32l = function (val) { 283 | var binary = []; 284 | binary[0] = val & 0xFF; 285 | binary[1] = (val >> 8) & 0xFF; 286 | binary[2] = (val >> 16) & 0xFF; 287 | binary[3] = (val >> 24) & 0xFF; 288 | return binary; 289 | }; 290 | packer.uint32b = function (val) { 291 | var binary = []; 292 | binary[0] = (val >> 24) & 0xFF; 293 | binary[1] = (val >> 16) & 0xFF; 294 | binary[2] = (val >> 8) & 0xFF; 295 | binary[3] = (val) & 0xFF; 296 | return binary; 297 | }; 298 | 299 | packer.uint40l = function (val) { 300 | var binary = []; 301 | binary[0] = val & 0xFF; 302 | binary[1] = (val >> 8) & 0xFF; 303 | binary[2] = (val >> 16) & 0xFF; 304 | binary[3] = (val >> 24) & 0xFF; 305 | binary[4] = (val / 0x100000000) & 0xFF; 306 | return binary; 307 | }; 308 | packer.uint40b = function (val) { 309 | var binary = []; 310 | binary[0] = (val / 0x100000000) & 0xFF; 311 | binary[1] = (val >> 24) & 0xFF; 312 | binary[2] = (val >> 16) & 0xFF; 313 | binary[3] = (val >> 8) & 0xFF; 314 | binary[4] = (val) & 0xFF; 315 | return binary; 316 | }; 317 | 318 | packer.uint48l = function (val) { 319 | var binary = []; 320 | binary[0] = val & 0xFF; 321 | binary[1] = (val >> 8) & 0xFF; 322 | binary[2] = (val >> 16) & 0xFF; 323 | binary[3] = (val >> 24) & 0xFF; 324 | binary[4] = (val / 0x100000000) & 0xFF; 325 | binary[5] = (val / 0x10000000000) & 0xFF; 326 | return binary; 327 | }; 328 | 329 | packer.uint48b = function (val) { 330 | var binary = []; 331 | binary[0] = (val / 0x10000000000) & 0xFF; 332 | binary[1] = (val / 0x100000000) & 0xFF; 333 | binary[2] = (val >> 24) & 0xFF; 334 | binary[3] = (val >> 16) & 0xFF; 335 | binary[4] = (val >> 8) & 0xFF; 336 | binary[5] = (val) & 0xFF; 337 | return binary; 338 | }; 339 | 340 | packer.uint56l = function (val) { 341 | var binary = []; 342 | binary[0] = val & 0xFF; 343 | binary[1] = (val >> 8) & 0xFF; 344 | binary[2] = (val >> 16) & 0xFF; 345 | binary[3] = (val >> 24) & 0xFF; 346 | binary[4] = (val / 0x100000000) & 0xFF; 347 | binary[5] = (val / 0x10000000000) & 0xFF; 348 | binary[6] = (val / 0x1000000000000) & 0xFF; 349 | return binary; 350 | }; 351 | 352 | packer.uint56b = function (val) { 353 | var binary = []; 354 | binary[0] = (val / 0x1000000000000) & 0xFF; 355 | binary[1] = (val / 0x10000000000) & 0xFF; 356 | binary[2] = (val / 0x100000000) & 0xFF; 357 | binary[3] = (val >> 24) & 0xFF; 358 | binary[4] = (val >> 16) & 0xFF; 359 | binary[5] = (val >> 8) & 0xFF; 360 | binary[6] = (val) & 0xFF; 361 | return binary; 362 | }; 363 | 364 | packer.uint64l = function (val) { 365 | var binary = []; 366 | binary[0] = val & 0xFF; 367 | binary[1] = (val >> 8) & 0xFF; 368 | binary[2] = (val >> 16) & 0xFF; 369 | binary[3] = (val >> 24) & 0xFF; 370 | binary[4] = (val / 0x100000000) & 0xFF; 371 | binary[5] = (val / 0x10000000000) & 0xFF; 372 | binary[6] = (val / 0x1000000000000) & 0xFF; 373 | binary[7] = (val / 0x100000000000000) & 0xFF; 374 | return binary; 375 | }; 376 | 377 | packer.uint64b = function (val) { 378 | var binary = []; 379 | binary[0] = (val / 0x100000000000000) & 0xFF; 380 | binary[1] = (val / 0x1000000000000) & 0xFF; 381 | binary[2] = (val / 0x10000000000) & 0xFF; 382 | binary[3] = (val / 0x100000000) & 0xFF; 383 | binary[4] = (val >> 24) & 0xFF; 384 | binary[5] = (val >> 16) & 0xFF; 385 | binary[6] = (val >> 8) & 0xFF; 386 | binary[7] = (val) & 0xFF; 387 | return binary; 388 | }; 389 | 390 | /** 391 | * Unpacker Routines 392 | * 393 | * @api private 394 | */ 395 | var unpacker = {}; 396 | 397 | unpacker.string = function (pointer, buffer, sz) { 398 | var string = ''; 399 | var eos = false; 400 | 401 | for (var i = 0; i < sz; i++) { 402 | var byte = buffer[pointer.offset++]; 403 | if (byte === 0) eos = true; 404 | 405 | if (!eos) string += String.fromCharCode(byte); 406 | } 407 | 408 | return string; 409 | }; 410 | 411 | unpacker.cstring = function (pointer, buffer, sz) { 412 | var string = ''; 413 | 414 | // first byte of null-term string 415 | var byte = buffer[pointer.offset]; 416 | 417 | while (byte > 0) { 418 | string += String.fromCharCode(byte); 419 | byte = buffer[++pointer.offset]; 420 | } 421 | 422 | pointer.offset++; 423 | 424 | return string; 425 | }; 426 | 427 | unpacker.boolean = function (pointer, buffer) { 428 | return buffer[pointer.offset++] == 0 ? false : true; 429 | }; 430 | 431 | unpacker.nibble = function (pointer, buffer) { 432 | throw new Error('Not Yet Impelemented'); 433 | }; 434 | 435 | unpacker.uint8l = function (pointer, buffer) { 436 | return buffer[pointer.offset++]; 437 | }; 438 | unpacker.uint8b = function (pointer, buffer) { 439 | return buffer[pointer.offset++]; 440 | }; 441 | 442 | unpacker.uint16l = function (pointer, buffer) { 443 | var binary = buffer[pointer.offset++]; 444 | binary |= buffer[pointer.offset++] << 8; 445 | binary >>>= 0; 446 | return binary; 447 | }; 448 | unpacker.uint16b = function (pointer, buffer) { 449 | var binary = buffer[pointer.offset++] << 8; 450 | binary |= buffer[pointer.offset++]; 451 | binary >>>= 0; 452 | return binary; 453 | }; 454 | 455 | unpacker.uint24l = function (pointer, buffer) { 456 | var binary = buffer[pointer.offset++]; 457 | binary |= buffer[pointer.offset++] << 8; 458 | binary |= buffer[pointer.offset++] << 16; 459 | binary >>>= 0; 460 | return binary; 461 | }; 462 | unpacker.uint24b = function (pointer, buffer) { 463 | var binary = buffer[pointer.offset++] << 16; 464 | binary |= buffer[pointer.offset++] << 8; 465 | binary |= buffer[pointer.offset++]; 466 | binary >>>= 0; 467 | return binary; 468 | }; 469 | 470 | unpacker.uint32l = function (pointer, buffer) { 471 | var binary = buffer[pointer.offset++]; 472 | binary |= buffer[pointer.offset++] << 8; 473 | binary |= buffer[pointer.offset++] << 16; 474 | binary |= buffer[pointer.offset++] << 24; 475 | binary >>>= 0; 476 | return binary; 477 | }; 478 | unpacker.uint32b = function (pointer, buffer) { 479 | var binary = buffer[pointer.offset++] << 24; 480 | binary |= buffer[pointer.offset++] << 16; 481 | binary |= buffer[pointer.offset++] << 8; 482 | binary |= buffer[pointer.offset++]; 483 | binary >>>= 0; 484 | return binary; 485 | }; 486 | 487 | unpacker.uint40l = function (pointer, buffer) { 488 | var binary = buffer[pointer.offset++]; 489 | binary |= buffer[pointer.offset++] << 8; 490 | binary |= buffer[pointer.offset++] << 16; 491 | binary |= buffer[pointer.offset++] << 24; 492 | binary >>>= 0; 493 | binary += buffer[pointer.offset++] * 0x100000000; 494 | return binary; 495 | }; 496 | unpacker.uint40b = function (pointer, buffer) { 497 | var binary = buffer[pointer.offset++] * 0x100000000; 498 | binary >>>= 0; 499 | binary |= buffer[pointer.offset++] << 24; 500 | binary |= buffer[pointer.offset++] << 16; 501 | binary |= buffer[pointer.offset++] << 8; 502 | binary |= buffer[pointer.offset++]; 503 | return binary; 504 | }; 505 | 506 | unpacker.uint48l = function (pointer, buffer) { 507 | var binary = buffer[pointer.offset++]; 508 | binary |= buffer[pointer.offset++] << 8; 509 | binary |= buffer[pointer.offset++] << 16; 510 | binary |= buffer[pointer.offset++] << 24; 511 | binary >>>= 0; 512 | binary += buffer[pointer.offset++] * 0x100000000; 513 | binary += buffer[pointer.offset++] * 0x10000000000; 514 | return binary; 515 | }; 516 | unpacker.uint48b = function (pointer, buffer) { 517 | var binary = buffer[pointer.offset++] * 0x10000000000; 518 | binary += buffer[pointer.offset++] * 0x100000000; 519 | binary >>>= 0; 520 | binary |= buffer[pointer.offset++] < 24; 521 | binary |= buffer[pointer.offset++] << 16; 522 | binary |= buffer[pointer.offset++] << 8; 523 | binary |= buffer[pointer.offset++]; 524 | return binary; 525 | }; 526 | 527 | unpacker.uint56l = function (pointer, buffer) { 528 | var binary = buffer[pointer.offset++]; 529 | binary |= buffer[pointer.offset++] << 8; 530 | binary |= buffer[pointer.offset++] << 16; 531 | binary |= buffer[pointer.offset++] << 24; 532 | binary >>>= 0; 533 | binary += buffer[pointer.offset++] * 0x100000000; 534 | binary += buffer[pointer.offset++] * 0x10000000000; 535 | binary += buffer[pointer.offset++] * 0x1000000000000; 536 | return binary; 537 | }; 538 | unpacker.uint56b = function (pointer, buffer) { 539 | var binary = buffer[pointer.offset++] * 0x1000000000000; 540 | binary += buffer[pointer.offset++] * 0x10000000000; 541 | binary += buffer[pointer.offset++] * 0x100000000; 542 | binary >>>= 0; 543 | binary |= buffer[pointer.offset++] < 24; 544 | binary |= buffer[pointer.offset++] << 16; 545 | binary |= buffer[pointer.offset++] << 8; 546 | binary |= buffer[pointer.offset++]; 547 | return binary; 548 | }; 549 | 550 | unpacker.uint64l = function (pointer, buffer) { 551 | var binary = buffer[pointer.offset++]; 552 | binary |= buffer[pointer.offset++] << 8; 553 | binary |= buffer[pointer.offset++] << 16; 554 | binary |= buffer[pointer.offset++] << 24; 555 | binary >>>= 0; 556 | binary += buffer[pointer.offset++] * 0x100000000; 557 | binary += buffer[pointer.offset++] * 0x10000000000; 558 | binary += buffer[pointer.offset++] * 0x1000000000000; 559 | binary += buffer[pointer.offset++] * 0x100000000000000; 560 | return binary; 561 | }; 562 | unpacker.uint64b = function (pointer, buffer) { 563 | var binary = buffer[pointer.offset++] * 0x100000000000000; 564 | binary += buffer[pointer.offset++] * 0x1000000000000; 565 | binary += buffer[pointer.offset++] * 0x10000000000; 566 | binary += buffer[pointer.offset++] * 0x100000000; 567 | binary >>>= 0; 568 | binary |= buffer[pointer.offset++] < 24; 569 | binary |= buffer[pointer.offset++] << 16; 570 | binary |= buffer[pointer.offset++] << 8; 571 | binary |= buffer[pointer.offset++]; 572 | return binary; 573 | }; 574 | 575 | cstruct.packers = packer; 576 | cstruct.unpackers = unpacker; 577 | --------------------------------------------------------------------------------