📄 pack.c
字号:
} efree(argv); efree(formatcodes); efree(formatargs); output[outputpos] = '\0'; RETVAL_STRINGL(output, outputpos, 1); efree(output);}/* }}} *//* {{{ php_unpack */static long php_unpack(char *data, int size, int issigned, int *map){ long result; char *cresult = (char *) &result; int i; result = issigned ? -1 : 0; for (i = 0; i < size; i++) { cresult[map[i]] = *data++; } return result;}/* }}} *//* unpack() is based on Perl's unpack(), but is modified a bit from there. * Rather than depending on error-prone ordered lists or syntactically * unpleasant pass-by-reference, we return an object with named paramters * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the * formatter char (like pack()), "[repeat]" is the optional repeater argument, * and "name" is the name of the variable to use. * Example: "c2chars/nints" will return an object with fields * chars1, chars2, and ints. * Numeric pack types will return numbers, a and A will return strings, * f and d will return doubles. * Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @. *//* {{{ proto array unpack(string format, string input) Unpack binary string into named array elements according to format argument */PHP_FUNCTION(unpack){ zval **formatarg; zval **inputarg; char *format; char *input; int formatlen; int inputpos, inputlen; int i; if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &formatarg, &inputarg) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string_ex(formatarg); convert_to_string_ex(inputarg); format = Z_STRVAL_PP(formatarg); formatlen = Z_STRLEN_PP(formatarg); input = Z_STRVAL_PP(inputarg); inputlen = Z_STRLEN_PP(inputarg); inputpos = 0; array_init(return_value); while (formatlen-- > 0) { char type = *(format++); char c; int arg = 1, argb; char *name; int namelen; int size=0; /* Handle format arguments if any */ if (formatlen > 0) { c = *format; if (c >= '0' && c <= '9') { arg = atoi(format); while (formatlen > 0 && *format >= '0' && *format <= '9') { format++; formatlen--; } } else if (c == '*') { arg = -1; format++; formatlen--; } } /* Get of new value in array */ name = format; argb = arg; while (formatlen > 0 && *format != '/') { formatlen--; format++; } namelen = format - name; if (namelen > 200) namelen = 200; switch ((int) type) { /* Never use any input */ case 'X': size = -1; break; case '@': size = 0; break; case 'a': case 'A': size = arg; arg = 1; break; case 'h': case 'H': size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg; arg = 1; break; /* Use 1 byte of input */ case 'c': case 'C': case 'x': size = 1; break; /* Use 2 bytes of input */ case 's': case 'S': case 'n': case 'v': size = 2; break; /* Use sizeof(int) bytes of input */ case 'i': case 'I': size = sizeof(int); break; /* Use 4 bytes of input */ case 'l': case 'L': case 'N': case 'V': size = 4; break; /* Use sizeof(float) bytes of input */ case 'f': size = sizeof(float); break; /* Use sizeof(double) bytes of input */ case 'd': size = sizeof(double); break; } /* Do actual unpacking */ for (i = 0; i != arg; i++ ) { /* Space for name + number, safe as namelen is ensured <= 200 */ char n[256]; if (arg != 1 || namelen == 0) { /* Need to add element number to name */ sprintf(n, "%.*s%d", namelen, name, i + 1); } else { /* Truncate name to next format code or end of string */ sprintf(n, "%.*s", namelen, name); } if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: integer overflow", type); inputpos = 0; } if ((inputpos + size) <= inputlen) { switch ((int) type) { case 'a': case 'A': { char pad = (type == 'a') ? '\0' : ' '; int len = inputlen - inputpos; /* Remaining string */ /* If size was given take minimum of len and size */ if ((size >= 0) && (len > size)) { len = size; } size = len; /* Remove padding chars from unpacked data */ while (--len >= 0) { if (input[inputpos + len] != pad) break; } add_assoc_stringl(return_value, n, &input[inputpos], len + 1, 1); break; } case 'h': case 'H': { int len = (inputlen - inputpos) * 2; /* Remaining */ int nibbleshift = (type == 'h') ? 0 : 4; int first = 1; char *buf; int ipos, opos; /* If size was given take minimum of len and size */ if (size >= 0 && len > (size * 2)) { len = size * 2; } if (argb > 0) { len -= argb % 2; } buf = emalloc(len + 1); for (ipos = opos = 0; opos < len; opos++) { char c = (input[inputpos + ipos] >> nibbleshift) & 0xf; if (c < 10) { c += '0'; } else { c += 'a' - 10; } buf[opos] = c; nibbleshift = (nibbleshift + 4) & 7; if (first-- == 0) { ipos++; first = 1; } } buf[len] = '\0'; add_assoc_stringl(return_value, n, buf, len, 1); efree(buf); break; } case 'c': case 'C': { int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0; long v = php_unpack(&input[inputpos], 1, issigned, byte_map); add_assoc_long(return_value, n, v); break; } case 's': case 'S': case 'n': case 'v': { long v; int issigned = 0; int *map = machine_endian_short_map; if (type == 's') { issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80; } else if (type == 'n') { map = big_endian_short_map; } else if (type == 'v') { map = little_endian_short_map; } v = php_unpack(&input[inputpos], 2, issigned, map); add_assoc_long(return_value, n, v); break; } case 'i': case 'I': { long v; int issigned = 0; if (type == 'i') { issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80; } v = php_unpack(&input[inputpos], sizeof(int), issigned, int_map); add_assoc_long(return_value, n, v); break; } case 'l': case 'L': case 'N': case 'V': { int issigned = 0; int *map = machine_endian_long_map; long v; if (type == 'l') { issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80; } else if (type == 'N') { map = big_endian_long_map; } else if (type == 'V') { map = little_endian_long_map; } v = php_unpack(&input[inputpos], 4, issigned, map); add_assoc_long(return_value, n, v); break; } case 'f': { float v; memcpy(&v, &input[inputpos], sizeof(float)); add_assoc_double(return_value, n, (double)v); break; } case 'd': { double v; memcpy(&v, &input[inputpos], sizeof(double)); add_assoc_double(return_value, n, v); break; } case 'x': /* Do nothing with input, just skip it */ break; case 'X': if (inputpos < size) { inputpos = -size; i = arg - 1; /* Break out of for loop */ if (arg >= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type); } } break; case '@': if (arg <= inputlen) { inputpos = arg; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type); } i = arg - 1; /* Done, break out of for loop */ break; } inputpos += size; if (inputpos < 0) { if (size != -1) { /* only print warning if not working with * */ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type); } inputpos = 0; } } else if (arg < 0) { /* Reached end of input for '*' repeater */ break; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough input, need %d, have %d", type, size, inputlen - inputpos); zval_dtor(return_value); RETURN_FALSE; } } formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */ format++; }}/* }}} *//* {{{ PHP_MINIT_FUNCTION */PHP_MINIT_FUNCTION(pack){ int machine_endian_check = 1; int i; machine_little_endian = ((char *)&machine_endian_check)[0]; if (machine_little_endian) { /* Where to get lo to hi bytes from */ byte_map[0] = 0; for (i = 0; i < sizeof(int); i++) { int_map[i] = i; } machine_endian_short_map[0] = 0; machine_endian_short_map[1] = 1; big_endian_short_map[0] = 1; big_endian_short_map[1] = 0; little_endian_short_map[0] = 0; little_endian_short_map[1] = 1; machine_endian_long_map[0] = 0; machine_endian_long_map[1] = 1; machine_endian_long_map[2] = 2; machine_endian_long_map[3] = 3; big_endian_long_map[0] = 3; big_endian_long_map[1] = 2; big_endian_long_map[2] = 1; big_endian_long_map[3] = 0; little_endian_long_map[0] = 0; little_endian_long_map[1] = 1; little_endian_long_map[2] = 2; little_endian_long_map[3] = 3; } else { zval val; int size = sizeof(Z_LVAL(val)); Z_LVAL(val)=0; /*silence a warning*/ /* Where to get hi to lo bytes from */ byte_map[0] = size - 1; for (i = 0; i < sizeof(int); i++) { int_map[i] = size - (sizeof(int) - i); } machine_endian_short_map[0] = size - 2; machine_endian_short_map[1] = size - 1; big_endian_short_map[0] = size - 2; big_endian_short_map[1] = size - 1; little_endian_short_map[0] = size - 1; little_endian_short_map[1] = size - 2; machine_endian_long_map[0] = size - 4; machine_endian_long_map[1] = size - 3; machine_endian_long_map[2] = size - 2; machine_endian_long_map[3] = size - 1; big_endian_long_map[0] = size - 4; big_endian_long_map[1] = size - 3; big_endian_long_map[2] = size - 2; big_endian_long_map[3] = size - 1; little_endian_long_map[0] = size - 1; little_endian_long_map[1] = size - 2; little_endian_long_map[2] = size - 3; little_endian_long_map[3] = size - 4; } return SUCCESS;}/* }}} *//* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -