├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── Readme.md ├── cppMsg.js ├── package-lock.json ├── package.json ├── test.js └── test2.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | node_modules 50 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\test.js" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 shudingbo 4 | Copyright (c) 2017 darnold79 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Binary data structure transformation for JavaScript 2 | 3 | ## Installation 4 | 5 | Using npm: 6 | 7 | $ npm install cppmsg 8 | 9 | To run the tests: 10 | 11 | $ node test.js 12 | 13 | ## Description 14 | You can use this module, parse binary data from the c + +, can also be generate binary data(from json object) that c/c++ can phrase. 15 | This module provides follow function: 16 | - Encode json data to binary data 17 | - Decode binary data to json 18 | - support more data type: int8/16/32/64, uint8/16/32, float,double,bool,string 19 | - support msg nested; 20 | 21 | You can phrase C++ binary data sturct from network to json. 22 | Note: c/c++ data struct must one bit algin. 23 | 24 | ## cppMsg.msg constructor overloads 25 | - `new cppMsg.msg()` create empty cppMsg; 26 | - `new cppMsg.msg( ds )` ds is data struct define Array 27 | - `new cppMsg.msg( ds, data)` ds is data struct define Array. data(optional) is init json data. 28 | - `new cppMsg.msg( ds, null, opts)` ds is data struct define Array. data(optional) is init json data. 29 | * opts, {useIconv: true } ,useIconv `boolean`, maybe use iconv-lite convert code 30 | 31 | ## cppMsg.msg methods 32 | - **encodeMsg( data )** : json data object; 33 | - **encodeMsg2( data )** : json data object( use msg internal buffer, return internal buffer ); 34 | - **encodeMsgToBuff (data, buff, offset?)** : encode to buffer 35 | * **data**, object; 36 | * **buff**, target buffer; 37 | * **offset**, data offset, default `0`; 38 | - **decodeMsg( buf, offset? )** : decode Buffer to json data object; 39 | * **offset**, data offset, default `0`; 40 | 41 | next methods using stream mode: 42 | - push_uint8 43 | - push_int8 44 | - push_uint16 45 | - push_int16 46 | - push_uint32 47 | - push_int32 48 | - push_string 49 | - push_char 50 | - encode( data ) : data is json data stream. 51 | 52 | ## Examples 53 | 54 | ### Normal Mode 55 | Assume this for all examples below 56 | 57 | C++ Code: 58 | ```c++ 59 | //C++ struct define Must one byte algin 60 | struct head{ 61 | int mainType; 62 | int subType; 63 | }; 64 | 65 | struct msg{ 66 | int reg; 67 | int chkCode; 68 | int iType; 69 | bool bMonitor; 70 | char workPath[10]; 71 | unsigned int processID; 72 | struct head testObj; 73 | long long testin64; 74 | float floatArray3[3]; 75 | }; 76 | ``` 77 | Nodejs code: 78 | ```js 79 | var cppMsg = require('./cppMsg.js'); 80 | 81 | var msg_def = { 82 | msgHead:[ 83 | ['mainType','int32'], 84 | ['subType', 'int32'] 85 | ] 86 | }; 87 | 88 | 89 | var msg = new cppMsg.msg( 90 | [ 91 | ['reg','int32'], 92 | ['chkCode','int32'], 93 | ['iType','int32'], 94 | ['bMonitor', 'bool'], 95 | ['workPath','string',10], 96 | ['processID','uint32'], 97 | ['testObj','object', msg_def.msgHead], // nested other 98 | ['testint64','int64'], 99 | ['floatArray3', 'float', , , 3] 100 | ],null, {useIconv: false} 101 | ); 102 | 103 | var buff = msg.encodeMsg2( { 104 | reg : 2, 105 | chkCode : 0, 106 | iType : 2, 107 | bMonitor : false, 108 | workPath : 'no 你 work', 109 | processID : 1234, 110 | testObj :{ 111 | mainType : 0x01020304, 112 | subType : 0x0A0B0C0D 113 | }, 114 | testint64 : 0xCDEF, 115 | floatArray3: [1.1, 2.2, 9.7] 116 | } ); 117 | 118 | console.log( buff ); 119 | 120 | var data = msg.decodeMsg( buff ); 121 | console.log( data ); 122 | ``` 123 | 124 | ### stream mode 125 | ```js 126 | msg.push_int32(2); // reg 127 | msg.push_int32(0); // chkCode 128 | msg.push_int32(2); // iType 129 | 130 | msg.push_uint8(0); // bMonitor 131 | msg.push_string('no worker path',10); 132 | msg.push_string('no worker path',20); 133 | msg.push_string('brnn-20',20); 134 | msg.push_uint32( 1234 ); // processID 135 | 136 | console.log( msg.encode()); 137 | ``` 138 | 139 | ## contributors 140 | 1. [FreeBugs](https://github.com/FreeBugs) 141 | 1. [kirtanshetty](https://github.com/kirtanshetty) 142 | 143 | ## Changelog 144 | ### 1.2.1 145 | 1. Fixed encodeMsg2 using internal cache causing string dirty; 146 | 1. optimize performance encodeMsg; 147 | 1. add `decodeMsgFromBuff` 148 | 149 | ### 1.2.0 150 | 1. add method `encodeMsg2`, Improve performance 1x 151 | 1. optimize performance encodeMsg 152 | 1. optimize performance decodeMsg, Improve performance 1x 153 | 1. change msg construct add params `opts` 154 | * opts, `{useIconv: true }` 155 | * useIconv, `boolean`, true(default): use iconv-lite convert code.( false, __Improve performance__) 156 | 1. Default string code,change to `utf8` 157 | 158 | ### 1.1.0 159 | 1. Using ES6 syntax 160 | 2. optimize performance encodeMsg 161 | 162 | ### 1.0.3 163 | 1. fix int64 decode/encode error( Works only for numbers <= Number.MAX_SAFE_INTEGER ). 164 | 2. fix object decode error. 165 | ### 1.0.2 166 | 1. merge darnold79 change,add array support. 167 | 168 | ### 1.0.1 169 | 1. string type add encode support(using iconv-lite). 170 | 171 | ### 1.0.0 172 | 1. init. 173 | 174 | 175 | ## LICENSE 176 | 177 | The MIT License (MIT) 178 | 179 | Copyright (c) 2017 Shudingbo 180 | Copyright (c) 2017 darnold79 (node-cppMsg-dynamic) 181 | -------------------------------------------------------------------------------- /cppMsg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by shudingbo on 2/25/16. 3 | * This module using encode/decode c/c++ Binary data structure data struct as json; 4 | * support message nested. 5 | * support data type: 6 | int8 7 | uint8 8 | int16 9 | uint16 10 | int32 11 | uint32 12 | int64 13 | float 14 | double 15 | bool 16 | string 17 | object 18 | 19 | * Modified by darnold79 on 2/14/17 20 | * Added support for arrays of any supported data type. 21 | 22 | * Modified by darnold79 on 2/28/17 23 | * Added support for dynamic array length (if specified as last element in struct). 24 | */ 25 | 26 | 27 | const iconv = require('iconv-lite'); 28 | 29 | 30 | ///////////// cppString defined 31 | class CppString { 32 | constructor( str, len ){ 33 | this.str = ""; 34 | if (str.length > len - 1) { 35 | this.str = str.slice(0, len - 1); 36 | } else { 37 | this.str = str; 38 | } 39 | this.str += "\0"; 40 | 41 | this.byteLen = len; 42 | this.buffer = Buffer.allocUnsafe(this.byteLen); 43 | this.length = this.buffer.length; 44 | 45 | this.process(); 46 | } 47 | 48 | toString () { 49 | return this.buffer.toString(); 50 | } 51 | 52 | 53 | getLength () { 54 | return this.buffer.length; 55 | } 56 | 57 | process () { 58 | this.buffer.fill(0); 59 | this.buffer.write(this.str); 60 | /*for(let i=this.str.length;i < this.buffer.length;i++){ 61 | this.buffer[i] = 0x00; 62 | }*/ 63 | } 64 | } 65 | 66 | 67 | class CppNum { 68 | constructor(num, intType) { 69 | this.num = num; 70 | 71 | this.buffer = null; 72 | this.value = null; 73 | this.numArray = null; 74 | this.byteLen = null; 75 | this.intType = intType; 76 | 77 | switch (this.intType) { 78 | case DataType.uint8: 79 | this.numArray = new Uint8Array([num]); 80 | this.byteLen = 1; 81 | break; 82 | case DataType.uint16: 83 | this.numArray = new Uint16Array([num]); 84 | this.byteLen = 2; 85 | break; 86 | case DataType.uint32: 87 | this.numArray = new Uint32Array([num]); 88 | this.byteLen = 4; 89 | break; 90 | case DataType.int8: 91 | this.numArray = new Int8Array([num]); 92 | this.byteLen = 1; 93 | break; 94 | case DataType.int16: 95 | this.numArray = new Int16Array([num]); 96 | this.byteLen = 2; 97 | break; 98 | case DataType.int32: 99 | this.numArray = new Int32Array([num]); 100 | this.byteLen = 4; 101 | break; 102 | default: 103 | break; 104 | } 105 | this.process(); 106 | } 107 | 108 | 109 | process () { 110 | this.value = this.numArray[0]; 111 | this.buffer = Buffer.allocUnsafe(this.byteLen); 112 | 113 | switch (this.intType) { 114 | case DataType.uint8://uint8 115 | this.buffer.writeUInt8(this.value, 0);//little endian (按小端对齐) 116 | break; 117 | case DataType.uint16://uint16 118 | this.buffer.writeUInt16LE(this.value, 0);//little endian (按小端对齐) 119 | break; 120 | case DataType.uint32://uint32 121 | this.buffer.writeUInt32LE(this.value, 0);//little endian (按小端对齐) 122 | break; 123 | case DataType.int8://int8 124 | this.buffer.writeInt8(this.value, 0);//little endian (按小端对齐) 125 | break; 126 | case DataType.int16://int16 127 | this.buffer.writeInt16LE(this.value, 0);//little endian (按小端对齐) 128 | break; 129 | case DataType.int32://int32 130 | this.buffer.writeInt32LE(this.value, 0);//little endian (按小端对齐) 131 | break; 132 | default: 133 | break; 134 | } 135 | } 136 | } 137 | 138 | 139 | 140 | 141 | function isObject(obj) { 142 | return (Object.prototype.toString.call(obj) === '[object Object]'); 143 | } 144 | 145 | 146 | // 基本数据类型定义 147 | const DataType = { 148 | int8: 0, 149 | uint8: 1, 150 | int16: 2, 151 | uint16: 3, 152 | int32: 4, 153 | uint32: 5, 154 | int64: 6, 155 | float: 7, 156 | double: 8, 157 | bool: 9, 158 | string: 10, 159 | object: 11 160 | }; 161 | 162 | const DataTypeLen = [1, 1, 2, 2, 4, 4, 8, 4, 8, 1, 0]; 163 | 164 | class msg { 165 | 166 | /** 167 | ds = [{:[,[len],[arraylen]]}] 168 | [ 169 | [ 'reg','int32'], 170 | [ 'workPath','string',250 }, 171 | [ 'someArray','uint32',,16 }, 172 | ] 173 | 174 | */ 175 | constructor (ds, data ,opts) { 176 | this.listBuffer = []; // 数据 Buffer 177 | this.length = 0; // 已放入Buffer的数据长度 178 | 179 | this.dsEncode = {}; // 编码使用结构 { name:[,,[len]] } 180 | this.dsDecode = []; // 解码使用的结构 [,,,] 181 | this.dsLen = 0; 182 | 183 | let defOpt = { useIconv: true }; 184 | 185 | this.opts = isObject(opts) ? opts : defOpt; 186 | if( this.opts.useIconv === undefined ){ 187 | this.opts.useIconv = true; 188 | } 189 | 190 | const ret = this.phraseDS(ds); 191 | if (ret !== false) { 192 | this.dsLen = ret[0]; 193 | this.dsEncode = ret[1]; 194 | this.dsDecode = ret[2]; 195 | } 196 | 197 | this.encodeBuf = Buffer.allocUnsafe(this.dsLen); 198 | 199 | if (isObject(data)) { 200 | this.encodeMsg(data); 201 | } 202 | } 203 | 204 | 205 | phraseDS (ds) { 206 | if (Array.isArray(ds)) { 207 | let len = ds.length; 208 | let offset = 0; 209 | let dataType = DataType.int8; 210 | let dataLen = 1; 211 | let arrayLen = 1; 212 | 213 | let dsLen = 0; 214 | let dsEncode = {}; // 编码使用结构 { name:[,,[len]] } 215 | let dsDecode = []; // 解码使用的结构 [,,,] 216 | 217 | for (let i = 0; i < len; i++) { 218 | let it = ds[i]; 219 | 220 | if (Array.isArray(it) && it.length >= 2) { 221 | dataType = DataType[ it[1] ]; 222 | if( dataType === undefined ) { 223 | dataType = -1; 224 | } 225 | let enAddin = null; 226 | let deAddin = null; 227 | if (dataType === -1) { 228 | throw Error(' cppType.msg ds phrase error '); 229 | } else { 230 | if (dataType === DataType.string) { // 字符串 231 | if (it.length < 3) { 232 | throw Error(' cppType.msg ds phrase error: [string] '); 233 | } 234 | dataLen = parseInt(it[2]); 235 | 236 | if (it.length > 3 && it[3] != undefined) { 237 | deAddin = it[3]; 238 | enAddin = it[3]; 239 | } else { 240 | enAddin = 'utf8'; 241 | deAddin = 'utf8'; 242 | } 243 | } else if (dataType === DataType.object) { // 对象 244 | dataLen = -1; 245 | let ret = this.phraseDS(it[2]); 246 | if (ret !== false) { 247 | //console.log('ret-------- testObj', ret ); 248 | dataLen = ret[0]; 249 | enAddin = ret[1]; 250 | deAddin = ret[2]; 251 | } 252 | } 253 | else { 254 | dataLen = DataTypeLen[dataType]; 255 | } 256 | if (it.length > 4) { 257 | arrayLen = parseInt(it[4]); 258 | } else { 259 | arrayLen = 1; 260 | } 261 | } 262 | 263 | 264 | dsEncode[it[0]] = [dataType, offset, dataLen, enAddin, arrayLen]; 265 | dsDecode.push([offset, dataLen, dataType, it[0], deAddin, arrayLen]); 266 | 267 | offset += dataLen * arrayLen; 268 | dsLen += dataLen * arrayLen; 269 | } else { 270 | throw Error('data struct parseError!'); 271 | } 272 | } 273 | 274 | return [dsLen, dsEncode, dsDecode, arrayLen]; 275 | } 276 | else { 277 | return false; 278 | } 279 | } 280 | 281 | 282 | push_uint8 (value) { 283 | let uint8Value = new CppNum(value, DataType.uint8); 284 | this.listBuffer.push(uint8Value.buffer); 285 | this.length += uint8Value.byteLen; 286 | } 287 | 288 | push_int8 (value) { 289 | let int8Value = new CppNum(value, DataType.int8); 290 | this.listBuffer.push(int8Value.buffer); 291 | this.length += int8Value.byteLen; 292 | } 293 | 294 | push_uint16 (value) { 295 | let uint16Value = new CppNum(value, DataType.uint16); 296 | this.listBuffer.push(uint16Value.buffer); 297 | this.length += uint16Value.byteLen; 298 | } 299 | 300 | push_int16 (value) { 301 | let int16Value = new CppNum(value, DataType.int16); 302 | this.listBuffer.push(int16Value.buffer); 303 | this.length += int16Value.byteLen; 304 | } 305 | 306 | push_uint32 (value) { 307 | let uint32Value = new CppNum(value, DataType.uint32); 308 | this.listBuffer.push(uint32Value.buffer); 309 | this.length += uint32Value.byteLen; 310 | } 311 | 312 | push_int32 (value) { 313 | let int32Value = new CppNum(value, DataType.int32); 314 | this.listBuffer.push(int32Value.buffer); 315 | this.length += int32Value.byteLen; 316 | } 317 | 318 | push_string (strValue, len) { 319 | let strValue1 = new CppString(strValue, len); 320 | this.listBuffer.push(strValue1.buffer); 321 | this.length += strValue1.byteLen; 322 | }; 323 | 324 | push_char (strChar) { 325 | let strValue = new CppString(strValue, 2); 326 | this.listBuffer.push(strValue.buffer); 327 | this.length += strValue.byteLen; 328 | }; 329 | 330 | encode (data) { 331 | if (isObject(data)) { 332 | let msgBuf = this.encodeMsg(data); 333 | this.listBuffer.push(msgBuf); 334 | this.length += this.listBuffer.length; 335 | } 336 | 337 | if (this.listBuffer.length > 0) { 338 | return Buffer.concat(this.listBuffer); 339 | } 340 | 341 | return false; 342 | } 343 | 344 | 345 | /** decode message as object 346 | * @param {buffer} buf data buffer 347 | * @param {number} offset the data buffer offset 348 | * @return {object} the data object 349 | */ 350 | decodeMsg (buf,offset) { 351 | let off = (offset)? offset : 0; 352 | return decodeObject(buf, off, this.dsDecode, this.opts); 353 | } 354 | 355 | 356 | /** encode message as Buffer 357 | * @param {object} data the encode object 358 | * @return {buffer} The Buffer ( new Buffer ) 359 | */ 360 | encodeMsg (data) { 361 | return encodeObject(data, this.dsLen, this.dsEncode, null, 0, this.opts); 362 | } 363 | 364 | /** encode message use internal buffer 365 | * @param {object} data the encode object 366 | * @return {buffer} The internal Buffer 367 | */ 368 | encodeMsg2 (data) { 369 | return encodeObject(data, this.dsLen, this.dsEncode, this.encodeBuf, 0, this.opts); 370 | } 371 | 372 | /** encode message to Buffer 373 | * @param {object} data the encode object 374 | * @param {buffer} buff the encode buffer 375 | * @param {number} offset the encode buffer offset 376 | * @return {buffer} The internal Buffer 377 | */ 378 | encodeMsgToBuff (data, buff, offset) { 379 | return encodeObject(data, this.dsLen, this.dsEncode, buff, offset, this.opts); 380 | } 381 | } 382 | 383 | 384 | /** encode msg( new Buffer) 385 | * 386 | * @param {buffer} buf the Buffer 387 | * @param {number} offset the Buffer offset 388 | * @param {object} dsEncode encode struct 389 | * @param {{useIconv:boolean}?} opts 390 | * 391 | * @return {object} the decode object 392 | */ 393 | function decodeObject(buf, offset, dsDecode,opts) { 394 | let data = {}; 395 | // [,,,] 396 | for (let i = 0; i < dsDecode.length; i++) { 397 | let info = dsDecode[i]; 398 | let off = info[0] + offset; 399 | let key = info[3]; 400 | let arrayLen = info[5]; 401 | let values = []; 402 | for (let arri = 0; arri < arrayLen; arri++) { 403 | if(off >= buf.length) continue; 404 | switch (info[2]) { 405 | case DataType.int8: 406 | values.push(buf.readInt8(off)); 407 | break; 408 | case DataType.int16: 409 | values.push(buf.readInt16LE(off)); 410 | break; 411 | case DataType.int32: 412 | values.push(buf.readInt32LE(off)); 413 | break; 414 | case DataType.int64: 415 | let high = buf.readUInt32LE(off); 416 | let low = buf.readUInt32LE(off + 4); 417 | values.push(low * 0x100000000 + high); 418 | break; 419 | case DataType.uint8: 420 | values.push(buf.readUInt8(off)); 421 | break; 422 | case DataType.uint16: 423 | values.push(buf.readUInt16LE(off)); 424 | break; 425 | case DataType.uint32: 426 | values.push(buf.readUInt32LE(off)); 427 | break; 428 | case DataType.float: 429 | values.push(buf.readFloatLE(off)); 430 | break; 431 | case DataType.double: 432 | values.push(buf.readDoubleLE(off)); 433 | break; 434 | case DataType.bool: 435 | values.push(buf.readUInt8(off) !== 0); 436 | break; 437 | case DataType.string: { 438 | if( opts.useIconv === true ){ 439 | let val = iconv.decode(buf.slice(off, off + info[1] - 1), info[4]); 440 | values.push(val.replace(/\0[\s\S]*/g, '')); 441 | } else { 442 | let val = buf.slice(off, off + info[1] - 1).toString(); 443 | values.push(val.replace(/\0[\s\S]*/g, '')); 444 | } 445 | } break; 446 | case DataType.object: 447 | values.push(decodeObject(buf, off, info[4],opts)); 448 | break; 449 | } 450 | off += info[1]; 451 | } 452 | data[key] = arrayLen <= 1 ? values[0] : values; 453 | } 454 | 455 | return data; 456 | } 457 | 458 | /** encode msg( new Buffer) 459 | * 460 | * @param {object} data the encode object 461 | * @param {number} dsLen the Buffer len 462 | * @param {object} dsEncode encode struct 463 | * @param {{useIconv:boolean}?} opt 464 | * 465 | * @return {Buffer} 466 | */ 467 | function encodeObject(data, dsLen, dsEncode, _buff, _offset, opt) { 468 | let keyInfo = null; 469 | let msgBuf = ( _buff ) ? _buff : Buffer.allocUnsafe(dsLen); 470 | let _off = ( _offset ) ? _offset : 0; 471 | 472 | for (let p in data) { 473 | keyInfo = dsEncode[p]; // { name:[,,[len],[arraylen]] } 474 | if (keyInfo === undefined) { 475 | continue; 476 | } 477 | let out = Array.isArray(data[p]) ? data[p] : [data[p]]; 478 | let off = _off + keyInfo[1]; 479 | 480 | for(let idx=0; idx< out.length;idx++) 481 | { 482 | let x = out[idx]; 483 | switch (keyInfo[0]) { 484 | case DataType.int8: 485 | msgBuf.writeInt8(x, off); 486 | break; 487 | case DataType.int16: 488 | msgBuf.writeInt16LE(x, off); 489 | break; 490 | case DataType.int32: 491 | msgBuf.writeInt32LE(x, off); 492 | break; 493 | case DataType.int64: 494 | let high = ~~(x / 0xFFFFFFFF); 495 | let low = (x % 0xFFFFFFFF) - high; 496 | 497 | msgBuf.writeUInt32LE(low, off); 498 | msgBuf.writeUInt32LE(high, (off+4)); 499 | break; 500 | case DataType.uint8: 501 | msgBuf.writeUInt8(x, off); 502 | break; 503 | case DataType.uint16: 504 | msgBuf.writeUInt16LE(x, off); 505 | break; 506 | case DataType.uint32: 507 | msgBuf.writeUInt32LE(x, off); 508 | break; 509 | case DataType.float: 510 | msgBuf.writeFloatLE(x, off); 511 | break; 512 | case DataType.double: 513 | msgBuf.writeDoubleLE(x, off); 514 | break; 515 | case DataType.bool: 516 | msgBuf.writeUInt8(x ? 1 : 0, off); 517 | break; 518 | case DataType.string: 519 | let bufT = null; 520 | if( opt.useIconv === true ) { 521 | bufT = iconv.encode((x.length > keyInfo[2] - 1)?x.slice(0, keyInfo[2] - 1):x, keyInfo[3]); 522 | } else { 523 | bufT = Buffer.from( (x.length > keyInfo[2] - 1)?x.slice(0, keyInfo[2] - 1):x ); 524 | } 525 | bufT.copy(msgBuf, off); 526 | let fillBufLen = keyInfo[2] - bufT.length; 527 | if( fillBufLen <= 20 ) { 528 | let offT = off + bufT.length; 529 | for( let i=0;i< fillBufLen; i++){ 530 | msgBuf[ offT++ ] = 0; 531 | } 532 | } else { 533 | msgBuf.fill(0,off + bufT.length,off + keyInfo[2] ); 534 | } 535 | break; 536 | case DataType.object: 537 | encodeObject(x, keyInfo[2], keyInfo[3], msgBuf, off, opt); 538 | break; 539 | } 540 | off += keyInfo[2]; 541 | } 542 | } 543 | 544 | return msgBuf; 545 | } 546 | 547 | 548 | module.exports = { msg, DataType }; 549 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cppmsg", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "iconv-lite": { 8 | "version": "0.6.2", 9 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", 10 | "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", 11 | "requires": { 12 | "safer-buffer": ">= 2.1.2 < 3.0.0" 13 | } 14 | }, 15 | "safer-buffer": { 16 | "version": "2.1.2", 17 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 18 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cppmsg", 3 | "version": "1.2.1", 4 | "description": "C++ stuct encoding/decoding with support for (dynamic) arrays.", 5 | "main": "cppMsg.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "binary", 11 | "data", 12 | "struct" 13 | ], 14 | "author": { 15 | "name": "shudingbo", 16 | "email": "shudingbo@qq.com" 17 | }, 18 | "contributors": [ 19 | { 20 | "name": "darnold79", 21 | "email": "npm@darnold.org" 22 | }, 23 | { 24 | "name": "kirtanshetty", 25 | "email": "kirtankshetty@gmail.com" 26 | } 27 | ], 28 | "license": "MIT", 29 | "dependencies": { 30 | "iconv-lite": "^0.6.2" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/shudingbo/node-cppMsg.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/shudingbo/issues" 38 | }, 39 | "engines": { 40 | "node": ">=8.9.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by sdb on 2/25/16. 3 | */ 4 | 5 | const cppMsg = require('./cppMsg.js'); 6 | let testCnt = 100000; 7 | let testEncode = true; 8 | let testDecode = true; 9 | 10 | console.log('start Test...'); 11 | 12 | let msg_def = { 13 | msgHead: [ 14 | ['mainType', 'int32'], 15 | ['subType', 'int32'] 16 | ] 17 | }; 18 | 19 | let sTime = Date.now(); 20 | let msgTest = new cppMsg.msg( 21 | [ 22 | ['reg', 'int32'], 23 | ['chkCode', 'int32'], 24 | ['iType', 'int32'], 25 | ['bMonitor', 'bool'], 26 | ['workPath', 'string', 32], 27 | ['processID', 'uint32'], 28 | ['testObj', 'object', msg_def.msgHead], 29 | ['testint64', 'int64'], 30 | // ['floatArray3', 'float', , , 3], 31 | ['alert', 'string', 10, 'gb2312', 2] 32 | ], null, {useIconv: false} 33 | ); 34 | 35 | 36 | console.log(`parse Test: consume time ${Date.now() - sTime} ms`); 37 | 38 | let testObj = { 39 | reg: testCnt, 40 | chkCode: 0, 41 | iType: 2, 42 | bMonitor: false, 43 | workPath: 'no 你好 work', 44 | processID: 1234, 45 | 46 | testObj: { 47 | mainType: 0x01020304, 48 | subType: 0x0A0B0C0D 49 | }, 50 | 51 | testint64: 8888321499136, 52 | //floatArray3: [1.1, 2.2, 9.7], 53 | alert: ['quick fox', 'lazy dog'] 54 | }; 55 | 56 | let buffTest = msgTest.encodeMsg( testObj ); 57 | let dataTestOut = msgTest.decodeMsg(buffTest); 58 | if(JSON.stringify(testObj) === JSON.stringify(dataTestOut)){ 59 | console.log('encode is equ? ', true); 60 | } else { 61 | console.log('encode not equ', dataTestOut); 62 | } 63 | 64 | if( msgTest.encodeMsg2 ) { 65 | let buffTest = msgTest.encodeMsg2( testObj ); 66 | let dataTestOut = msgTest.decodeMsg(buffTest); 67 | if(JSON.stringify(testObj) === JSON.stringify(dataTestOut)){ 68 | console.log('encode2 is equ? ', true); 69 | } else { 70 | console.log('encode2 not equ', dataTestOut); 71 | } 72 | 73 | } 74 | 75 | let msg = new cppMsg.msg( 76 | [ 77 | ['reg', 'int32'], 78 | ['chkCode', 'int32'], 79 | ['iType', 'int32'], 80 | ['bMonitor', 'bool'], 81 | ['workPath', 'string', 32, 'gb2312'], 82 | ['processID', 'uint32'], 83 | ['testObj', 'object', msg_def.msgHead], 84 | ['testint64', 'int64'], 85 | ['floatArray3', 'float', , , 3], 86 | ['alert', 'string', 10, 'gb2312', 2] 87 | ], 88 | null, {useIconv: false} 89 | ); 90 | 91 | let sz = ['123', '452kjads', 'lkrtq','hhwerqt','quick fox', 'lazy dog'] 92 | 93 | if( testEncode === true ) { 94 | sTime = Date.now(); 95 | let szIdx = 0; 96 | for( let i=0;i= sz.length ) { 117 | szIdx = 0; 118 | } 119 | let buff = msg.encodeMsg({ 120 | reg: testCnt, 121 | chkCode: 0, 122 | iType: 2, 123 | bMonitor: false, 124 | workPath: 'no 你好 work', 125 | processID: Math.round(Math.random() * 10000), 126 | testObj: { 127 | mainType: 0x01020304, 128 | subType: 0x0A0B0C0D 129 | }, 130 | 131 | testint64: 8888321499136, 132 | floatArray3: [1.1, 2.2, 9.7], 133 | alert: ['quick fox', str] 134 | }); 135 | 136 | //let data = msg.decodeMsg(buff); 137 | //console.log( data ); 138 | } 139 | console.log(`encode ${testCnt} Test: consume time ${Date.now() - sTime} ms`); 140 | 141 | if( msg.encodeMsg2 ){ 142 | sTime = Date.now(); 143 | let szIdx = 0; 144 | for( let i=0;i= sz.length ) { 165 | szIdx = 0; 166 | } 167 | let buff = msg.encodeMsg2({ 168 | reg: testCnt, 169 | chkCode: 0, 170 | iType: 2, 171 | bMonitor: false, 172 | workPath: 'no 你好 work', 173 | processID: Math.round(Math.random() * 10000), 174 | 175 | testObj: { 176 | mainType: 0x01020304, 177 | subType: 0x0A0B0C0D 178 | }, 179 | 180 | testint64: 8888321499136, 181 | floatArray3: [1.1, 2.2, 9.7], 182 | alert: ['quick fox', str] 183 | }); 184 | } 185 | console.log(`encode2 ${testCnt} Test: consume time ${Date.now() - sTime} ms`); 186 | } 187 | } 188 | 189 | if( testDecode ){ 190 | 191 | let buff = msg.encodeMsg({ 192 | reg: testCnt, 193 | chkCode: 0, 194 | iType: 2, 195 | bMonitor: false, 196 | workPath: 'no 你好 work', 197 | processID: Math.round(Math.random() * 10000), 198 | testObj: { 199 | mainType: 0x01020304, 200 | subType: 0x0A0B0C0D 201 | }, 202 | 203 | testint64: 8888321499136, 204 | floatArray3: [1.1, 2.2, 9.7], 205 | alert: ['quick fox', 'lazy dog'] 206 | }); 207 | 208 | sTime = Date.now(); 209 | for( let i=0;i