OLD | NEW |
| (Empty) |
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 | |
31 #include <ext/spl/spl_iterators.h> | |
32 #include <Zend/zend_API.h> | |
33 #include <Zend/zend_interfaces.h> | |
34 | |
35 #include "protobuf.h" | |
36 | |
37 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1) | |
38 ZEND_ARG_INFO(0, index) | |
39 ZEND_END_ARG_INFO() | |
40 | |
41 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2) | |
42 ZEND_ARG_INFO(0, index) | |
43 ZEND_ARG_INFO(0, newval) | |
44 ZEND_END_ARG_INFO() | |
45 | |
46 ZEND_BEGIN_ARG_INFO(arginfo_void, 0) | |
47 ZEND_END_ARG_INFO() | |
48 | |
49 static zend_function_entry repeated_field_methods[] = { | |
50 PHP_ME(RepeatedField, __construct, NULL, ZEND_ACC_PUBLIC) | |
51 PHP_ME(RepeatedField, append, NULL, ZEND_ACC_PUBLIC) | |
52 PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC) | |
53 PHP_ME(RepeatedField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC) | |
54 PHP_ME(RepeatedField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC) | |
55 PHP_ME(RepeatedField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC) | |
56 PHP_ME(RepeatedField, count, arginfo_void, ZEND_ACC_PUBLIC) | |
57 PHP_ME(RepeatedField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) | |
58 ZEND_FE_END | |
59 }; | |
60 | |
61 static zend_function_entry repeated_field_iter_methods[] = { | |
62 PHP_ME(RepeatedFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) | |
63 PHP_ME(RepeatedFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) | |
64 PHP_ME(RepeatedFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) | |
65 PHP_ME(RepeatedFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) | |
66 PHP_ME(RepeatedFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) | |
67 ZEND_FE_END | |
68 }; | |
69 | |
70 // Forward declare static functions. | |
71 | |
72 static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC); | |
73 static void repeated_field_free(void *object TSRMLS_DC); | |
74 static int repeated_field_array_init(zval *array, upb_fieldtype_t type, | |
75 uint size ZEND_FILE_LINE_DC); | |
76 static void repeated_field_free_element(void *object); | |
77 static void repeated_field_write_dimension(zval *object, zval *offset, | |
78 zval *value TSRMLS_DC); | |
79 static int repeated_field_has_dimension(zval *object, zval *offset TSRMLS_DC); | |
80 static HashTable *repeated_field_get_gc(zval *object, zval ***table, | |
81 int *n TSRMLS_DC); | |
82 | |
83 static zend_object_value repeated_field_iter_create(zend_class_entry *ce TSRMLS_
DC); | |
84 static void repeated_field_iter_free(void *object TSRMLS_DC); | |
85 | |
86 // ----------------------------------------------------------------------------- | |
87 // RepeatedField creation/desctruction | |
88 // ----------------------------------------------------------------------------- | |
89 | |
90 zend_class_entry* repeated_field_type; | |
91 zend_class_entry* repeated_field_iter_type; | |
92 zend_object_handlers* repeated_field_handlers; | |
93 | |
94 void repeated_field_init(TSRMLS_D) { | |
95 zend_class_entry class_type; | |
96 const char* class_name = "Google\\Protobuf\\Internal\\RepeatedField"; | |
97 INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name), | |
98 repeated_field_methods); | |
99 | |
100 repeated_field_type = zend_register_internal_class(&class_type TSRMLS_CC); | |
101 repeated_field_type->create_object = repeated_field_create; | |
102 | |
103 zend_class_implements(repeated_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess, | |
104 zend_ce_aggregate, spl_ce_Countable); | |
105 | |
106 repeated_field_handlers = PEMALLOC(zend_object_handlers); | |
107 memcpy(repeated_field_handlers, zend_get_std_object_handlers(), | |
108 sizeof(zend_object_handlers)); | |
109 repeated_field_handlers->get_gc = repeated_field_get_gc; | |
110 } | |
111 | |
112 static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC) { | |
113 zend_object_value retval = {0}; | |
114 RepeatedField *intern; | |
115 | |
116 intern = emalloc(sizeof(RepeatedField)); | |
117 memset(intern, 0, sizeof(RepeatedField)); | |
118 | |
119 zend_object_std_init(&intern->std, ce TSRMLS_CC); | |
120 object_properties_init(&intern->std, ce); | |
121 | |
122 intern->array = NULL; | |
123 intern->type = 0; | |
124 intern->msg_ce = NULL; | |
125 | |
126 retval.handle = zend_objects_store_put( | |
127 intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, | |
128 (zend_objects_free_object_storage_t)repeated_field_free, NULL TSRMLS_CC); | |
129 retval.handlers = repeated_field_handlers; | |
130 | |
131 return retval; | |
132 } | |
133 | |
134 static void repeated_field_free(void *object TSRMLS_DC) { | |
135 RepeatedField *intern = object; | |
136 zend_object_std_dtor(&intern->std TSRMLS_CC); | |
137 zval_ptr_dtor(&intern->array); | |
138 efree(object); | |
139 } | |
140 | |
141 static int repeated_field_array_init(zval *array, upb_fieldtype_t type, | |
142 uint size ZEND_FILE_LINE_DC) { | |
143 ALLOC_HASHTABLE(Z_ARRVAL_P(array)); | |
144 | |
145 switch (type) { | |
146 case UPB_TYPE_STRING: | |
147 case UPB_TYPE_BYTES: | |
148 case UPB_TYPE_MESSAGE: | |
149 zend_hash_init(Z_ARRVAL_P(array), size, NULL, ZVAL_PTR_DTOR, 0); | |
150 break; | |
151 default: | |
152 zend_hash_init(Z_ARRVAL_P(array), size, NULL, repeated_field_free_element, | |
153 0); | |
154 } | |
155 Z_TYPE_P(array) = IS_ARRAY; | |
156 return SUCCESS; | |
157 } | |
158 | |
159 static void repeated_field_free_element(void *object) { | |
160 } | |
161 | |
162 // ----------------------------------------------------------------------------- | |
163 // RepeatedField Handlers | |
164 // ----------------------------------------------------------------------------- | |
165 | |
166 static void repeated_field_write_dimension(zval *object, zval *offset, | |
167 zval *value TSRMLS_DC) { | |
168 uint64_t index; | |
169 | |
170 RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC); | |
171 HashTable *ht = HASH_OF(intern->array); | |
172 int size = native_slot_size(intern->type); | |
173 | |
174 unsigned char memory[NATIVE_SLOT_MAX_SIZE]; | |
175 memset(memory, 0, NATIVE_SLOT_MAX_SIZE); | |
176 | |
177 if (!native_slot_set(intern->type, intern->msg_ce, memory, value TSRMLS_CC)) { | |
178 return; | |
179 } | |
180 | |
181 if (!offset || Z_TYPE_P(offset) == IS_NULL) { | |
182 index = zend_hash_num_elements(HASH_OF(intern->array)); | |
183 } else { | |
184 if (protobuf_convert_to_uint64(offset, &index)) { | |
185 if (!zend_hash_index_exists(ht, index)) { | |
186 zend_error(E_USER_ERROR, "Element at %llu doesn't exist.\n", index); | |
187 return; | |
188 } | |
189 } else { | |
190 return; | |
191 } | |
192 } | |
193 | |
194 zend_hash_index_update(ht, index, memory, size, NULL); | |
195 } | |
196 | |
197 static HashTable *repeated_field_get_gc(zval *object, zval ***table, | |
198 int *n TSRMLS_DC) { | |
199 *table = NULL; | |
200 *n = 0; | |
201 RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC); | |
202 return HASH_OF(intern->array); | |
203 } | |
204 | |
205 // ----------------------------------------------------------------------------- | |
206 // C RepeatedField Utilities | |
207 // ----------------------------------------------------------------------------- | |
208 | |
209 void *repeated_field_index_native(RepeatedField *intern, int index TSRMLS_DC) { | |
210 HashTable *ht = HASH_OF(intern->array); | |
211 void *value; | |
212 | |
213 if (zend_hash_index_find(ht, index, (void **)&value) == FAILURE) { | |
214 zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index); | |
215 return NULL; | |
216 } | |
217 | |
218 return value; | |
219 } | |
220 | |
221 void repeated_field_push_native(RepeatedField *intern, void *value TSRMLS_DC) { | |
222 HashTable *ht = HASH_OF(intern->array); | |
223 int size = native_slot_size(intern->type); | |
224 zend_hash_next_index_insert(ht, (void **)value, size, NULL); | |
225 } | |
226 | |
227 void repeated_field_create_with_type(zend_class_entry *ce, | |
228 const upb_fielddef *field, | |
229 zval **repeated_field TSRMLS_DC) { | |
230 MAKE_STD_ZVAL(*repeated_field); | |
231 Z_TYPE_PP(repeated_field) = IS_OBJECT; | |
232 Z_OBJVAL_PP(repeated_field) = | |
233 repeated_field_type->create_object(repeated_field_type TSRMLS_CC); | |
234 | |
235 RepeatedField *intern = | |
236 zend_object_store_get_object(*repeated_field TSRMLS_CC); | |
237 intern->type = upb_fielddef_type(field); | |
238 if (intern->type == UPB_TYPE_MESSAGE) { | |
239 const upb_msgdef *msg = upb_fielddef_msgsubdef(field); | |
240 zval *desc_php = get_def_obj(msg); | |
241 Descriptor *desc = zend_object_store_get_object(desc_php TSRMLS_CC); | |
242 intern->msg_ce = desc->klass; | |
243 } | |
244 MAKE_STD_ZVAL(intern->array); | |
245 repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC); | |
246 | |
247 // TODO(teboring): Link class entry for message and enum | |
248 } | |
249 | |
250 | |
251 // ----------------------------------------------------------------------------- | |
252 // PHP RepeatedField Methods | |
253 // ----------------------------------------------------------------------------- | |
254 | |
255 /** | |
256 * Constructs an instance of RepeatedField. | |
257 * @param long Type of the stored element. | |
258 * @param string Message/Enum class name (message/enum fields only). | |
259 */ | |
260 PHP_METHOD(RepeatedField, __construct) { | |
261 long type; | |
262 zend_class_entry* klass = NULL; | |
263 | |
264 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|C", &type, &klass) == | |
265 FAILURE) { | |
266 return; | |
267 } | |
268 | |
269 RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
270 intern->type = to_fieldtype(type); | |
271 intern->msg_ce = klass; | |
272 | |
273 MAKE_STD_ZVAL(intern->array); | |
274 repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC); | |
275 | |
276 if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) { | |
277 zend_error(E_USER_ERROR, "Message type must have concrete class."); | |
278 return; | |
279 } | |
280 | |
281 // TODO(teboring): Consider enum. | |
282 } | |
283 | |
284 /** | |
285 * Append element to the end of the repeated field. | |
286 * @param object The element to be added. | |
287 */ | |
288 PHP_METHOD(RepeatedField, append) { | |
289 zval *value; | |
290 | |
291 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == | |
292 FAILURE) { | |
293 return; | |
294 } | |
295 repeated_field_write_dimension(getThis(), NULL, value TSRMLS_CC); | |
296 } | |
297 | |
298 /** | |
299 * Check whether the element at given index exists. | |
300 * @param long The index to be checked. | |
301 * @return bool True if the element at the given index exists. | |
302 */ | |
303 PHP_METHOD(RepeatedField, offsetExists) { | |
304 long index; | |
305 | |
306 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == | |
307 FAILURE) { | |
308 return; | |
309 } | |
310 | |
311 RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
312 | |
313 RETURN_BOOL(index >= 0 && | |
314 index < zend_hash_num_elements(HASH_OF(intern->array))); | |
315 } | |
316 | |
317 /** | |
318 * Return the element at the given index. | |
319 * This will also be called for: $ele = $arr[0] | |
320 * @param long The index of the element to be fetched. | |
321 * @return object The stored element at given index. | |
322 * @exception Invalid type for index. | |
323 * @exception Non-existing index. | |
324 */ | |
325 PHP_METHOD(RepeatedField, offsetGet) { | |
326 long index; | |
327 void *memory; | |
328 | |
329 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == | |
330 FAILURE) { | |
331 return; | |
332 } | |
333 | |
334 RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
335 HashTable *table = HASH_OF(intern->array); | |
336 | |
337 if (zend_hash_index_find(table, index, (void **)&memory) == FAILURE) { | |
338 zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); | |
339 return; | |
340 } | |
341 | |
342 native_slot_get(intern->type, memory, return_value_ptr TSRMLS_CC); | |
343 } | |
344 | |
345 /** | |
346 * Assign the element at the given index. | |
347 * This will also be called for: $arr []= $ele and $arr[0] = ele | |
348 * @param long The index of the element to be assigned. | |
349 * @param object The element to be assigned. | |
350 * @exception Invalid type for index. | |
351 * @exception Non-existing index. | |
352 * @exception Incorrect type of the element. | |
353 */ | |
354 PHP_METHOD(RepeatedField, offsetSet) { | |
355 zval *index, *value; | |
356 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) == | |
357 FAILURE) { | |
358 return; | |
359 } | |
360 repeated_field_write_dimension(getThis(), index, value TSRMLS_CC); | |
361 } | |
362 | |
363 /** | |
364 * Remove the element at the given index. | |
365 * This will also be called for: unset($arr) | |
366 * @param long The index of the element to be removed. | |
367 * @exception Invalid type for index. | |
368 * @exception The element to be removed is not at the end of the RepeatedField. | |
369 */ | |
370 PHP_METHOD(RepeatedField, offsetUnset) { | |
371 long index; | |
372 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == | |
373 FAILURE) { | |
374 return; | |
375 } | |
376 | |
377 RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
378 | |
379 // Only the element at the end of the array can be removed. | |
380 if (index == -1 || | |
381 index != (zend_hash_num_elements(HASH_OF(intern->array)) - 1)) { | |
382 zend_error(E_USER_ERROR, "Cannot remove element at %ld.\n", index); | |
383 return; | |
384 } | |
385 | |
386 zend_hash_index_del(HASH_OF(intern->array), index); | |
387 } | |
388 | |
389 /** | |
390 * Return the number of stored elements. | |
391 * This will also be called for: count($arr) | |
392 * @return long The number of stored elements. | |
393 */ | |
394 PHP_METHOD(RepeatedField, count) { | |
395 RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
396 | |
397 if (zend_parse_parameters_none() == FAILURE) { | |
398 return; | |
399 } | |
400 | |
401 RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->array))); | |
402 } | |
403 | |
404 /** | |
405 * Return the beginning iterator. | |
406 * This will also be called for: foreach($arr) | |
407 * @return object Beginning iterator. | |
408 */ | |
409 PHP_METHOD(RepeatedField, getIterator) { | |
410 zval *iter_php = NULL; | |
411 MAKE_STD_ZVAL(iter_php); | |
412 Z_TYPE_P(iter_php) = IS_OBJECT; | |
413 Z_OBJVAL_P(iter_php) = repeated_field_iter_type->create_object( | |
414 repeated_field_iter_type TSRMLS_CC); | |
415 | |
416 RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
417 RepeatedFieldIter *iter = zend_object_store_get_object(iter_php TSRMLS_CC); | |
418 iter->repeated_field = intern; | |
419 iter->position = 0; | |
420 | |
421 RETURN_ZVAL(iter_php, 1, 1); | |
422 } | |
423 | |
424 // ----------------------------------------------------------------------------- | |
425 // RepeatedFieldIter creation/desctruction | |
426 // ----------------------------------------------------------------------------- | |
427 | |
428 void repeated_field_iter_init(TSRMLS_D) { | |
429 zend_class_entry class_type; | |
430 const char* class_name = "Google\\Protobuf\\Internal\\RepeatedFieldIter"; | |
431 INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name), | |
432 repeated_field_iter_methods); | |
433 | |
434 repeated_field_iter_type = | |
435 zend_register_internal_class(&class_type TSRMLS_CC); | |
436 repeated_field_iter_type->create_object = repeated_field_iter_create; | |
437 | |
438 zend_class_implements(repeated_field_iter_type TSRMLS_CC, 1, | |
439 zend_ce_iterator); | |
440 } | |
441 | |
442 static zend_object_value repeated_field_iter_create( | |
443 zend_class_entry *ce TSRMLS_DC) { | |
444 zend_object_value retval = {0}; | |
445 RepeatedFieldIter *intern; | |
446 | |
447 intern = emalloc(sizeof(RepeatedFieldIter)); | |
448 memset(intern, 0, sizeof(RepeatedFieldIter)); | |
449 | |
450 zend_object_std_init(&intern->std, ce TSRMLS_CC); | |
451 object_properties_init(&intern->std, ce); | |
452 | |
453 intern->repeated_field = NULL; | |
454 intern->position = 0; | |
455 | |
456 retval.handle = zend_objects_store_put( | |
457 intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, | |
458 (zend_objects_free_object_storage_t)repeated_field_iter_free, | |
459 NULL TSRMLS_CC); | |
460 retval.handlers = zend_get_std_object_handlers(); | |
461 | |
462 return retval; | |
463 } | |
464 | |
465 static void repeated_field_iter_free(void *object TSRMLS_DC) { | |
466 RepeatedFieldIter *intern = object; | |
467 zend_object_std_dtor(&intern->std TSRMLS_CC); | |
468 efree(object); | |
469 } | |
470 | |
471 // ----------------------------------------------------------------------------- | |
472 // PHP RepeatedFieldIter Methods | |
473 // ----------------------------------------------------------------------------- | |
474 | |
475 PHP_METHOD(RepeatedFieldIter, rewind) { | |
476 RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
477 intern->position = 0; | |
478 } | |
479 | |
480 PHP_METHOD(RepeatedFieldIter, current) { | |
481 RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
482 RepeatedField *repeated_field = intern->repeated_field; | |
483 | |
484 long index; | |
485 void *memory; | |
486 | |
487 HashTable *table = HASH_OF(repeated_field->array); | |
488 | |
489 if (zend_hash_index_find(table, intern->position, (void **)&memory) == | |
490 FAILURE) { | |
491 zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); | |
492 return; | |
493 } | |
494 native_slot_get(repeated_field->type, memory, return_value_ptr TSRMLS_CC); | |
495 } | |
496 | |
497 PHP_METHOD(RepeatedFieldIter, key) { | |
498 RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
499 RETURN_LONG(intern->position); | |
500 } | |
501 | |
502 PHP_METHOD(RepeatedFieldIter, next) { | |
503 RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
504 ++intern->position; | |
505 } | |
506 | |
507 PHP_METHOD(RepeatedFieldIter, valid) { | |
508 RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); | |
509 RETURN_BOOL(zend_hash_num_elements(HASH_OF(intern->repeated_field->array)) > | |
510 intern->position); | |
511 } | |
OLD | NEW |