diff options
Diffstat (limited to 'php/msgpack.c')
| -rw-r--r-- | php/msgpack.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/php/msgpack.c b/php/msgpack.c new file mode 100644 index 0000000..b38eb18 --- /dev/null +++ b/php/msgpack.c @@ -0,0 +1,656 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Hideyuki TAKEI | + +----------------------------------------------------------------------+ +*/ + +/* $Id: header 226204 2007-01-01 19:32:10Z iliaa $ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/php_smart_str.h" +#include "php_msgpack.h" + +#define PHP_EXT_VERSION "0.01" + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + + +/* pack */ +#include "msgpack/pack_define.h" + +#define msgpack_pack_inline_func(name) \ + static inline void msgpack_pack ## name + +#define msgpack_pack_inline_func_cint(name) \ + static inline void msgpack_pack ## name + +#define msgpack_pack_user smart_str* + +#define msgpack_pack_append_buffer(user, buf, len) \ + smart_str_appendl(user, (const void*)buf, len) + +#include "msgpack/pack_template.h" + + +/* unpack */ +#include "msgpack/unpack_define.h" + +typedef struct { + int finished; + char* source; +} unpack_user; + +#define msgpack_unpack_struct(name) \ + struct template ## name + +#define msgpack_unpack_func(ret, name) \ + ret template ## name + +#define msgpack_unpack_callback(name) \ + template_callback ## name + +#define msgpack_unpack_object zval* + +#define msgpack_unpack_user unpack_user + +struct template_context; +typedef struct template_context msgpack_unpack_t; + +static void template_init(msgpack_unpack_t* u); +static msgpack_unpack_object template_data(msgpack_unpack_t* u); +static int template_execute(msgpack_unpack_t* u, + const char* data, size_t len, size_t* off); + +ZEND_BEGIN_MODULE_GLOBALS(msgpack) + msgpack_unpack_t *global_mp; +ZEND_END_MODULE_GLOBALS(msgpack) + +#ifdef ZTS +#define MSGPACK_G(v) TSRMG(msgpack_globals_id, zend_msgpack_globals *, v) +#else +#define MSGPACK_G(v) (msgpack_globals.v) +#endif + +static inline msgpack_unpack_object template_callback_root(unpack_user* u) +{ + msgpack_unpack_object data; + ALLOC_INIT_ZVAL(data); + ZVAL_NULL(data); + return data; +} + +static inline int template_callback_uint8(unpack_user* u, uint8_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, d); return 0; } + +static inline int template_callback_uint16(unpack_user* u, uint16_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, d); return 0; } + +static inline int template_callback_uint32(unpack_user* u, uint32_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, d); return 0; } + +static inline int template_callback_uint64(unpack_user* u, uint64_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, d); return 0; } + +static inline int template_callback_int8(unpack_user* u, int8_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, (long)d); return 0; } + +static inline int template_callback_int16(unpack_user* u, int16_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, (long)d); return 0; } + +static inline int template_callback_int32(unpack_user* u, int32_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, (long)d); return 0; } + +static inline int template_callback_int64(unpack_user* u, int64_t d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_LONG(*o, d); return 0; } + +static inline int template_callback_float(unpack_user* u, float d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_DOUBLE(*o, d); return 0; } + +static inline int template_callback_double(unpack_user* u, double d, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_DOUBLE(*o, d); return 0; } + +static inline int template_callback_nil(unpack_user* u, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_NULL(*o); return 0; } + +static inline int template_callback_true(unpack_user* u, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_BOOL(*o, 1); return 0; } + +static inline int template_callback_false(unpack_user* u, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); ZVAL_BOOL(*o, 0); return 0;} + +static inline int template_callback_array(unpack_user* u, unsigned int n, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); array_init(*o); return 0; } + +static inline int template_callback_array_item(unpack_user* u, msgpack_unpack_object* c, msgpack_unpack_object o) +{ add_next_index_zval(*c, o); return 0; } + +static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_unpack_object* o) +{ ALLOC_INIT_ZVAL(*o); array_init(*o); return 0; } + +static inline int template_callback_map_item(unpack_user* u, msgpack_unpack_object* c, msgpack_unpack_object k, msgpack_unpack_object v) +{ + switch(k->type) { + case IS_LONG: + add_index_zval(*c, Z_LVAL(*k), v); + break; + case IS_STRING: + add_assoc_zval_ex(*c, Z_STRVAL(*k), Z_STRLEN(*k)+1, v); + break; + default: + zend_error(E_WARNING, "[msgpack] (php_msgpack_decode) illegal offset type, skip this decoding"); + break; + } + return 0; +} + + +static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_unpack_object* o) +{ + ALLOC_INIT_ZVAL(*o); + if (l == 0) { + ZVAL_STRINGL(*o, "", 0, 1); + } else { + ZVAL_STRINGL(*o, p, l, 1); + } + return 0; +} + +#include "msgpack/unpack_template.h" + +static PHP_GINIT_FUNCTION(msgpack); + +ZEND_DECLARE_MODULE_GLOBALS(msgpack) + +/* True global resources - no need for thread safety here */ +static int le_msgpack; + +/* {{{ msgpack_functions[] + * + * Every user visible function must have an entry in msgpack_functions[]. + */ +zend_function_entry msgpack_functions[] = { + PHP_FE(msgpack_pack, NULL) + PHP_FE(msgpack_unpack, NULL) + PHP_FE(msgpack_unpack_limit, NULL) + PHP_ME(msgpack, initialize, NULL, 0) + PHP_ME(msgpack, execute, NULL, 0) + PHP_ME(msgpack, execute_limit, NULL, 0) + PHP_ME(msgpack, finished, NULL, 0) + PHP_ME(msgpack, data, NULL, 0) + {NULL, NULL, NULL} /* Must be the last line in msgpack_functions[] */ +}; +/* }}} */ + +/* {{{ msgpack_module_entry + */ +zend_module_entry msgpack_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "msgpack", + msgpack_functions, + PHP_MINIT(msgpack), + PHP_MSHUTDOWN(msgpack), + PHP_RINIT(msgpack), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(msgpack), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(msgpack), +#if ZEND_MODULE_API_NO >= 20010901 + "0.1", /* Replace with version number for your extension */ +#endif + PHP_MODULE_GLOBALS(msgpack), + PHP_GINIT(msgpack), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ + +#ifdef COMPILE_DL_MSGPACK +ZEND_GET_MODULE(msgpack) +#endif + +/* {{{ PHP_GINIT_FUNCTION */ +static PHP_GINIT_FUNCTION(msgpack) +{ + msgpack_globals->global_mp = NULL; +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(msgpack) +{ + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "MessagePack", msgpack_functions); + msgpack_ce = zend_register_internal_class(&ce TSRMLS_CC); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(msgpack) +{ + /* uncomment this line if you have INI entries + UNREGISTER_INI_ENTRIES(); + */ + if (MSGPACK_G(global_mp)) { + efree(MSGPACK_G(global_mp)); + MSGPACK_G(global_mp) = NULL; + } + + return SUCCESS; +} +/* }}} */ + +/* Remove if there's nothing to do at request start */ +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(msgpack) +{ + return SUCCESS; +} +/* }}} */ + +/* Remove if there's nothing to do at request end */ +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(msgpack) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(msgpack) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "msgpack support", "enabled"); + php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION); + php_info_print_table_row(2, "author", "Hideyuki TAKEI"); + php_info_print_table_row(2, "homepage", "http://msgpack.sourceforge.net"); + php_info_print_table_row(2, "open sourced by", "KLab inc."); + php_info_print_table_end(); +} +/* }}} */ + +PHP_MSGPACK_API int msgpack_determine_array_type(zval **val TSRMLS_DC) /* {{{ */ +{ + int i; + HashTable *myht = HASH_OF(*val); + + i = myht ? zend_hash_num_elements(myht) : 0; + if (i > 0) { + char *key; + ulong index, idx; + uint key_len; + HashPosition pos; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + idx = 0; + for (;; zend_hash_move_forward_ex(myht, &pos)) { + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if (i == HASH_KEY_NON_EXISTANT) + break; + if (i == HASH_KEY_IS_STRING) { + return 1; + } else { + if (index != idx) { + return 1; + } + } + idx++; + } + } + return 0; +} +/* }}} */ + +PHP_MSGPACK_API void msgpack_pack_array_hash(smart_str *pk, zval **val TSRMLS_DC) /* {{{ */ +{ + int i, r; + HashTable *myht; + + if(Z_TYPE_PP(val) == IS_ARRAY){ + myht = HASH_OF(*val); + r = msgpack_determine_array_type(val TSRMLS_CC); + } + else{ + myht = Z_OBJPROP_PP(val); + r = 1; + } + + i = myht ? zend_hash_num_elements(myht) : 0; + + if(r == 0){ + msgpack_pack_array(pk, i); + } + else{ + msgpack_pack_map(pk, i); + } + + if(i>0){ + char *key; + zval **data; + ulong index; + uint key_len; + HashPosition pos; + HashTable *tmp_ht; + int need_comma = 0; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + for(;; zend_hash_move_forward_ex(myht, &pos)){ + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if(i==HASH_KEY_NON_EXISTANT) + break; + if(zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS){ + tmp_ht = HASH_OF(*data); + if (tmp_ht) + tmp_ht->nApplyCount++; + + if(r==0) + php_msgpack_pack(pk, *data TSRMLS_CC); + else if(r==1){ + if(i==HASH_KEY_IS_STRING){ + if(key[0]=='\0' && Z_TYPE_PP(val)==IS_OBJECT){ + // Skip protected and private members. + if(tmp_ht) + tmp_ht->nApplyCount--; + continue; + } + msgpack_pack_raw(pk, key_len-1); + msgpack_pack_raw_body(pk, key, key_len-1); + php_msgpack_pack(pk, *data TSRMLS_CC); + } + else{ + msgpack_pack_long(pk, index); + php_msgpack_pack(pk, *data TSRMLS_CC); + } + } + + if(tmp_ht){ + tmp_ht->nApplyCount--; + } + } + } + + } +} +/* }}} */ + +PHP_MSGPACK_API void php_msgpack_pack(smart_str *pk, zval *val TSRMLS_DC) /* {{{ */ +{ + switch(Z_TYPE_P(val)){ + case IS_NULL: + msgpack_pack_nil(pk); + break; + case IS_BOOL: + if (Z_BVAL_P(val)) + msgpack_pack_true(pk); + else + msgpack_pack_false(pk); + break; + case IS_LONG: + msgpack_pack_long(pk, Z_LVAL_P(val)); + break; + case IS_DOUBLE: + { + double dbl = Z_DVAL_P(val); + if (zend_isinf(dbl) || zend_isnan(dbl)) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_pack) double %.9g does not conform to the MSGPACK spec, encoded as 0", dbl); + ZVAL_LONG(val, 0); + } + msgpack_pack_double(pk, Z_DVAL_P(val)); + } + break; + case IS_STRING: + msgpack_pack_raw(pk, Z_STRLEN_P(val)); + msgpack_pack_raw_body(pk, Z_STRVAL_P(val), Z_STRLEN_P(val)); + break; + case IS_ARRAY: + case IS_OBJECT: + msgpack_pack_array_hash(pk, &val TSRMLS_CC); + break; + defalut: + zend_error(E_WARNING, "[msgpack] (php_msgpack_pack) type is unsupported, encoded as null"); + msgpack_pack_nil(pk); + break; + } + + return; +} +/* }}} */ + +PHP_MSGPACK_API void php_msgpack_unpack_limit(zval *return_value, const char *buf, int len, zend_bool assoc TSRMLS_DC) /* {{{ */ +{ + if (len<=0) { + RETURN_NUL(); + } + + msgpack_unpack_t mp; + template_init(&mp); + unpack_user u = {0, ""}; + + size_t from = 0; + char* dptr = (char*)buf; + long dlen = len; + int ret; + + (&mp)->user.source = (char*)buf; + ret = template_execute(&mp, dptr, (size_t)dlen, &from); + (&mp)->user.source = ""; + + if(ret < 0) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpack) parse error"); + } else if(ret == 0) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpack) insufficient bytes"); + } else { + if(from < dlen) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpack) extra bytes"); + } + + *return_value = *template_data(&mp); + FREE_ZVAL(template_data(&mp)); + } +} +/* }}} */ + + +PHP_FUNCTION(msgpack_pack) +{ + zval *parameter; + smart_str buf = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶meter) == FAILURE) { + return; + } + + php_msgpack_pack(&buf, parameter TSRMLS_CC); + + ZVAL_STRINGL(return_value, buf.c, buf.len, 1); + + smart_str_free(&buf); +} + +PHP_FUNCTION(msgpack_unpack) +{ + char *parameter; + int parameter_len; + zend_bool assoc = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", + ¶meter, ¶meter_len, &assoc) == FAILURE) { + return; + } + + if (!parameter_len) { + RETURN_NULL(); + } + + php_msgpack_unpack_limit(return_value, parameter, parameter_len, assoc TSRMLS_CC); +} + +PHP_FUNCTION(msgpack_unpack_limit) +{ + char *parameter; + int parameter_len; + int limit; + zend_bool assoc = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|b", + ¶meter, ¶meter_len, &limit, &assoc) == FAILURE) { + return; + } + + if (!parameter_len) { + RETURN_NULL(); + } + else if (parameter_len < limit) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpack) limit greater than data_len"); + limit = parameter_len; + } + + php_msgpack_unpack_limit(return_value, parameter, limit, assoc TSRMLS_CC); +} + + +PHP_MSGPACK_API void php_msgpack_unpacker_execute_limit(zval *return_value, const char *buf, int off, int len, zend_bool assoc TSRMLS_DC) /* {{{ */ +{ + if (len<=0) { + RETURN_NUL(); + } + + size_t from = off; + char* dptr = (char*)buf; + long dlen = len; + int ret; + + if(from >= dlen) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpacker_execute_limit) offset is bigger than data buffer size"); + } + + MSGPACK_G(global_mp)->user.source = (char*)buf; + ret = template_execute(MSGPACK_G(global_mp), dptr, (size_t)dlen, &from); + MSGPACK_G(global_mp)->user.source = ""; + + if(ret < 0) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpacker_execute_limit) parse error"); + } else if(ret > 0) { + MSGPACK_G(global_mp)->user.finished = 1; + RETVAL_LONG(from); + } else { + MSGPACK_G(global_mp)->user.finished = 0; + RETVAL_LONG(from); + } +} +/* }}} */ + +PHP_MSGPACK_API void php_msgpack_unpacker_reset(TSRMLS_D) /* {{{ */ +{ + if(MSGPACK_G(global_mp)) { + efree(MSGPACK_G(global_mp)); + MSGPACK_G(global_mp) = NULL; + } + MSGPACK_G(global_mp) = safe_emalloc(sizeof(msgpack_unpack_t), 1, 0); + + template_init(MSGPACK_G(global_mp)); + unpack_user u = {0, ""}; + MSGPACK_G(global_mp)->user = u; + return; +} +/* }}} */ + +PHP_METHOD(msgpack, initialize) +{ + php_msgpack_unpacker_reset(TSRMLS_C); + return; +} + +PHP_METHOD(msgpack, execute) +{ + char *data; + int off; + int data_len; + zend_bool assoc = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|b", + &data, &data_len, &off, &assoc) == FAILURE) { + return; + } + + if (!data_len) { + RETURN_NULL(); + } + + php_msgpack_unpacker_execute_limit(return_value, data, off, data_len, assoc TSRMLS_CC); +} + +PHP_METHOD(msgpack, execute_limit) +{ + char *data; + int off; + int data_len; + int limit; + zend_bool assoc = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", + &data, &data_len, &off, &limit, &assoc) == FAILURE) { + return; + } + + if (!data_len) { + RETURN_NULL(); + } + else if (data_len < limit) { + zend_error(E_WARNING, "[msgpack] (php_msgpack_unpack) limit greater than (data+off)_len"); + limit = data_len; + } + + php_msgpack_unpacker_execute_limit(return_value, data, off, limit, assoc TSRMLS_CC); +} + +PHP_METHOD(msgpack, finished) +{ + if(MSGPACK_G(global_mp)->user.finished == 1) { + RETURN_TRUE; + } + RETURN_FALSE; +} + +PHP_METHOD(msgpack, data) +{ + *return_value = *template_data(MSGPACK_G(global_mp)); + return; +} + +/* + * 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 + */ |
