├── .gitignore ├── Makefile ├── README ├── README.md ├── binding.gyp ├── example └── packit.js ├── hipack.js ├── nbproject ├── Package-Default.bash ├── configurations.xml ├── private │ ├── configurations.xml │ ├── private.properties │ └── private.xml ├── project.properties └── project.xml ├── pack.c ├── pack.cc ├── pack.h ├── package.json ├── test ├── speedtest.js └── test.js └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | *.tgz 4 | build 5 | *~ 6 | *.~ 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN = ./node_modules/.bin 2 | MOCHA_OPTS = --timeout 10s --recursive 3 | REPORTER = spec 4 | TEST_FILES = test/acceptance 5 | 6 | all: 7 | #$(MAKE) static 8 | node-gyp configure build 9 | 10 | clean: 11 | rm -rf build 12 | 13 | lint: 14 | $(BIN)/jshint hipack.js test/* 15 | 16 | temp: 17 | rm -rf tmp/hipack 18 | mkdir -p tmp/hipack 19 | cp -r README *.{cc,h,js*} binding.gyp Makefile test tmp/hipack 20 | cd tmp/hipack 21 | 22 | test: 23 | node test/test.js 24 | 25 | install: 26 | @npm install 27 | 28 | package: temp install 29 | cd tmp && tar -czvf hipack.tgz hipack 30 | @npm hipack 31 | 32 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | HiPack 2 | 3 | This is a copy of PHP's pack/unpack function made into a nodejs module. 4 | Its a 100% clone with an additional options. 5 | 6 | * A new feature where we can populate an existing Buffer is now there, so 7 | * the caller can 8 | worry about Buffer management. Simply make the first parameter a buffer 9 | 10 | * Also we can supply an array of values, rather than arguments, so the 2nd 11 | * parameter is an array. 12 | ie. pack( "VV", [ 23,55 ] ); 13 | 14 | * Added a Perl style format that was missing, which is format character "V" 15 | * and "v" 16 | 17 | 18 | 19 | ## Install 20 | 21 | To install HiPack, use [npm](http://github.com/rauls/nodejs-pack) 22 | 23 | $ npm install hipack 24 | 25 | ## Packing Bytes Test 26 | 27 | To test packing bytes, run 28 | $ node test/test.js 29 | $ node test/speedtest.js 30 | $ node example/packit.js CCCC 25 26 27 27 31 | 32 | To use in code, 33 | var pack = require( 'hipack' ).pack; 34 | var buffer = pack( "CCCV", [ 1,2,3, 12345679 ] ); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## HiPack 2 | 3 | This is a copy of PHP's pack/unpack function made into a nodejs module. 4 | Its a 100% clone with an additional options. 5 | 6 | * A new feature where we can populate an existing Buffer is now there, so the caller can 7 | worry about Buffer management. Simply make the first parameter a buffer 8 | 9 | * Also we can supply an array of values, rather than arguments, so the 2nd parameter is an array. 10 | ie. pack( "VV", [ 23,55 ] ); 11 | 12 | * Added a Perl style format that was missing, which is format character "V" and "v" 13 | 14 | 15 | 16 | ## Install 17 | 18 | To install HiPack, use [npm](http://github.com/rauls/nodejs-pack) 19 | 20 | $ npm install hipack 21 | 22 | ## Packing Bytes Test 23 | 24 | To test packing bytes, run 25 | $ node test/test.js 26 | $ node test/speedtest.js 27 | $ node example/packit.js CCCC 25 26 27 27 28 | 29 | To use in code, 30 | var pack = require( 'hipack' ).pack; 31 | var buffer = pack( "CCCV", [ 1,2,3, 12345679 ] ); 32 | 33 | 34 | 35 | ## Original Pack Documentation 36 | 37 | pack - Pack data into binary Buffer object 38 | 39 | ### Description 40 | 41 | Buffer object = pack ( [ DestBuffer ] , string format [, mixed $args [, mixed n... ]] ) 42 | Pack given arguments into a binary Buffer according to format. 43 | 44 | The idea for this function was taken from Perl and all formatting codes work the same as in Perl. However, there are some formatting codes that are missing such as Perl's "u" format code. 45 | 46 | Note that the distinction between signed and unsigned values only affects the function unpack(), where as function pack() gives the same result for signed and unsigned format codes. 47 | 48 | ### Parameters 49 | 50 | ## format 51 | The format string consists of format codes followed by an optional repeater argument. The repeater argument can be either an integer value or * for repeating to the end of the input data. For a, A, h, H the repeat count specifies how many characters of one data argument are taken, for @ it is the absolute position where to put the next data, for everything else the repeat count specifies how many data arguments are consumed and packed into the resulting binary string. 52 | 53 | ## Currently implemented formats are: 54 | 55 | ### pack() format characters 56 | 57 | Code | Description 58 | -----|------------ 59 | a | NUL-padded string 60 | A | SPACE-padded string 61 | h | Hex string, low nibble first 62 | H | Hex string, high nibble first 63 | c | signed char 64 | C | unsigned char 65 | s | signed short (always 16 bit, machine byte order) 66 | S | unsigned short (always 16 bit, machine byte order) 67 | n | unsigned short (always 16 bit, big endian byte order) 68 | v | unsigned short (always 16 bit, little endian byte order) 69 | i | signed integer (machine dependent size and byte order) 70 | I | unsigned integer (machine dependent size and byte order) 71 | l | signed long (always 32 bit, machine byte order) 72 | L | unsigned long (always 32 bit, machine byte order) 73 | N | unsigned long (always 32 bit, big endian byte order) 74 | V | unsigned long (always 32 bit, little endian byte order) 75 | f | float (machine dependent size and representation) 76 | d | double (machine dependent size and representation) 77 | x | NUL byte 78 | X | Back up one byte 79 | Z | NUL-padded string (new in PHP 5.5) 80 | @ | NUL-fill to absolute position 81 | 82 | ## Return Values 83 | 84 | Returns a Buffer containing data. 85 | 86 | 87 | 88 | # unpack 89 | 90 | ## unpack - Unpack data from a Buffer 91 | 92 | ### Description 93 | 94 | Object unpack ( String format , Buffer data, [ Boolean PerlFormat ] ) 95 | Unpacks from a binary string into an array according to the given format. 96 | 97 | The unpacked data is stored in an associative array. To accomplish this you have to name the different format codes and separate them by a slash /. If a repeater argument is present, then each of the array keys will have a sequence number behind the given name. 98 | 99 | Example: "c2chars/nints" will return an object with fields 100 | Object = { chars1: 0, chars2: 0, ints: 0 }. 101 | 102 | ## Parameters 103 | 104 | format 105 | See pack() for an explanation of the format codes. 106 | 107 | data 108 | The packed data. 109 | 110 | PerlFormat 111 | If this parameter exists and is true, we will ignore the PHP style names, and use just the variables index 112 | 113 | Example: "c2n" will return an object with fields 114 | Object = { 1: 0, 2: 0, 3: 0 } 115 | 116 | ## Return Values 117 | 118 | Returns an associative array containing unpacked elements of binary string. 119 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "hipack", 5 | "sources": [ "pack.cc" ] 6 | } 7 | ], 8 | 'cflags': [ 9 | '-Wall', 10 | '-O3' 11 | ] 12 | } -------------------------------------------------------------------------------- /example/packit.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/node 2 | 3 | var pack = require( "hipack" ); 4 | 5 | var str = process.ARGV[2]; 6 | 7 | var pos = 3; 8 | var Params = []; 9 | while( pos < process.ARGC ) 10 | { 11 | Params.push( process.ARGV[ pos ] ); 12 | pos++; 13 | } 14 | 15 | var buf = pack.pack( "VVC", 65,66,67 ); 16 | 17 | console.log( "Buffers are : " ); 18 | console.dir( (buf) ); 19 | 20 | function SimpleTest() 21 | { 22 | console.log("Converting VARs to RAW\nCalling pack.pack( 'VVC', 65,66,67 )" ); 23 | var buf = pack.pack( "VVC", 65,66,67 ); 24 | 25 | console.log( "Buffers are : " ); 26 | console.dir( (buf) ); 27 | 28 | var test = new Buffer(8); 29 | console.dir( test ); 30 | } 31 | 32 | 33 | // Loop overhead is 50ms for 1m items. 34 | // Calling pack.N is a 500ms overhead doing 'nothing' 35 | function SpeedTest() 36 | { 37 | var raw; 38 | 39 | var t1 = new Date(); 40 | var tot = 1e6; 41 | for(i=0;i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | pack.c 12 | pack.cc 13 | pack.h 14 | 15 | 19 | Makefile 20 | 21 | 22 | ^(nbproject)$ 23 | 24 | . 25 | 26 | Makefile 27 | 28 | 29 | 30 | LOCAL_SOURCES 31 | default 32 | 33 | 34 | 35 | . 36 | ${MAKE} -f Makefile 37 | ${MAKE} -f Makefile clean 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /nbproject/private/configurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Makefile 4 | 5 | 6 | 7 | localhost 8 | 2 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | gdb 26 | 27 | 28 | 29 | "${OUTPUT_PATH}" 30 | 31 | "${OUTPUT_PATH}" 32 | 33 | true 34 | 0 35 | 0 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /nbproject/private/private.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rauls/nodejs-pack/d47489237df5dd630d98b6f949b59c8d4e907e08/nbproject/private/private.properties -------------------------------------------------------------------------------- /nbproject/private/private.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0 5 | 0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rauls/nodejs-pack/d47489237df5dd630d98b6f949b59c8d4e907e08/nbproject/project.properties -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.cnd.makeproject 4 | 5 | 6 | packsrc 7 | c 8 | cc 9 | h 10 | UTF-8 11 | 12 | 13 | . 14 | 15 | 16 | 17 | Default 18 | 0 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /pack.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Chris Schneider | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | /* $Id$ */ 19 | 20 | #include "php.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef PHP_WIN32 29 | #define O_RDONLY _O_RDONLY 30 | #include "win32/param.h" 31 | #elif defined(NETWARE) 32 | #ifdef USE_WINSOCK 33 | #include 34 | #else 35 | #include 36 | #endif 37 | #include 38 | #else 39 | #include 40 | #endif 41 | #include "ext/standard/head.h" 42 | #include "php_string.h" 43 | #include "pack.h" 44 | #if HAVE_PWD_H 45 | #ifdef PHP_WIN32 46 | #include "win32/pwd.h" 47 | #else 48 | #include 49 | #endif 50 | #endif 51 | #include "fsock.h" 52 | #if HAVE_NETINET_IN_H 53 | #include 54 | #endif 55 | 56 | #define INC_OUTPUTPOS(a,b) \ 57 | if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \ 58 | efree(argv); \ 59 | efree(formatcodes); \ 60 | efree(formatargs); \ 61 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: integer overflow in format string", code); \ 62 | RETURN_FALSE; \ 63 | } \ 64 | outputpos += (a)*(b); 65 | 66 | /* Whether machine is little endian */ 67 | char machine_little_endian; 68 | 69 | /* Mapping of byte from char (8bit) to long for machine endian */ 70 | static int byte_map[1]; 71 | 72 | /* Mappings of bytes from int (machine dependant) to int for machine endian */ 73 | static int int_map[sizeof(int)]; 74 | 75 | /* Mappings of bytes from shorts (16bit) for all endian environments */ 76 | static int machine_endian_short_map[2]; 77 | static int big_endian_short_map[2]; 78 | static int little_endian_short_map[2]; 79 | 80 | /* Mappings of bytes from longs (32bit) for all endian environments */ 81 | static int machine_endian_long_map[4]; 82 | static int big_endian_long_map[4]; 83 | static int little_endian_long_map[4]; 84 | 85 | /* {{{ php_pack 86 | */ 87 | static void php_pack(zval **val, int size, int *map, char *output) 88 | { 89 | int i; 90 | char *v; 91 | 92 | convert_to_long_ex(val); 93 | v = (char *) &Z_LVAL_PP(val); 94 | 95 | for (i = 0; i < size; i++) { 96 | *output++ = v[map[i]]; 97 | } 98 | } 99 | /* }}} */ 100 | 101 | /* pack() idea stolen from Perl (implemented formats behave the same as there) 102 | * Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @. 103 | */ 104 | /* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]]) 105 | Takes one or more arguments and packs them into a binary string according to the format argument */ 106 | PHP_FUNCTION(pack) 107 | { 108 | zval ***argv = NULL; 109 | int num_args, i; 110 | int currentarg; 111 | char *format; 112 | int formatlen; 113 | char *formatcodes; 114 | int *formatargs; 115 | int formatcount = 0; 116 | int outputpos = 0, outputsize = 0; 117 | char *output; 118 | 119 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &argv, &num_args) == FAILURE) { 120 | return; 121 | } 122 | 123 | if (Z_ISREF_PP(argv[0])) { 124 | SEPARATE_ZVAL(argv[0]); 125 | } 126 | convert_to_string_ex(argv[0]); 127 | 128 | format = Z_STRVAL_PP(argv[0]); 129 | formatlen = Z_STRLEN_PP(argv[0]); 130 | 131 | /* We have a maximum of format codes to deal with */ 132 | formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0); 133 | formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0); 134 | currentarg = 1; 135 | 136 | /* Preprocess format into formatcodes and formatargs */ 137 | for (i = 0; i < formatlen; formatcount++) { 138 | char code = format[i++]; 139 | int arg = 1; 140 | 141 | /* Handle format arguments if any */ 142 | if (i < formatlen) { 143 | char c = format[i]; 144 | 145 | if (c == '*') { 146 | arg = -1; 147 | i++; 148 | } 149 | else if (c >= '0' && c <= '9') { 150 | arg = atoi(&format[i]); 151 | 152 | while (format[i] >= '0' && format[i] <= '9' && i < formatlen) { 153 | i++; 154 | } 155 | } 156 | } 157 | 158 | /* Handle special arg '*' for all codes and check argv overflows */ 159 | switch ((int) code) { 160 | /* Never uses any args */ 161 | case 'x': 162 | case 'X': 163 | case '@': 164 | if (arg < 0) { 165 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: '*' ignored", code); 166 | arg = 1; 167 | } 168 | break; 169 | 170 | /* Always uses one arg */ 171 | case 'a': 172 | case 'A': 173 | case 'h': 174 | case 'H': 175 | if (currentarg >= num_args) { 176 | efree(argv); 177 | efree(formatcodes); 178 | efree(formatargs); 179 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough arguments", code); 180 | RETURN_FALSE; 181 | } 182 | 183 | if (arg < 0) { 184 | if (Z_ISREF_PP(argv[currentarg])) { 185 | SEPARATE_ZVAL(argv[currentarg]); 186 | } 187 | convert_to_string_ex(argv[currentarg]); 188 | arg = Z_STRLEN_PP(argv[currentarg]); 189 | } 190 | 191 | currentarg++; 192 | break; 193 | 194 | /* Use as many args as specified */ 195 | case 'c': 196 | case 'C': 197 | case 's': 198 | case 'S': 199 | case 'i': 200 | case 'I': 201 | case 'l': 202 | case 'L': 203 | case 'n': 204 | case 'N': 205 | case 'v': 206 | case 'V': 207 | case 'f': 208 | case 'd': 209 | if (arg < 0) { 210 | arg = num_args - currentarg; 211 | } 212 | 213 | currentarg += arg; 214 | 215 | if (currentarg > num_args) { 216 | efree(argv); 217 | efree(formatcodes); 218 | efree(formatargs); 219 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: too few arguments", code); 220 | RETURN_FALSE; 221 | } 222 | break; 223 | 224 | default: 225 | efree(argv); 226 | efree(formatcodes); 227 | efree(formatargs); 228 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: unknown format code", code); 229 | RETURN_FALSE; 230 | } 231 | 232 | formatcodes[formatcount] = code; 233 | formatargs[formatcount] = arg; 234 | } 235 | 236 | if (currentarg < num_args) { 237 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d arguments unused", (num_args - currentarg)); 238 | } 239 | 240 | /* Calculate output length and upper bound while processing*/ 241 | for (i = 0; i < formatcount; i++) { 242 | int code = (int) formatcodes[i]; 243 | int arg = formatargs[i]; 244 | 245 | switch ((int) code) { 246 | case 'h': 247 | case 'H': 248 | INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */ 249 | break; 250 | 251 | case 'a': 252 | case 'A': 253 | case 'c': 254 | case 'C': 255 | case 'x': 256 | INC_OUTPUTPOS(arg,1) /* 8 bit per arg */ 257 | break; 258 | 259 | case 's': 260 | case 'S': 261 | case 'n': 262 | case 'v': 263 | INC_OUTPUTPOS(arg,2) /* 16 bit per arg */ 264 | break; 265 | 266 | case 'i': 267 | case 'I': 268 | INC_OUTPUTPOS(arg,sizeof(int)) 269 | break; 270 | 271 | case 'l': 272 | case 'L': 273 | case 'N': 274 | case 'V': 275 | INC_OUTPUTPOS(arg,4) /* 32 bit per arg */ 276 | break; 277 | 278 | case 'f': 279 | INC_OUTPUTPOS(arg,sizeof(float)) 280 | break; 281 | 282 | case 'd': 283 | INC_OUTPUTPOS(arg,sizeof(double)) 284 | break; 285 | 286 | case 'X': 287 | outputpos -= arg; 288 | 289 | if (outputpos < 0) { 290 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", code); 291 | outputpos = 0; 292 | } 293 | break; 294 | 295 | case '@': 296 | outputpos = arg; 297 | break; 298 | } 299 | 300 | if (outputsize < outputpos) { 301 | outputsize = outputpos; 302 | } 303 | } 304 | 305 | output = emalloc(outputsize + 1); 306 | outputpos = 0; 307 | currentarg = 1; 308 | 309 | /* Do actual packing */ 310 | for (i = 0; i < formatcount; i++) { 311 | int code = (int) formatcodes[i]; 312 | int arg = formatargs[i]; 313 | zval **val; 314 | 315 | switch ((int) code) { 316 | case 'a': 317 | case 'A': 318 | memset(&output[outputpos], (code == 'a') ? '\0' : ' ', arg); 319 | val = argv[currentarg++]; 320 | if (Z_ISREF_PP(val)) { 321 | SEPARATE_ZVAL(val); 322 | } 323 | convert_to_string_ex(val); 324 | memcpy(&output[outputpos], Z_STRVAL_PP(val), 325 | (Z_STRLEN_PP(val) < arg) ? Z_STRLEN_PP(val) : arg); 326 | outputpos += arg; 327 | break; 328 | 329 | case 'h': 330 | case 'H': { 331 | int nibbleshift = (code == 'h') ? 0 : 4; 332 | int first = 1; 333 | char *v; 334 | 335 | val = argv[currentarg++]; 336 | if (Z_ISREF_PP(val)) { 337 | SEPARATE_ZVAL(val); 338 | } 339 | convert_to_string_ex(val); 340 | v = Z_STRVAL_PP(val); 341 | outputpos--; 342 | if(arg > Z_STRLEN_PP(val)) { 343 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough characters in string", code); 344 | arg = Z_STRLEN_PP(val); 345 | } 346 | 347 | while (arg-- > 0) { 348 | char n = *v++; 349 | 350 | if (n >= '0' && n <= '9') { 351 | n -= '0'; 352 | } else if (n >= 'A' && n <= 'F') { 353 | n -= ('A' - 10); 354 | } else if (n >= 'a' && n <= 'f') { 355 | n -= ('a' - 10); 356 | } else { 357 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: illegal hex digit %c", code, n); 358 | n = 0; 359 | } 360 | 361 | if (first--) { 362 | output[++outputpos] = 0; 363 | } else { 364 | first = 1; 365 | } 366 | 367 | output[outputpos] |= (n << nibbleshift); 368 | nibbleshift = (nibbleshift + 4) & 7; 369 | } 370 | 371 | outputpos++; 372 | break; 373 | } 374 | 375 | case 'c': 376 | case 'C': 377 | while (arg-- > 0) { 378 | php_pack(argv[currentarg++], 1, byte_map, &output[outputpos]); 379 | outputpos++; 380 | } 381 | break; 382 | 383 | case 's': 384 | case 'S': 385 | case 'n': 386 | case 'v': { 387 | int *map = machine_endian_short_map; 388 | 389 | if (code == 'n') { 390 | map = big_endian_short_map; 391 | } else if (code == 'v') { 392 | map = little_endian_short_map; 393 | } 394 | 395 | while (arg-- > 0) { 396 | php_pack(argv[currentarg++], 2, map, &output[outputpos]); 397 | outputpos += 2; 398 | } 399 | break; 400 | } 401 | 402 | case 'i': 403 | case 'I': 404 | while (arg-- > 0) { 405 | php_pack(argv[currentarg++], sizeof(int), int_map, &output[outputpos]); 406 | outputpos += sizeof(int); 407 | } 408 | break; 409 | 410 | case 'l': 411 | case 'L': 412 | case 'N': 413 | case 'V': { 414 | int *map = machine_endian_long_map; 415 | 416 | if (code == 'N') { 417 | map = big_endian_long_map; 418 | } else if (code == 'V') { 419 | map = little_endian_long_map; 420 | } 421 | 422 | while (arg-- > 0) { 423 | php_pack(argv[currentarg++], 4, map, &output[outputpos]); 424 | outputpos += 4; 425 | } 426 | break; 427 | } 428 | 429 | case 'f': { 430 | float v; 431 | 432 | while (arg-- > 0) { 433 | val = argv[currentarg++]; 434 | convert_to_double_ex(val); 435 | v = (float) Z_DVAL_PP(val); 436 | memcpy(&output[outputpos], &v, sizeof(v)); 437 | outputpos += sizeof(v); 438 | } 439 | break; 440 | } 441 | 442 | case 'd': { 443 | double v; 444 | 445 | while (arg-- > 0) { 446 | val = argv[currentarg++]; 447 | convert_to_double_ex(val); 448 | v = (double) Z_DVAL_PP(val); 449 | memcpy(&output[outputpos], &v, sizeof(v)); 450 | outputpos += sizeof(v); 451 | } 452 | break; 453 | } 454 | 455 | case 'x': 456 | memset(&output[outputpos], '\0', arg); 457 | outputpos += arg; 458 | break; 459 | 460 | case 'X': 461 | outputpos -= arg; 462 | 463 | if (outputpos < 0) { 464 | outputpos = 0; 465 | } 466 | break; 467 | 468 | case '@': 469 | if (arg > outputpos) { 470 | memset(&output[outputpos], '\0', arg - outputpos); 471 | } 472 | outputpos = arg; 473 | break; 474 | } 475 | } 476 | 477 | efree(argv); 478 | efree(formatcodes); 479 | efree(formatargs); 480 | output[outputpos] = '\0'; 481 | RETVAL_STRINGL(output, outputpos, 1); 482 | efree(output); 483 | } 484 | /* }}} */ 485 | 486 | /* {{{ php_unpack 487 | */ 488 | static long php_unpack(char *data, int size, int issigned, int *map) 489 | { 490 | long result; 491 | char *cresult = (char *) &result; 492 | int i; 493 | 494 | result = issigned ? -1 : 0; 495 | 496 | for (i = 0; i < size; i++) { 497 | cresult[map[i]] = *data++; 498 | } 499 | 500 | return result; 501 | } 502 | /* }}} */ 503 | 504 | /* unpack() is based on Perl's unpack(), but is modified a bit from there. 505 | * Rather than depending on error-prone ordered lists or syntactically 506 | * unpleasant pass-by-reference, we return an object with named paramters 507 | * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the 508 | * formatter char (like pack()), "[repeat]" is the optional repeater argument, 509 | * and "name" is the name of the variable to use. 510 | * Example: "c2chars/nints" will return an object with fields 511 | * chars1, chars2, and ints. 512 | * Numeric pack types will return numbers, a and A will return strings, 513 | * f and d will return doubles. 514 | * Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @. 515 | */ 516 | /* {{{ proto array unpack(string format, string input) 517 | Unpack binary string into named array elements according to format argument */ 518 | PHP_FUNCTION(unpack) 519 | { 520 | char *format, *input, *formatarg, *inputarg; 521 | int formatlen, formatarg_len, inputarg_len; 522 | int inputpos, inputlen, i; 523 | 524 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &formatarg, &formatarg_len, 525 | &inputarg, &inputarg_len) == FAILURE) { 526 | return; 527 | } 528 | 529 | format = formatarg; 530 | formatlen = formatarg_len; 531 | input = inputarg; 532 | inputlen = inputarg_len; 533 | inputpos = 0; 534 | 535 | array_init(return_value); 536 | 537 | while (formatlen-- > 0) { 538 | char type = *(format++); 539 | char c; 540 | int arg = 1, argb; 541 | char *name; 542 | int namelen; 543 | int size=0; 544 | 545 | /* Handle format arguments if any */ 546 | if (formatlen > 0) { 547 | c = *format; 548 | 549 | if (c >= '0' && c <= '9') { 550 | arg = atoi(format); 551 | 552 | while (formatlen > 0 && *format >= '0' && *format <= '9') { 553 | format++; 554 | formatlen--; 555 | } 556 | } else if (c == '*') { 557 | arg = -1; 558 | format++; 559 | formatlen--; 560 | } 561 | } 562 | 563 | /* Get of new value in array */ 564 | name = format; 565 | argb = arg; 566 | 567 | while (formatlen > 0 && *format != '/') { 568 | formatlen--; 569 | format++; 570 | } 571 | 572 | namelen = format - name; 573 | 574 | if (namelen > 200) 575 | namelen = 200; 576 | 577 | switch ((int) type) { 578 | /* Never use any input */ 579 | case 'X': 580 | size = -1; 581 | break; 582 | 583 | case '@': 584 | size = 0; 585 | break; 586 | 587 | case 'a': 588 | case 'A': 589 | size = arg; 590 | arg = 1; 591 | break; 592 | 593 | case 'h': 594 | case 'H': 595 | size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg; 596 | arg = 1; 597 | break; 598 | 599 | /* Use 1 byte of input */ 600 | case 'c': 601 | case 'C': 602 | case 'x': 603 | size = 1; 604 | break; 605 | 606 | /* Use 2 bytes of input */ 607 | case 's': 608 | case 'S': 609 | case 'n': 610 | case 'v': 611 | size = 2; 612 | break; 613 | 614 | /* Use sizeof(int) bytes of input */ 615 | case 'i': 616 | case 'I': 617 | size = sizeof(int); 618 | break; 619 | 620 | /* Use 4 bytes of input */ 621 | case 'l': 622 | case 'L': 623 | case 'N': 624 | case 'V': 625 | size = 4; 626 | break; 627 | 628 | /* Use sizeof(float) bytes of input */ 629 | case 'f': 630 | size = sizeof(float); 631 | break; 632 | 633 | /* Use sizeof(double) bytes of input */ 634 | case 'd': 635 | size = sizeof(double); 636 | break; 637 | 638 | default: 639 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid format type %c", type); 640 | zval_dtor(return_value); 641 | RETURN_FALSE; 642 | break; 643 | } 644 | 645 | /* Do actual unpacking */ 646 | for (i = 0; i != arg; i++ ) { 647 | /* Space for name + number, safe as namelen is ensured <= 200 */ 648 | char n[256]; 649 | 650 | if (arg != 1 || namelen == 0) { 651 | /* Need to add element number to name */ 652 | snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1); 653 | } else { 654 | /* Truncate name to next format code or end of string */ 655 | snprintf(n, sizeof(n), "%.*s", namelen, name); 656 | } 657 | 658 | if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) { 659 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: integer overflow", type); 660 | inputpos = 0; 661 | } 662 | 663 | if ((inputpos + size) <= inputlen) { 664 | switch ((int) type) { 665 | case 'a': 666 | case 'A': { 667 | char pad = (type == 'a') ? '\0' : ' '; 668 | int len = inputlen - inputpos; /* Remaining string */ 669 | 670 | /* If size was given take minimum of len and size */ 671 | if ((size >= 0) && (len > size)) { 672 | len = size; 673 | } 674 | 675 | size = len; 676 | 677 | /* Remove padding chars from unpacked data */ 678 | while (--len >= 0) { 679 | if (input[inputpos + len] != pad) 680 | break; 681 | } 682 | 683 | add_assoc_stringl(return_value, n, &input[inputpos], len + 1, 1); 684 | break; 685 | } 686 | 687 | case 'h': 688 | case 'H': { 689 | int len = (inputlen - inputpos) * 2; /* Remaining */ 690 | int nibbleshift = (type == 'h') ? 0 : 4; 691 | int first = 1; 692 | char *buf; 693 | int ipos, opos; 694 | 695 | /* If size was given take minimum of len and size */ 696 | if (size >= 0 && len > (size * 2)) { 697 | len = size * 2; 698 | } 699 | 700 | if (argb > 0) { 701 | len -= argb % 2; 702 | } 703 | 704 | buf = emalloc(len + 1); 705 | 706 | for (ipos = opos = 0; opos < len; opos++) { 707 | char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf; 708 | 709 | if (cc < 10) { 710 | cc += '0'; 711 | } else { 712 | cc += 'a' - 10; 713 | } 714 | 715 | buf[opos] = cc; 716 | nibbleshift = (nibbleshift + 4) & 7; 717 | 718 | if (first-- == 0) { 719 | ipos++; 720 | first = 1; 721 | } 722 | } 723 | 724 | buf[len] = '\0'; 725 | add_assoc_stringl(return_value, n, buf, len, 1); 726 | efree(buf); 727 | break; 728 | } 729 | 730 | case 'c': 731 | case 'C': { 732 | int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0; 733 | long v = php_unpack(&input[inputpos], 1, issigned, byte_map); 734 | add_assoc_long(return_value, n, v); 735 | break; 736 | } 737 | 738 | case 's': 739 | case 'S': 740 | case 'n': 741 | case 'v': { 742 | long v; 743 | int issigned = 0; 744 | int *map = machine_endian_short_map; 745 | 746 | if (type == 's') { 747 | issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80; 748 | } else if (type == 'n') { 749 | map = big_endian_short_map; 750 | } else if (type == 'v') { 751 | map = little_endian_short_map; 752 | } 753 | 754 | v = php_unpack(&input[inputpos], 2, issigned, map); 755 | add_assoc_long(return_value, n, v); 756 | break; 757 | } 758 | 759 | case 'i': 760 | case 'I': { 761 | long v = 0; 762 | int issigned = 0; 763 | 764 | if (type == 'i') { 765 | issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80; 766 | } else if (sizeof(long) > 4 && (input[inputpos + machine_endian_long_map[3]] & 0x80) == 0x80) { 767 | v = ~INT_MAX; 768 | } 769 | 770 | v |= php_unpack(&input[inputpos], sizeof(int), issigned, int_map); 771 | add_assoc_long(return_value, n, v); 772 | break; 773 | } 774 | 775 | case 'l': 776 | case 'L': 777 | case 'N': 778 | case 'V': { 779 | int issigned = 0; 780 | int *map = machine_endian_long_map; 781 | long v = 0; 782 | 783 | if (type == 'l' || type == 'L') { 784 | issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80; 785 | } else if (type == 'N') { 786 | issigned = input[inputpos] & 0x80; 787 | map = big_endian_long_map; 788 | } else if (type == 'V') { 789 | issigned = input[inputpos + 3] & 0x80; 790 | map = little_endian_long_map; 791 | } 792 | 793 | if (sizeof(long) > 4 && issigned) { 794 | v = ~INT_MAX; 795 | } 796 | 797 | v |= php_unpack(&input[inputpos], 4, issigned, map); 798 | if (sizeof(long) > 4) { 799 | if (type == 'l') { 800 | v = (signed int) v; 801 | } else { 802 | v = (unsigned int) v; 803 | } 804 | } 805 | add_assoc_long(return_value, n, v); 806 | break; 807 | } 808 | 809 | case 'f': { 810 | float v; 811 | 812 | memcpy(&v, &input[inputpos], sizeof(float)); 813 | add_assoc_double(return_value, n, (double)v); 814 | break; 815 | } 816 | 817 | case 'd': { 818 | double v; 819 | 820 | memcpy(&v, &input[inputpos], sizeof(double)); 821 | add_assoc_double(return_value, n, v); 822 | break; 823 | } 824 | 825 | case 'x': 826 | /* Do nothing with input, just skip it */ 827 | break; 828 | 829 | case 'X': 830 | if (inputpos < size) { 831 | inputpos = -size; 832 | i = arg - 1; /* Break out of for loop */ 833 | 834 | if (arg >= 0) { 835 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type); 836 | } 837 | } 838 | break; 839 | 840 | case '@': 841 | if (arg <= inputlen) { 842 | inputpos = arg; 843 | } else { 844 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type); 845 | } 846 | 847 | i = arg - 1; /* Done, break out of for loop */ 848 | break; 849 | } 850 | 851 | inputpos += size; 852 | if (inputpos < 0) { 853 | if (size != -1) { /* only print warning if not working with * */ 854 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type); 855 | } 856 | inputpos = 0; 857 | } 858 | } else if (arg < 0) { 859 | /* Reached end of input for '*' repeater */ 860 | break; 861 | } else { 862 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough input, need %d, have %d", type, size, inputlen - inputpos); 863 | zval_dtor(return_value); 864 | RETURN_FALSE; 865 | } 866 | } 867 | 868 | formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */ 869 | format++; 870 | } 871 | } 872 | /* }}} */ 873 | 874 | /* {{{ PHP_MINIT_FUNCTION 875 | */ 876 | PHP_MINIT_FUNCTION(pack) 877 | { 878 | int machine_endian_check = 1; 879 | int i; 880 | 881 | machine_little_endian = ((char *)&machine_endian_check)[0]; 882 | 883 | if (machine_little_endian) { 884 | /* Where to get lo to hi bytes from */ 885 | byte_map[0] = 0; 886 | 887 | for (i = 0; i < (int)sizeof(int); i++) { 888 | int_map[i] = i; 889 | } 890 | 891 | machine_endian_short_map[0] = 0; 892 | machine_endian_short_map[1] = 1; 893 | big_endian_short_map[0] = 1; 894 | big_endian_short_map[1] = 0; 895 | little_endian_short_map[0] = 0; 896 | little_endian_short_map[1] = 1; 897 | 898 | machine_endian_long_map[0] = 0; 899 | machine_endian_long_map[1] = 1; 900 | machine_endian_long_map[2] = 2; 901 | machine_endian_long_map[3] = 3; 902 | big_endian_long_map[0] = 3; 903 | big_endian_long_map[1] = 2; 904 | big_endian_long_map[2] = 1; 905 | big_endian_long_map[3] = 0; 906 | little_endian_long_map[0] = 0; 907 | little_endian_long_map[1] = 1; 908 | little_endian_long_map[2] = 2; 909 | little_endian_long_map[3] = 3; 910 | } 911 | else { 912 | zval val; 913 | int size = sizeof(Z_LVAL(val)); 914 | Z_LVAL(val)=0; /*silence a warning*/ 915 | 916 | /* Where to get hi to lo bytes from */ 917 | byte_map[0] = size - 1; 918 | 919 | for (i = 0; i < (int)sizeof(int); i++) { 920 | int_map[i] = size - (sizeof(int) - i); 921 | } 922 | 923 | machine_endian_short_map[0] = size - 2; 924 | machine_endian_short_map[1] = size - 1; 925 | big_endian_short_map[0] = size - 2; 926 | big_endian_short_map[1] = size - 1; 927 | little_endian_short_map[0] = size - 1; 928 | little_endian_short_map[1] = size - 2; 929 | 930 | machine_endian_long_map[0] = size - 4; 931 | machine_endian_long_map[1] = size - 3; 932 | machine_endian_long_map[2] = size - 2; 933 | machine_endian_long_map[3] = size - 1; 934 | big_endian_long_map[0] = size - 4; 935 | big_endian_long_map[1] = size - 3; 936 | big_endian_long_map[2] = size - 2; 937 | big_endian_long_map[3] = size - 1; 938 | little_endian_long_map[0] = size - 1; 939 | little_endian_long_map[1] = size - 2; 940 | little_endian_long_map[2] = size - 3; 941 | little_endian_long_map[3] = size - 4; 942 | } 943 | 944 | return SUCCESS; 945 | } 946 | /* }}} */ 947 | 948 | /* 949 | * Local variables: 950 | * tab-width: 4 951 | * c-basic-offset: 4 952 | * End: 953 | * vim600: noet sw=4 ts=4 fdm=marker 954 | * vim<600: noet sw=4 ts=4 955 | */ 956 | -------------------------------------------------------------------------------- /pack.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | //#include "ext/standard/head.h" 16 | //#include "NODEJS_string.h" 17 | 18 | #if HAVE_NETINET_IN_H 19 | #include 20 | #endif 21 | 22 | #include 23 | #include "pack.h" 24 | 25 | 26 | using namespace v8; 27 | using namespace node; 28 | 29 | 30 | #define THROW_ERROR(bufname, x) { \ 31 | char bufname[256] = {0}; \ 32 | sprintf x; \ 33 | return ThrowException(Exception::Error (String::New(bufname))); \ 34 | } 35 | 36 | #define THROW_IF_NOT(condition, text) if (!(condition)) { \ 37 | return ThrowException(Exception::Error (String::New(text))); \ 38 | } 39 | 40 | #define THROW_IF_NOT_A(condition, bufname, x) if (!(condition)) { \ 41 | char bufname[256] = {0}; \ 42 | sprintf x; \ 43 | return ThrowException(Exception::Error (String::New(bufname))); \ 44 | } 45 | 46 | 47 | #define TARGET (v8::Handle target) 48 | 49 | #define NODEJS_ERROR( strfmt, arg ) THROW_ERROR( _buf, (_buf, (const char*)strfmt, arg) ) 50 | 51 | 52 | #define INC_OUTPUTPOS(a,b) \ 53 | if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \ 54 | NODEJS_ERROR( "Type %c: integer overflow in format string", code); \ 55 | return Undefined(); \ 56 | } \ 57 | outputpos += (a)*(b); 58 | 59 | #define safe_emalloc(len,objsize,setwith) malloc( len*objsize ) 60 | 61 | #define ToCString(value) (*value ? *value : "") 62 | 63 | inline double ToDouble(v8::Local value) 64 | { 65 | return static_cast(value->NumberValue()); 66 | } 67 | inline float ToFloat(v8::Local value) 68 | { 69 | return static_cast(value->NumberValue()); 70 | } 71 | 72 | 73 | 74 | char *BufferData(node::Buffer *b) { 75 | return node::Buffer::Data(b->handle_); 76 | } 77 | size_t BufferLength(node::Buffer *b) { 78 | return node::Buffer::Length(b->handle_); 79 | } 80 | char *BufferData(v8::Local buf_obj) { 81 | v8::HandleScope scope; 82 | return node::Buffer::Data(buf_obj); 83 | } 84 | size_t BufferLength(v8::Local buf_obj) { 85 | v8::HandleScope scope; 86 | return node::Buffer::Length(buf_obj); 87 | } 88 | 89 | static int debug_mode; 90 | /* Whether machine is little endian */ 91 | static char machine_little_endian; 92 | 93 | /* Mapping of byte from char (8bit) to long for machine endian */ 94 | static int byte_map[1]; 95 | 96 | /* Mappings of bytes from int (machine dependant) to int for machine endian */ 97 | static int int_map[sizeof(int)]; 98 | 99 | /* Mappings of bytes from shorts (16bit) for all endian environments */ 100 | static int machine_endian_short_map[2]; 101 | static int big_endian_short_map[2]; 102 | static int little_endian_short_map[2]; 103 | 104 | /* Mappings of bytes from longs (32bit) for all endian environments */ 105 | static int machine_endian_long_map[4]; 106 | static int big_endian_long_map[4]; 107 | static int little_endian_long_map[4]; 108 | 109 | 110 | class HiPack : public ObjectWrap { 111 | public: 112 | static void SetDebug(int n) { debug_mode = n; } 113 | static int IsDebug() { return debug_mode; } 114 | 115 | static Handle New(const Arguments& args) { 116 | HandleScope scope; 117 | 118 | HiPack* hipack = new HiPack(); 119 | hipack->Wrap(args.This()); 120 | 121 | return args.This(); 122 | } 123 | /* {{{ NODEJS_pack 124 | */ 125 | static void NODEJS_pack(int32_t val, int size, int *map, char *output) 126 | { 127 | int i; 128 | char *v; 129 | 130 | //convert_to_long_ex(val); 131 | v = (char *) &val; 132 | 133 | for (i = 0; i < size; i++) { 134 | *output++ = v[map[i]]; 135 | } 136 | } 137 | /* }}} */ 138 | 139 | // We define a max size, since we want to avoid any mallocs() theres are on the stack and faster. 140 | // I think 48 is enough for 99.99% cases. Next release we might have a init option 141 | #define MAX_FORMAT_CODES (48) 142 | 143 | // We also store the output locally in the stack if its under 256 bytes in size, else we malloc it. 144 | 145 | /* pack() idea stolen from Perl (implemented formats behave the same as there) 146 | * Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, V, v, @. 147 | */ 148 | /* {{{ proto string pack(String format, mixed arg1 [, mixed arg2 [, mixed ...]]) 149 | Takes one or more arguments and packs them into a binary string according to the format argument 150 | * If the first ARG is a Buffer(), is that as the output instead of creating its own buffer. 151 | */ 152 | static Handle pack(const Arguments& argv) 153 | { 154 | HandleScope scope; 155 | int outputpos = 0, outputsize = 0; 156 | int num_args, i; 157 | int currentarg = 0, firstarg = 0; 158 | char *output = NULL; 159 | char formatcodes[MAX_FORMAT_CODES]; // Local stored codes for faster access/ avoid mallocs 160 | int formatargs[MAX_FORMAT_CODES]; // Local index store. 161 | int formatcount = 0; 162 | int formatlen; 163 | const char *format; 164 | bool argsFromArray = false; 165 | bool parentBuffer = false; 166 | Local outputBuffer; 167 | char localOutput[256]; 168 | 169 | num_args = argv.Length(); 170 | if (argv.Length() < 2 ) { 171 | NODEJS_ERROR( "Not enough arguements, total args: %d", argv.Length() ); 172 | } 173 | 174 | if (argv.Length() >= 1 ) 175 | { 176 | currentarg = 0; 177 | if (argv[currentarg]->IsObject() ) 178 | { 179 | if( IsDebug() ) printf("Using passed in buffer as the output\n"); 180 | outputBuffer = ( argv[currentarg]->ToObject() ); 181 | parentBuffer = true; 182 | currentarg = 1; 183 | if (!argv[currentarg]->IsString()) { 184 | NODEJS_ERROR( "Second argument must be a string or buffer, type of arg is %d", argv[currentarg]->IsString() ); 185 | } 186 | } else 187 | if (!argv[currentarg]->IsString()) { 188 | NODEJS_ERROR( "First argument must be a string or buffer, type of arg is %d", argv[currentarg]->IsString() ); 189 | } 190 | } 191 | 192 | //printf( "arg1 is : %s", *args[1] ); 193 | //Handle arg2 = args[1]->ToObject(); 194 | //Array array = args[1]->ToObject()-> 195 | //num_args = 3; 196 | //Local argv[32]; 197 | //for( int i=0;iGet(i); 199 | // } 200 | 201 | String::Utf8Value formatString( argv[currentarg] ); 202 | formatlen = formatString.length(); 203 | format = ToCString( formatString ); 204 | 205 | /* We have a maximum of format codes to deal with */ 206 | //formatcodes = (char*)safe_emalloc(formatlen, sizeof(*formatcodes), 0); 207 | //formatargs = (int*)safe_emalloc(formatlen, sizeof(*formatargs), 0); 208 | 209 | if( IsDebug()>0 ) printf("starting.. numargs=%d, formatlen=%d\n", num_args, formatlen); 210 | 211 | currentarg++; 212 | firstarg = currentarg; 213 | 214 | Local args; 215 | if( argv[currentarg]->IsArray() == true ) 216 | { 217 | args = Array::Cast(*argv[currentarg]); 218 | if(IsDebug()) printf("Args size is %d\n", args->Length() ); 219 | num_args = args->Length(); 220 | currentarg = firstarg = 0; 221 | //for( i=0;iGet(i)->Int32Value() ); 223 | //} 224 | argsFromArray = true; 225 | } 226 | #define GETARG(n) (argsFromArray ? args->Get(n) : argv[n]) 227 | 228 | 229 | /* Preprocess format into formatcodes and formatargs */ 230 | for (i = 0; i < formatlen; formatcount++) { 231 | char code = format[i++]; 232 | int arg = 1; 233 | 234 | if( IsDebug()>0 ) printf("starting..format args currentarg=%d \n", currentarg ); 235 | 236 | /* Handle format arguments if any */ 237 | if (i < formatlen) { 238 | char c = format[i]; 239 | 240 | if (c == '*') { 241 | arg = -1; 242 | i++; 243 | } 244 | else if (c >= '0' && c <= '9') { 245 | arg = atoi(&format[i]); 246 | 247 | while (format[i] >= '0' && format[i] <= '9' && i < formatlen) { 248 | i++; 249 | } 250 | } 251 | } 252 | 253 | if( IsDebug()>0 ) printf("starting..format codes currentargs=%d\n", currentarg ); 254 | 255 | /* Handle special arg '*' for all codes and check argv overflows */ 256 | switch ((int) code) { 257 | /* Never uses any args */ 258 | case 'x': 259 | case 'X': 260 | case '@': 261 | if (arg < 0) { 262 | NODEJS_ERROR( "Type %c: '*' ignored", code); 263 | arg = 1; 264 | } 265 | break; 266 | 267 | /* Always uses one arg */ 268 | case 'a': 269 | case 'A': 270 | case 'Z': 271 | case 'h': 272 | case 'H': 273 | if (currentarg >= num_args) { 274 | NODEJS_ERROR( "Type %c: not enough arguments", code); 275 | return Undefined(); 276 | } 277 | 278 | if (arg < 0) { 279 | Local currstr = GETARG(currentarg)->ToString(); 280 | arg = currstr->Length(); 281 | if( code == 'Z' ) { 282 | arg++; 283 | } 284 | } 285 | currentarg++; 286 | break; 287 | 288 | /* Use as many args as specified */ 289 | case 'c': 290 | case 'C': 291 | case 's': 292 | case 'S': 293 | case 'i': 294 | case 'I': 295 | case 'l': 296 | case 'L': 297 | case 'n': 298 | case 'N': 299 | case 'v': 300 | case 'V': 301 | case 'f': 302 | case 'd': 303 | if (arg < 0) { 304 | arg = num_args - currentarg; 305 | } 306 | //printf("currentarg=%d, arg=%d, code=%c\n", currentarg, arg, code ); 307 | currentarg += arg; 308 | 309 | if (currentarg > num_args) { 310 | NODEJS_ERROR( "Type %c: too few arguments", code); 311 | return Undefined(); 312 | } 313 | break; 314 | 315 | default: 316 | NODEJS_ERROR( "Type %c: unknown format code", code); 317 | return Undefined(); 318 | } 319 | 320 | formatcodes[formatcount] = code; 321 | formatargs[formatcount] = arg; 322 | } 323 | 324 | if (currentarg < num_args) { 325 | NODEJS_ERROR( "%d arguments unused", (num_args - currentarg)); 326 | } 327 | 328 | /* Calculate output length and upper bound while processing*/ 329 | for (i = 0; i < formatcount; i++) { 330 | int code = (int) formatcodes[i]; 331 | int arg = formatargs[i]; 332 | 333 | switch ((int) code) { 334 | case 'h': 335 | case 'H': 336 | INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */ 337 | break; 338 | 339 | case 'a': 340 | case 'A': 341 | case 'Z': 342 | case 'c': 343 | case 'C': 344 | case 'x': 345 | INC_OUTPUTPOS(arg,1) /* 8 bit per arg */ 346 | break; 347 | 348 | case 's': 349 | case 'S': 350 | case 'n': 351 | case 'v': 352 | INC_OUTPUTPOS(arg,2) /* 16 bit per arg */ 353 | break; 354 | 355 | case 'i': 356 | case 'I': 357 | INC_OUTPUTPOS(arg,sizeof(int)) 358 | break; 359 | 360 | case 'l': 361 | case 'L': 362 | case 'N': 363 | case 'V': 364 | INC_OUTPUTPOS(arg,4) /* 32 bit per arg */ 365 | break; 366 | 367 | case 'f': 368 | INC_OUTPUTPOS(arg,sizeof(float)) 369 | break; 370 | 371 | case 'd': 372 | INC_OUTPUTPOS(arg,sizeof(double)) 373 | break; 374 | 375 | case 'X': 376 | outputpos -= arg; 377 | 378 | if (outputpos < 0) { 379 | NODEJS_ERROR( "Type %c: outside of string", code); 380 | outputpos = 0; 381 | } 382 | break; 383 | 384 | case '@': 385 | outputpos = arg; 386 | break; 387 | } 388 | 389 | if (outputsize < outputpos) { 390 | outputsize = outputpos; 391 | } 392 | } 393 | 394 | 395 | if( parentBuffer ) 396 | { 397 | output = BufferData( outputBuffer ); 398 | } else 399 | if( outputsize > 255 ) { 400 | output = (char*)malloc(outputsize + 1); 401 | } else { 402 | output = localOutput; // Use local storage 403 | } 404 | 405 | outputpos = 0; 406 | currentarg = firstarg; 407 | 408 | Local val; 409 | Local strval; 410 | /* Do actual packing */ 411 | for (i = 0; i < formatcount; i++) { 412 | 413 | int code = (int) formatcodes[i]; 414 | int arg = formatargs[i]; 415 | 416 | switch ((int) code) { 417 | case 'a': 418 | case 'A': 419 | case 'Z': 420 | { 421 | int arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1); 422 | memset(&output[outputpos], (code == 'a' || code == 'Z') ? '\0' : ' ', arg); 423 | //String::Utf8Value utf( argv[currentarg++]->ToString() ); 424 | String::Utf8Value utf( GETARG(currentarg)->ToString() ); 425 | currentarg++; 426 | memcpy(&output[outputpos], *utf, (utf.length() < arg_cp) ? utf.length() : arg_cp); 427 | outputpos += arg; 428 | } 429 | break; 430 | 431 | case 'h': 432 | case 'H': { 433 | int nibbleshift = (code == 'h') ? 0 : 4; 434 | int first = 1; 435 | 436 | String::Utf8Value utf( GETARG(currentarg)->ToString() ); 437 | currentarg++; 438 | 439 | if(arg > utf.length() ) { 440 | NODEJS_ERROR( "Type %c: not enough characters in string", code ); 441 | arg = utf.length(); 442 | } 443 | 444 | outputpos--; 445 | int vi=0; 446 | char *v = *utf; 447 | while (arg-- > 0) { 448 | char n = v[vi++]; 449 | 450 | if (n >= '0' && n <= '9') { 451 | n -= '0'; 452 | } else if (n >= 'A' && n <= 'F') { 453 | n -= ('A' - 10); 454 | } else if (n >= 'a' && n <= 'f') { 455 | n -= ('a' - 10); 456 | } else { 457 | THROW_ERROR( _buf, (_buf, "Type %c: illegal hex digit %c", code, n) ); 458 | n = 0; 459 | } 460 | 461 | if (first--) { 462 | output[++outputpos] = 0; 463 | } else { 464 | first = 1; 465 | } 466 | 467 | output[outputpos] |= (n << nibbleshift); 468 | nibbleshift = (nibbleshift + 4) & 7; 469 | } 470 | 471 | outputpos++; 472 | break; 473 | } 474 | 475 | case 'c': 476 | case 'C': 477 | while (arg-- > 0) { 478 | val = GETARG(currentarg); 479 | currentarg++; 480 | NODEJS_pack( (int)val->Int32Value(), 1, byte_map, &output[outputpos]); 481 | outputpos++; 482 | } 483 | break; 484 | 485 | case 's': 486 | case 'S': 487 | case 'n': 488 | case 'v': { 489 | int *map = machine_endian_short_map; 490 | 491 | if (code == 'n') { 492 | map = big_endian_short_map; 493 | } else if (code == 'v') { 494 | map = little_endian_short_map; 495 | } 496 | 497 | while (arg-- > 0) { 498 | val = GETARG(currentarg); 499 | currentarg++; 500 | NODEJS_pack(val->Int32Value(), 2, map, &output[outputpos]); 501 | outputpos += 2; 502 | } 503 | break; 504 | } 505 | 506 | case 'i': 507 | case 'I': 508 | while (arg-- > 0) { 509 | val = GETARG(currentarg); 510 | currentarg++; 511 | NODEJS_pack(val->Int32Value(), sizeof(int), int_map, &output[outputpos]); 512 | outputpos += sizeof(int); 513 | } 514 | break; 515 | 516 | case 'l': 517 | case 'L': 518 | case 'N': 519 | case 'V': { 520 | int *map = machine_endian_long_map; 521 | 522 | if (code == 'N') { 523 | map = big_endian_long_map; 524 | } else if (code == 'V') { 525 | map = little_endian_long_map; 526 | } 527 | 528 | while (arg-- > 0) { 529 | val = GETARG(currentarg); 530 | currentarg++; 531 | NODEJS_pack(val->Int32Value(), 4, map, &output[outputpos]); 532 | outputpos += 4; 533 | } 534 | break; 535 | } 536 | 537 | case 'f': { 538 | float v; 539 | 540 | while (arg-- > 0) { 541 | val = GETARG(currentarg); 542 | currentarg++; 543 | v = ToFloat( val ); 544 | memcpy(&output[outputpos], &v, sizeof(v)); 545 | outputpos += sizeof(v); 546 | } 547 | break; 548 | } 549 | 550 | case 'd': { 551 | double v; 552 | 553 | while (arg-- > 0) { 554 | val = GETARG(currentarg); 555 | currentarg++; 556 | v = ToDouble( val ); 557 | memcpy(&output[outputpos], &v, sizeof(v)); 558 | outputpos += sizeof(v); 559 | } 560 | break; 561 | } 562 | 563 | case 'x': 564 | memset(&output[outputpos], '\0', arg); 565 | outputpos += arg; 566 | break; 567 | 568 | case 'X': 569 | outputpos -= arg; 570 | 571 | if (outputpos < 0) { 572 | outputpos = 0; 573 | } 574 | break; 575 | 576 | case '@': 577 | if (arg > outputpos) { 578 | memset(&output[outputpos], '\0', arg - outputpos); 579 | } 580 | outputpos = arg; 581 | break; 582 | } 583 | } 584 | 585 | output[outputpos] = '\0'; 586 | 587 | //return Undefined(); 588 | if( parentBuffer ) { 589 | return scope.Close( outputBuffer ); 590 | } 591 | 592 | Local return_buffer = Buffer::New( output, outputpos ); 593 | if( outputsize > 255 ) { 594 | free(output); 595 | } 596 | return scope.Close( return_buffer->handle_ ); 597 | } 598 | /* }}} */ 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | /* {{{ NODEJS_unpack 608 | */ 609 | static long NODEJS_unpack(char *data, int size, int issigned, int *map) 610 | { 611 | long result; 612 | char *cresult = (char *) &result; 613 | int i; 614 | 615 | result = issigned ? -1 : 0; 616 | 617 | for (i = 0; i < size; i++) { 618 | cresult[map[i]] = *data++; 619 | } 620 | 621 | return result; 622 | } 623 | /* }}} */ 624 | 625 | 626 | 627 | 628 | 629 | /* unpack() is based on Perl's unpack(), but is modified a bit from there. 630 | * Rather than depending on error-prone ordered lists or syntactically 631 | * unpleasant pass-by-reference, we return an object with named paramters 632 | * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the 633 | * formatter char (like pack()), "[repeat]" is the optional repeater argument, 634 | * and "name" is the name of the variable to use. 635 | * Example: "c2chars/nints" will return an object with fields 636 | * chars1, chars2, and ints. 637 | * Numeric pack types will return numbers, a and A will return strings, 638 | * f and d will return doubles. 639 | * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @. 640 | */ 641 | /* {{{ proto array unpack(String format, Buffer input) 642 | Unpack binary string into named array elements according to format argument */ 643 | 644 | static Handle unpack(const Arguments& argv) 645 | { 646 | HandleScope scope; 647 | const char *format; 648 | char *input = NULL; 649 | int formatlen, formatidx=0; 650 | int inputpos, inputlen=0, i; 651 | bool usePerlFormat = false; 652 | 653 | if (argv.Length() == 0 || !argv[0]->IsString()) { 654 | return ThrowException(Exception::TypeError( 655 | String::New("First argument must be a string"))); 656 | } 657 | 658 | String::Utf8Value formatString( argv[0]->ToString() ); 659 | format = *(formatString); 660 | formatlen = formatString.length(); 661 | 662 | if (Buffer::HasInstance(argv[1])) { 663 | // buffer 664 | Local buffer = argv[1]->ToObject(); 665 | inputlen = BufferLength(buffer); 666 | input = BufferData(buffer); 667 | } 668 | 669 | inputpos = 0; 670 | if (argv.Length() >= 3 && argv[2].IsEmpty() == false ) { 671 | usePerlFormat = true; 672 | } 673 | 674 | Local return_value = Object::New(); 675 | 676 | while (formatlen-- > 0) { 677 | char type = format[ formatidx++ ]; 678 | char c; 679 | int arg = 1, argb; 680 | int namelen = 0; 681 | int size=0; 682 | Local name; 683 | 684 | /* Handle format arguments if any */ 685 | if (formatlen > 0) { 686 | c = format[formatidx]; 687 | 688 | if (c >= '0' && c <= '9') { 689 | arg = c - '0'; 690 | 691 | while (formatlen > 0 && format[formatidx] >= '0' && format[formatidx] <= '9') { 692 | arg = (arg*10) + (format[formatidx] - '0'); 693 | formatidx++; 694 | formatlen--; 695 | } 696 | } else if (c == '*') { 697 | arg = -1; 698 | formatidx++; 699 | formatlen--; 700 | } 701 | } 702 | argb = arg; 703 | 704 | /* Get of new value in array */ 705 | if( usePerlFormat == false ) { 706 | namelen = 0; 707 | while (formatlen > 0 && format[formatidx] != '/') { 708 | formatlen--; 709 | formatidx++; 710 | namelen++; 711 | } 712 | if (namelen > 200) 713 | namelen = 200; 714 | name = String::New( format+formatidx-namelen, namelen ); 715 | } else { 716 | char temp[8]; sprintf( temp, "%d", arg ); 717 | namelen = strlen(temp); 718 | name = String::New( temp, namelen ); 719 | } 720 | 721 | switch ((int) type) { 722 | /* Never use any input */ 723 | case 'X': 724 | size = -1; 725 | break; 726 | 727 | case '@': 728 | size = 0; 729 | break; 730 | 731 | case 'a': 732 | case 'A': 733 | case 'Z': 734 | size = arg; 735 | arg = 1; 736 | break; 737 | 738 | case 'h': 739 | case 'H': 740 | size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg; 741 | arg = 1; 742 | break; 743 | 744 | /* Use 1 byte of input */ 745 | case 'c': 746 | case 'C': 747 | case 'x': 748 | size = 1; 749 | break; 750 | 751 | /* Use 2 bytes of input */ 752 | case 's': 753 | case 'S': 754 | case 'n': 755 | case 'v': 756 | size = 2; 757 | break; 758 | 759 | /* Use sizeof(int) bytes of input */ 760 | case 'i': 761 | case 'I': 762 | size = sizeof(int); 763 | break; 764 | 765 | /* Use 4 bytes of input */ 766 | case 'l': 767 | case 'L': 768 | case 'N': 769 | case 'V': 770 | size = 4; 771 | break; 772 | 773 | /* Use sizeof(float) bytes of input */ 774 | case 'f': 775 | size = sizeof(float); 776 | break; 777 | 778 | /* Use sizeof(double) bytes of input */ 779 | case 'd': 780 | size = sizeof(double); 781 | break; 782 | 783 | default: 784 | NODEJS_ERROR( "Invalid format type %c", type); 785 | return Undefined(); 786 | break; 787 | } 788 | 789 | /* Do actual unpacking */ 790 | for (i = 0; i != arg; i++ ) { 791 | /* Space for name + number, safe as namelen is ensured <= 200 */ 792 | char n[256]; 793 | 794 | if (arg != 1 || namelen == 0) { 795 | /* Need to add element number to name */ 796 | snprintf(n, sizeof(n), "%.*s%d", namelen, *String::Utf8Value(name), i + 1); 797 | } else { 798 | /* Truncate name to next format code or end of string */ 799 | snprintf(n, sizeof(n), "%.*s", namelen, *String::Utf8Value(name)); 800 | } 801 | 802 | if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) { 803 | NODEJS_ERROR( "Type %c: integer overflow", type); 804 | inputpos = 0; 805 | } 806 | 807 | if ((inputpos + size) <= inputlen && input != NULL) { 808 | switch ((int) type) { 809 | case 'a': 810 | case 'A': { 811 | char pad = (type == 'a') ? '\0' : ' '; 812 | int len = inputlen - inputpos; /* Remaining string */ 813 | 814 | /* If size was given take minimum of len and size */ 815 | if ((size >= 0) && (len > size)) { 816 | len = size; 817 | } 818 | 819 | size = len; 820 | 821 | /* Remove padding chars from unpacked data */ 822 | while (--len >= 0) { 823 | if (input[inputpos + len] != pad) 824 | break; 825 | } 826 | 827 | return_value->Set( String::New(n), String::New( &input[inputpos], len + 1 ) ); 828 | break; 829 | } 830 | case 'Z': { 831 | /* Z will strip everything after the first null character */ 832 | char pad = '\0'; 833 | int s, 834 | len = inputlen - inputpos; /* Remaining string */ 835 | 836 | /* If size was given take minimum of len and size */ 837 | if ((size >= 0) && (len > size)) { 838 | len = size; 839 | } 840 | 841 | size = len; 842 | 843 | /* Remove everything after the first null */ 844 | for (s=0 ; s < len ; s++) { 845 | if (input[inputpos + s] == pad) 846 | break; 847 | } 848 | len = s; 849 | 850 | return_value->Set( String::New(n), String::New( &input[inputpos], len ) ); 851 | break; 852 | } 853 | case 'h': 854 | case 'H': { 855 | int len = (inputlen - inputpos) * 2; /* Remaining */ 856 | int nibbleshift = (type == 'h') ? 0 : 4; 857 | int first = 1; 858 | char *buf; 859 | int ipos, opos; 860 | 861 | /* If size was given take minimum of len and size */ 862 | if (size >= 0 && len > (size * 2)) { 863 | len = size * 2; 864 | } 865 | 866 | if (argb > 0) { 867 | len -= argb % 2; 868 | } 869 | 870 | buf = (char*)malloc(len + 1); 871 | 872 | for (ipos = opos = 0; opos < len; opos++) { 873 | char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf; 874 | 875 | if (cc < 10) { 876 | cc += '0'; 877 | } else { 878 | cc += 'a' - 10; 879 | } 880 | 881 | buf[opos] = cc; 882 | nibbleshift = (nibbleshift + 4) & 7; 883 | 884 | if (first-- == 0) { 885 | ipos++; 886 | first = 1; 887 | } 888 | } 889 | 890 | buf[len] = '\0'; 891 | //add_assoc_stringl(return_value, n, buf, len, 1); 892 | return_value->Set( String::New(n), String::New(buf,len) ); 893 | free(buf); 894 | break; 895 | } 896 | 897 | case 'c': 898 | case 'C': { 899 | int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0; 900 | long v = NODEJS_unpack(&input[inputpos], 1, issigned, byte_map); 901 | //add_assoc_long(return_value, n, v); 902 | return_value->Set( String::New(n), Integer::New(v) ); 903 | break; 904 | } 905 | 906 | case 's': 907 | case 'S': 908 | case 'n': 909 | case 'v': { 910 | long v; 911 | int issigned = 0; 912 | int *map = machine_endian_short_map; 913 | 914 | if (type == 's') { 915 | issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80; 916 | } else if (type == 'n') { 917 | map = big_endian_short_map; 918 | } else if (type == 'v') { 919 | map = little_endian_short_map; 920 | } 921 | 922 | v = NODEJS_unpack(&input[inputpos], 2, issigned, map); 923 | //add_assoc_long(return_value, n, v); 924 | return_value->Set( String::New(n), Integer::New(v) ); 925 | break; 926 | } 927 | 928 | case 'i': 929 | case 'I': { 930 | long v = 0; 931 | int issigned = 0; 932 | 933 | if (type == 'i') { 934 | issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80; 935 | } else if (sizeof(long) > 4 && (input[inputpos + machine_endian_long_map[3]] & 0x80) == 0x80) { 936 | v = ~INT_MAX; 937 | } 938 | 939 | v |= NODEJS_unpack(&input[inputpos], sizeof(int), issigned, int_map); 940 | //add_assoc_long(return_value, n, v); 941 | return_value->Set( String::New(n), Integer::New(v) ); 942 | break; 943 | } 944 | 945 | case 'l': 946 | case 'L': 947 | case 'N': 948 | case 'V': { 949 | int issigned = 0; 950 | int *map = machine_endian_long_map; 951 | long v = 0; 952 | 953 | if (type == 'l' || type == 'L') { 954 | issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80; 955 | } else if (type == 'N') { 956 | issigned = input[inputpos] & 0x80; 957 | map = big_endian_long_map; 958 | } else if (type == 'V') { 959 | issigned = input[inputpos + 3] & 0x80; 960 | map = little_endian_long_map; 961 | } 962 | 963 | if (sizeof(long) > 4 && issigned) { 964 | v = ~INT_MAX; 965 | } 966 | 967 | v |= NODEJS_unpack(&input[inputpos], 4, issigned, map); 968 | if (sizeof(long) > 4) { 969 | if (type == 'l') { 970 | v = (signed int) v; 971 | } else { 972 | v = (unsigned int) v; 973 | } 974 | } 975 | //add_assoc_long(return_value, n, v); 976 | return_value->Set( String::New(n), Integer::New(v) ); 977 | break; 978 | } 979 | 980 | case 'f': { 981 | float v; 982 | 983 | memcpy(&v, &input[inputpos], sizeof(float)); 984 | //add_assoc_double(return_value, n, (double)v); 985 | return_value->Set( String::New(n), Integer::New(v) ); 986 | break; 987 | } 988 | 989 | case 'd': { 990 | double v; 991 | 992 | memcpy(&v, &input[inputpos], sizeof(double)); 993 | //add_assoc_double(return_value, n, v); 994 | return_value->Set( String::New(n), Integer::New(v) ); 995 | break; 996 | } 997 | 998 | case 'x': 999 | /* Do nothing with input, just skip it */ 1000 | break; 1001 | 1002 | case 'X': 1003 | if (inputpos < size) { 1004 | inputpos = -size; 1005 | i = arg - 1; /* Break out of for loop */ 1006 | 1007 | if (arg >= 0) { 1008 | NODEJS_ERROR( "Type %c: outside of string", type); 1009 | } 1010 | } 1011 | break; 1012 | 1013 | case '@': 1014 | if (arg <= inputlen) { 1015 | inputpos = arg; 1016 | } else { 1017 | NODEJS_ERROR( "Type %c: outside of string", type); 1018 | } 1019 | 1020 | i = arg - 1; /* Done, break out of for loop */ 1021 | break; 1022 | } 1023 | 1024 | inputpos += size; 1025 | if (inputpos < 0) { 1026 | if (size != -1) { /* only print warning if not working with * */ 1027 | NODEJS_ERROR( "Type %c: outside of string", type); 1028 | } 1029 | inputpos = 0; 1030 | } 1031 | } else if (arg < 0) { 1032 | /* Reached end of input for '*' repeater */ 1033 | break; 1034 | } else { 1035 | THROW_ERROR( _buf, ( _buf, "Type %c: not enough input, need %d, have %d", type, size, inputlen - inputpos) ); 1036 | return Undefined(); 1037 | } 1038 | } 1039 | 1040 | formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */ 1041 | format++; 1042 | } 1043 | 1044 | return scope.Close( return_value ); 1045 | } 1046 | /* }}} */ 1047 | 1048 | 1049 | static Handle setdebug( const Arguments& argv ) 1050 | { 1051 | HandleScope scope; 1052 | if( argv[0]->IsInt32() ) 1053 | { 1054 | Local var = argv[0]->ToObject(); 1055 | SetDebug( var->Int32Value() ); 1056 | } 1057 | return scope.Close( True() ); 1058 | } 1059 | 1060 | 1061 | static Handle debug( void ) 1062 | { 1063 | HandleScope scope; 1064 | return scope.Close( IsDebug() ? True() : False() ); 1065 | } 1066 | 1067 | 1068 | /* {{{ NODEJS_MINIT_FUNCTION 1069 | */ 1070 | static void Initialize(Handle target) 1071 | { 1072 | HandleScope scope; 1073 | 1074 | Local t = FunctionTemplate::New(New); 1075 | t->InstanceTemplate()->SetInternalFieldCount(1); 1076 | 1077 | target->Set(String::NewSymbol("HiPack"), t->GetFunction()); 1078 | 1079 | //NODE_SET_PROTOTYPE_METHOD(t, "pack", pack); 1080 | NODE_SET_METHOD(target, "pack", pack); 1081 | NODE_SET_METHOD(target, "unpack", unpack); 1082 | NODE_SET_METHOD(target, "setdebug", setdebug); 1083 | //NODE_SET_METHOD(target, "debug", debug); 1084 | 1085 | SetDebug(0); 1086 | int machine_endian_check = 1; 1087 | int i; 1088 | 1089 | machine_little_endian = ((char *)&machine_endian_check)[0]; 1090 | 1091 | if (machine_little_endian) { 1092 | /* Where to get lo to hi bytes from */ 1093 | byte_map[0] = 0; 1094 | 1095 | for (i = 0; i < (int)sizeof(int); i++) { 1096 | int_map[i] = i; 1097 | } 1098 | 1099 | machine_endian_short_map[0] = 0; 1100 | machine_endian_short_map[1] = 1; 1101 | big_endian_short_map[0] = 1; 1102 | big_endian_short_map[1] = 0; 1103 | little_endian_short_map[0] = 0; 1104 | little_endian_short_map[1] = 1; 1105 | 1106 | machine_endian_long_map[0] = 0; 1107 | machine_endian_long_map[1] = 1; 1108 | machine_endian_long_map[2] = 2; 1109 | machine_endian_long_map[3] = 3; 1110 | big_endian_long_map[0] = 3; 1111 | big_endian_long_map[1] = 2; 1112 | big_endian_long_map[2] = 1; 1113 | big_endian_long_map[3] = 0; 1114 | little_endian_long_map[0] = 0; 1115 | little_endian_long_map[1] = 1; 1116 | little_endian_long_map[2] = 2; 1117 | little_endian_long_map[3] = 3; 1118 | } 1119 | else { 1120 | int val; 1121 | int size = sizeof((val)); 1122 | 1123 | /* Where to get hi to lo bytes from */ 1124 | byte_map[0] = size - 1; 1125 | 1126 | for (i = 0; i < (int)sizeof(int); i++) { 1127 | int_map[i] = size - (sizeof(int) - i); 1128 | } 1129 | 1130 | machine_endian_short_map[0] = size - 2; 1131 | machine_endian_short_map[1] = size - 1; 1132 | big_endian_short_map[0] = size - 2; 1133 | big_endian_short_map[1] = size - 1; 1134 | little_endian_short_map[0] = size - 1; 1135 | little_endian_short_map[1] = size - 2; 1136 | 1137 | machine_endian_long_map[0] = size - 4; 1138 | machine_endian_long_map[1] = size - 3; 1139 | machine_endian_long_map[2] = size - 2; 1140 | machine_endian_long_map[3] = size - 1; 1141 | big_endian_long_map[0] = size - 4; 1142 | big_endian_long_map[1] = size - 3; 1143 | big_endian_long_map[2] = size - 2; 1144 | big_endian_long_map[3] = size - 1; 1145 | little_endian_long_map[0] = size - 1; 1146 | little_endian_long_map[1] = size - 2; 1147 | little_endian_long_map[2] = size - 3; 1148 | little_endian_long_map[3] = size - 4; 1149 | } 1150 | } 1151 | /* }}} */ 1152 | 1153 | }; 1154 | 1155 | extern "C" void init (Handle target) 1156 | { 1157 | HandleScope scope; 1158 | HiPack::Initialize(target); 1159 | } 1160 | 1161 | NODE_MODULE(hipack, init); 1162 | 1163 | /* 1164 | * Local variables: 1165 | * tab-width: 4 1166 | * c-basic-offset: 4 1167 | * End: 1168 | * vim600: noet sw=4 ts=4 fdm=marker 1169 | * vim<600: noet sw=4 ts=4 1170 | */ 1171 | -------------------------------------------------------------------------------- /pack.h: -------------------------------------------------------------------------------- 1 | /* 2 | HiPack (C) Raul Sobon 3 | */ 4 | 5 | 6 | #ifndef PACK_H 7 | #define PACK_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace v8; 14 | 15 | #define NODEJS_MINIT_FUNCTION(_fname_) void (_fname_) (Handle target); 16 | #define NODEJS_FUNCTION(_fname_) Handle (_fname_) (const Arguments& argv); 17 | 18 | #endif /* PACK_H */ 19 | 20 | 21 | 22 | /* 23 | WSCRIPT= 24 | 25 | srcdir = '.' 26 | blddir = 'build' 27 | VERSION = '0.0.1' 28 | 29 | def set_options(opt): 30 | opt.tool_options('compiler_cxx') 31 | 32 | def configure(conf): 33 | conf.check_tool('compiler_cxx') 34 | conf.check_tool('node_addon') 35 | 36 | def build(bld): 37 | obj = bld.new_task_gen('cxx', 'shlib', 'node_addon') 38 | obj.target = 'pack' 39 | obj.source = 'pack.cc' 40 | 41 | */ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hipack", 3 | "description": "HiSpeed Pack/Unpack of bytes formats, just like PHP and PERL", 4 | "version": "1.0.4", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/rauls/nodejs-pack.git" 8 | }, 9 | 10 | "homepage": "http://github.com/rauls/nodejs-pack", 11 | "author": "Raul Sobon ", 12 | "main": "./build/Release/hipack", 13 | "directories": { 14 | "lib": "." 15 | }, 16 | "scripts": { 17 | "_preinstall": "make || gmake", 18 | "test": "node test/test.js" 19 | }, 20 | "engines": { 21 | "node": ">=0.8.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/speedtest.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/node 2 | 3 | var pack = require( "hipack" ); 4 | 5 | 6 | // Loop overhead is 50ms for 1m items. 7 | // Calling pack.N is a 500ms overhead doing 'nothing' 8 | function SpeedTest() 9 | { 10 | var raw; 11 | console.log("Performing a speed test, 1M packs()" ); 12 | 13 | var t1 = new Date(); 14 | var tot = 1e6; 15 | for(i=0;i