array.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. #include <ext/spl/spl_iterators.h>
  31. #include <Zend/zend_API.h>
  32. #include <Zend/zend_interfaces.h>
  33. #include "protobuf.h"
  34. ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
  35. ZEND_ARG_INFO(0, index)
  36. ZEND_END_ARG_INFO()
  37. ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
  38. ZEND_ARG_INFO(0, index)
  39. ZEND_ARG_INFO(0, newval)
  40. ZEND_END_ARG_INFO()
  41. ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
  42. ZEND_END_ARG_INFO()
  43. static zend_function_entry repeated_field_methods[] = {
  44. PHP_ME(RepeatedField, __construct, NULL, ZEND_ACC_PUBLIC)
  45. PHP_ME(RepeatedField, append, NULL, ZEND_ACC_PUBLIC)
  46. PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
  47. PHP_ME(RepeatedField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC)
  48. PHP_ME(RepeatedField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC)
  49. PHP_ME(RepeatedField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC)
  50. PHP_ME(RepeatedField, count, arginfo_void, ZEND_ACC_PUBLIC)
  51. PHP_ME(RepeatedField, getIterator, arginfo_void, ZEND_ACC_PUBLIC)
  52. ZEND_FE_END
  53. };
  54. static zend_function_entry repeated_field_iter_methods[] = {
  55. PHP_ME(RepeatedFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC)
  56. PHP_ME(RepeatedFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC)
  57. PHP_ME(RepeatedFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC)
  58. PHP_ME(RepeatedFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC)
  59. PHP_ME(RepeatedFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC)
  60. ZEND_FE_END
  61. };
  62. // Forward declare static functions.
  63. static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC);
  64. static void repeated_field_free(void *object TSRMLS_DC);
  65. static int repeated_field_array_init(zval *array, upb_fieldtype_t type,
  66. uint size ZEND_FILE_LINE_DC);
  67. static void repeated_field_free_element(void *object);
  68. static void repeated_field_write_dimension(zval *object, zval *offset,
  69. zval *value TSRMLS_DC);
  70. static int repeated_field_has_dimension(zval *object, zval *offset TSRMLS_DC);
  71. static HashTable *repeated_field_get_gc(zval *object, zval ***table,
  72. int *n TSRMLS_DC);
  73. static zend_object_value repeated_field_iter_create(zend_class_entry *ce TSRMLS_DC);
  74. static void repeated_field_iter_free(void *object TSRMLS_DC);
  75. // -----------------------------------------------------------------------------
  76. // RepeatedField creation/desctruction
  77. // -----------------------------------------------------------------------------
  78. zend_class_entry* repeated_field_type;
  79. zend_class_entry* repeated_field_iter_type;
  80. zend_object_handlers* repeated_field_handlers;
  81. void repeated_field_init(TSRMLS_D) {
  82. zend_class_entry class_type;
  83. const char* class_name = "Google\\Protobuf\\Internal\\RepeatedField";
  84. INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name),
  85. repeated_field_methods);
  86. repeated_field_type = zend_register_internal_class(&class_type TSRMLS_CC);
  87. repeated_field_type->create_object = repeated_field_create;
  88. zend_class_implements(repeated_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess,
  89. zend_ce_aggregate, spl_ce_Countable);
  90. repeated_field_handlers = PEMALLOC(zend_object_handlers);
  91. memcpy(repeated_field_handlers, zend_get_std_object_handlers(),
  92. sizeof(zend_object_handlers));
  93. repeated_field_handlers->write_dimension = repeated_field_write_dimension;
  94. repeated_field_handlers->get_gc = repeated_field_get_gc;
  95. }
  96. static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC) {
  97. zend_object_value retval = {0};
  98. RepeatedField *intern;
  99. intern = emalloc(sizeof(RepeatedField));
  100. memset(intern, 0, sizeof(RepeatedField));
  101. zend_object_std_init(&intern->std, ce TSRMLS_CC);
  102. object_properties_init(&intern->std, ce);
  103. intern->array = NULL;
  104. intern->type = 0;
  105. intern->msg_ce = NULL;
  106. retval.handle = zend_objects_store_put(
  107. intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
  108. (zend_objects_free_object_storage_t)repeated_field_free, NULL TSRMLS_CC);
  109. retval.handlers = repeated_field_handlers;
  110. return retval;
  111. }
  112. static void repeated_field_free(void *object TSRMLS_DC) {
  113. RepeatedField *intern = object;
  114. zend_object_std_dtor(&intern->std TSRMLS_CC);
  115. zval_ptr_dtor(&intern->array);
  116. efree(object);
  117. }
  118. static int repeated_field_array_init(zval *array, upb_fieldtype_t type,
  119. uint size ZEND_FILE_LINE_DC) {
  120. ALLOC_HASHTABLE(Z_ARRVAL_P(array));
  121. switch (type) {
  122. case UPB_TYPE_STRING:
  123. case UPB_TYPE_BYTES:
  124. case UPB_TYPE_MESSAGE:
  125. zend_hash_init(Z_ARRVAL_P(array), size, NULL, ZVAL_PTR_DTOR, 0);
  126. break;
  127. default:
  128. zend_hash_init(Z_ARRVAL_P(array), size, NULL, repeated_field_free_element,
  129. 0);
  130. }
  131. Z_TYPE_P(array) = IS_ARRAY;
  132. return SUCCESS;
  133. }
  134. static void repeated_field_free_element(void *object) {
  135. }
  136. // -----------------------------------------------------------------------------
  137. // RepeatedField Handlers
  138. // -----------------------------------------------------------------------------
  139. static void repeated_field_write_dimension(zval *object, zval *offset,
  140. zval *value TSRMLS_DC) {
  141. uint64_t index;
  142. RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC);
  143. HashTable *ht = HASH_OF(intern->array);
  144. int size = native_slot_size(intern->type);
  145. unsigned char memory[NATIVE_SLOT_MAX_SIZE];
  146. memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
  147. if (!native_slot_set(intern->type, intern->msg_ce, memory, value TSRMLS_CC)) {
  148. return;
  149. }
  150. if (!offset || Z_TYPE_P(offset) == IS_NULL) {
  151. index = zend_hash_num_elements(HASH_OF(intern->array));
  152. } else {
  153. if (protobuf_convert_to_uint64(offset, &index)) {
  154. if (!zend_hash_index_exists(ht, index)) {
  155. zend_error(E_USER_ERROR, "Element at %llu doesn't exist.\n", index);
  156. return;
  157. }
  158. } else {
  159. return;
  160. }
  161. }
  162. zend_hash_index_update(ht, index, memory, size, NULL);
  163. }
  164. static HashTable *repeated_field_get_gc(zval *object, zval ***table,
  165. int *n TSRMLS_DC) {
  166. *table = NULL;
  167. *n = 0;
  168. RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC);
  169. return HASH_OF(intern->array);
  170. }
  171. // -----------------------------------------------------------------------------
  172. // C RepeatedField Utilities
  173. // -----------------------------------------------------------------------------
  174. void *repeated_field_index_native(RepeatedField *intern, int index TSRMLS_DC) {
  175. HashTable *ht = HASH_OF(intern->array);
  176. void *value;
  177. if (zend_hash_index_find(ht, index, (void **)&value) == FAILURE) {
  178. zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
  179. return NULL;
  180. }
  181. return value;
  182. }
  183. void repeated_field_push_native(RepeatedField *intern, void *value TSRMLS_DC) {
  184. HashTable *ht = HASH_OF(intern->array);
  185. int size = native_slot_size(intern->type);
  186. zend_hash_next_index_insert(ht, (void **)value, size, NULL);
  187. }
  188. void repeated_field_create_with_type(zend_class_entry *ce,
  189. const upb_fielddef *field,
  190. zval **repeated_field TSRMLS_DC) {
  191. MAKE_STD_ZVAL(*repeated_field);
  192. Z_TYPE_PP(repeated_field) = IS_OBJECT;
  193. Z_OBJVAL_PP(repeated_field) =
  194. repeated_field_type->create_object(repeated_field_type TSRMLS_CC);
  195. RepeatedField *intern =
  196. zend_object_store_get_object(*repeated_field TSRMLS_CC);
  197. intern->type = upb_fielddef_type(field);
  198. if (intern->type == UPB_TYPE_MESSAGE) {
  199. const upb_msgdef *msg = upb_fielddef_msgsubdef(field);
  200. zval *desc_php = get_def_obj(msg);
  201. Descriptor *desc = zend_object_store_get_object(desc_php TSRMLS_CC);
  202. intern->msg_ce = desc->klass;
  203. }
  204. MAKE_STD_ZVAL(intern->array);
  205. repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC);
  206. // TODO(teboring): Link class entry for message and enum
  207. }
  208. // -----------------------------------------------------------------------------
  209. // PHP RepeatedField Methods
  210. // -----------------------------------------------------------------------------
  211. /**
  212. * Constructs an instance of RepeatedField.
  213. * @param long Type of the stored element.
  214. * @param string Message/Enum class name (message/enum fields only).
  215. */
  216. PHP_METHOD(RepeatedField, __construct) {
  217. long type;
  218. zend_class_entry* klass = NULL;
  219. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|C", &type, &klass) ==
  220. FAILURE) {
  221. return;
  222. }
  223. RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  224. intern->type = to_fieldtype(type);
  225. intern->msg_ce = klass;
  226. MAKE_STD_ZVAL(intern->array);
  227. repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC);
  228. if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) {
  229. zend_error(E_USER_ERROR, "Message type must have concrete class.");
  230. return;
  231. }
  232. // TODO(teboring): Consider enum.
  233. }
  234. /**
  235. * Append element to the end of the repeated field.
  236. * @param object The element to be added.
  237. */
  238. PHP_METHOD(RepeatedField, append) {
  239. zval *value;
  240. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) ==
  241. FAILURE) {
  242. return;
  243. }
  244. repeated_field_write_dimension(getThis(), NULL, value TSRMLS_CC);
  245. }
  246. /**
  247. * Check whether the element at given index exists.
  248. * @param long The index to be checked.
  249. * @return bool True if the element at the given index exists.
  250. */
  251. PHP_METHOD(RepeatedField, offsetExists) {
  252. long index;
  253. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
  254. FAILURE) {
  255. return;
  256. }
  257. RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  258. RETURN_BOOL(index >= 0 &&
  259. index < zend_hash_num_elements(HASH_OF(intern->array)));
  260. }
  261. /**
  262. * Return the element at the given index.
  263. * This will also be called for: $ele = $arr[0]
  264. * @param long The index of the element to be fetched.
  265. * @return object The stored element at given index.
  266. * @exception Invalid type for index.
  267. * @exception Non-existing index.
  268. */
  269. PHP_METHOD(RepeatedField, offsetGet) {
  270. long index;
  271. void *memory;
  272. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
  273. FAILURE) {
  274. return;
  275. }
  276. RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  277. HashTable *table = HASH_OF(intern->array);
  278. if (zend_hash_index_find(table, index, (void **)&memory) == FAILURE) {
  279. zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index);
  280. return;
  281. }
  282. native_slot_get(intern->type, memory, return_value_ptr TSRMLS_CC);
  283. }
  284. /**
  285. * Assign the element at the given index.
  286. * This will also be called for: $arr []= $ele and $arr[0] = ele
  287. * @param long The index of the element to be assigned.
  288. * @param object The element to be assigned.
  289. * @exception Invalid type for index.
  290. * @exception Non-existing index.
  291. * @exception Incorrect type of the element.
  292. */
  293. PHP_METHOD(RepeatedField, offsetSet) {
  294. zval *index, *value;
  295. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
  296. FAILURE) {
  297. return;
  298. }
  299. repeated_field_write_dimension(getThis(), index, value TSRMLS_CC);
  300. }
  301. /**
  302. * Remove the element at the given index.
  303. * This will also be called for: unset($arr)
  304. * @param long The index of the element to be removed.
  305. * @exception Invalid type for index.
  306. * @exception The element to be removed is not at the end of the RepeatedField.
  307. */
  308. PHP_METHOD(RepeatedField, offsetUnset) {
  309. long index;
  310. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
  311. FAILURE) {
  312. return;
  313. }
  314. RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  315. // Only the element at the end of the array can be removed.
  316. if (index == -1 ||
  317. index != (zend_hash_num_elements(HASH_OF(intern->array)) - 1)) {
  318. zend_error(E_USER_ERROR, "Cannot remove element at %ld.\n", index);
  319. return;
  320. }
  321. zend_hash_index_del(HASH_OF(intern->array), index);
  322. }
  323. /**
  324. * Return the number of stored elements.
  325. * This will also be called for: count($arr)
  326. * @return long The number of stored elements.
  327. */
  328. PHP_METHOD(RepeatedField, count) {
  329. RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  330. if (zend_parse_parameters_none() == FAILURE) {
  331. return;
  332. }
  333. RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->array)));
  334. }
  335. /**
  336. * Return the beginning iterator.
  337. * This will also be called for: foreach($arr)
  338. * @return object Beginning iterator.
  339. */
  340. PHP_METHOD(RepeatedField, getIterator) {
  341. zval *iter_php = NULL;
  342. MAKE_STD_ZVAL(iter_php);
  343. Z_TYPE_P(iter_php) = IS_OBJECT;
  344. Z_OBJVAL_P(iter_php) = repeated_field_iter_type->create_object(
  345. repeated_field_iter_type TSRMLS_CC);
  346. RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  347. RepeatedFieldIter *iter = zend_object_store_get_object(iter_php TSRMLS_CC);
  348. iter->repeated_field = intern;
  349. iter->position = 0;
  350. RETURN_ZVAL(iter_php, 1, 1);
  351. }
  352. // -----------------------------------------------------------------------------
  353. // RepeatedFieldIter creation/desctruction
  354. // -----------------------------------------------------------------------------
  355. void repeated_field_iter_init(TSRMLS_D) {
  356. zend_class_entry class_type;
  357. const char* class_name = "Google\\Protobuf\\Internal\\RepeatedFieldIter";
  358. INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name),
  359. repeated_field_iter_methods);
  360. repeated_field_iter_type =
  361. zend_register_internal_class(&class_type TSRMLS_CC);
  362. repeated_field_iter_type->create_object = repeated_field_iter_create;
  363. zend_class_implements(repeated_field_iter_type TSRMLS_CC, 1,
  364. zend_ce_iterator);
  365. }
  366. static zend_object_value repeated_field_iter_create(
  367. zend_class_entry *ce TSRMLS_DC) {
  368. zend_object_value retval = {0};
  369. RepeatedFieldIter *intern;
  370. intern = emalloc(sizeof(RepeatedFieldIter));
  371. memset(intern, 0, sizeof(RepeatedFieldIter));
  372. zend_object_std_init(&intern->std, ce TSRMLS_CC);
  373. object_properties_init(&intern->std, ce);
  374. intern->repeated_field = NULL;
  375. intern->position = 0;
  376. retval.handle = zend_objects_store_put(
  377. intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
  378. (zend_objects_free_object_storage_t)repeated_field_iter_free,
  379. NULL TSRMLS_CC);
  380. retval.handlers = zend_get_std_object_handlers();
  381. return retval;
  382. }
  383. static void repeated_field_iter_free(void *object TSRMLS_DC) {
  384. RepeatedFieldIter *intern = object;
  385. zend_object_std_dtor(&intern->std TSRMLS_CC);
  386. efree(object);
  387. }
  388. // -----------------------------------------------------------------------------
  389. // PHP RepeatedFieldIter Methods
  390. // -----------------------------------------------------------------------------
  391. PHP_METHOD(RepeatedFieldIter, rewind) {
  392. RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  393. intern->position = 0;
  394. }
  395. PHP_METHOD(RepeatedFieldIter, current) {
  396. RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  397. RepeatedField *repeated_field = intern->repeated_field;
  398. long index;
  399. void *memory;
  400. HashTable *table = HASH_OF(repeated_field->array);
  401. if (zend_hash_index_find(table, intern->position, (void **)&memory) ==
  402. FAILURE) {
  403. zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index);
  404. return;
  405. }
  406. native_slot_get(repeated_field->type, memory, return_value_ptr TSRMLS_CC);
  407. }
  408. PHP_METHOD(RepeatedFieldIter, key) {
  409. RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  410. RETURN_LONG(intern->position);
  411. }
  412. PHP_METHOD(RepeatedFieldIter, next) {
  413. RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  414. ++intern->position;
  415. }
  416. PHP_METHOD(RepeatedFieldIter, valid) {
  417. RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
  418. RETURN_BOOL(zend_hash_num_elements(HASH_OF(intern->repeated_field->array)) >
  419. intern->position);
  420. }