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 #include "utf8.h"
37 
38 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
39   ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO()40 ZEND_END_ARG_INFO()
41 
42 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
43   ZEND_ARG_INFO(0, index)
44   ZEND_ARG_INFO(0, newval)
45 ZEND_END_ARG_INFO()
46 
47 ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
48 ZEND_END_ARG_INFO()
49 
50 // Utilities
51 
52 void* upb_value_memory(upb_value* v) {
53   return (void*)(&v->val);
54 }
55 
56 // -----------------------------------------------------------------------------
57 // Basic map operations on top of upb's strtable.
58 //
59 // Note that we roll our own `Map` container here because, as for
60 // `RepeatedField`, we want a strongly-typed container. This is so that any user
61 // errors due to incorrect map key or value types are raised as close as
62 // possible to the error site, rather than at some deferred point (e.g.,
63 // serialization).
64 //
65 // We build our `Map` on top of upb_strtable so that we're able to take
66 // advantage of the native_slot storage abstraction, as RepeatedField does.
67 // (This is not quite a perfect mapping -- see the key conversions below -- but
68 // gives us full support and error-checking for all value types for free.)
69 // -----------------------------------------------------------------------------
70 
71 // Map values are stored using the native_slot abstraction (as with repeated
72 // field values), but keys are a bit special. Since we use a strtable, we need
73 // to store keys as sequences of bytes such that equality of those bytes maps
74 // one-to-one to equality of keys. We store strings directly (i.e., they map to
75 // their own bytes) and integers as native integers (using the native_slot
76 // abstraction).
77 
78 // Note that there is another tradeoff here in keeping string keys as native
79 // strings rather than PHP strings: traversing the Map requires conversion to
80 // PHP string values on every traversal, potentially creating more garbage. We
81 // should consider ways to cache a PHP version of the key if this becomes an
82 // issue later.
83 
84 // Forms a key to use with the underlying strtable from a PHP key value. |buf|
85 // must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to
86 // construct a key byte sequence if needed. |out_key| and |out_length| provide
87 // the resulting key data/length.
88 #define TABLE_KEY_BUF_LENGTH 8  // sizeof(uint64_t)
table_key(Map * self,zval * key,char * buf,const char ** out_key,size_t * out_length TSRMLS_DC)89 static bool table_key(Map* self, zval* key,
90                       char* buf,
91                       const char** out_key,
92                       size_t* out_length TSRMLS_DC) {
93   switch (self->key_type) {
94     case UPB_TYPE_STRING:
95       if (!protobuf_convert_to_string(key)) {
96         return false;
97       }
98       if (!is_structurally_valid_utf8(Z_STRVAL_P(key), Z_STRLEN_P(key))) {
99         zend_error(E_USER_ERROR, "Given key is not UTF8 encoded.");
100         return false;
101       }
102       *out_key = Z_STRVAL_P(key);
103       *out_length = Z_STRLEN_P(key);
104       break;
105 
106 #define CASE_TYPE(upb_type, type, c_type, php_type)                     \
107   case UPB_TYPE_##upb_type: {                                           \
108     c_type type##_value;                                                \
109     if (!protobuf_convert_to_##type(key, &type##_value)) {              \
110       return false;                                                     \
111     }                                                                   \
112     native_slot_set_by_array(self->key_type, NULL, buf, key TSRMLS_CC); \
113     *out_key = buf;                                                     \
114     *out_length = native_slot_size(self->key_type);                     \
115     break;                                                              \
116   }
117       CASE_TYPE(BOOL, bool, int8_t, BOOL)
118       CASE_TYPE(INT32, int32, int32_t, LONG)
119       CASE_TYPE(INT64, int64, int64_t, LONG)
120       CASE_TYPE(UINT32, uint32, uint32_t, LONG)
121       CASE_TYPE(UINT64, uint64, uint64_t, LONG)
122 
123 #undef CASE_TYPE
124 
125     default:
126       // Map constructor should not allow a Map with another key type to be
127       // constructed.
128       assert(false);
129       break;
130   }
131 
132   return true;
133 }
134 
135 // -----------------------------------------------------------------------------
136 // MapField methods
137 // -----------------------------------------------------------------------------
138 
139 static zend_function_entry map_field_methods[] = {
140   PHP_ME(MapField, __construct,  NULL,              ZEND_ACC_PUBLIC)
141   PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
142   PHP_ME(MapField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
143   PHP_ME(MapField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
144   PHP_ME(MapField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
145   PHP_ME(MapField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
146   PHP_ME(MapField, getIterator,  arginfo_void,      ZEND_ACC_PUBLIC)
147   ZEND_FE_END
148 };
149 
150 // Forward declare static functions.
151 
152 static void map_field_write_dimension(zval *object, zval *key,
153                                       zval *value TSRMLS_DC);
154 
155 // -----------------------------------------------------------------------------
156 // MapField creation/desctruction
157 // -----------------------------------------------------------------------------
158 
159 zend_class_entry* map_field_type;
160 zend_class_entry* map_field_iter_type;
161 
162 zend_object_handlers* map_field_handlers;
163 zend_object_handlers* map_field_iter_handlers;
164 
map_begin_internal(Map * map,MapIter * iter)165 static void map_begin_internal(Map *map, MapIter *iter) {
166   iter->self = map;
167   upb_strtable_begin(&iter->it, &map->table);
168 }
169 
map_field_get_gc(zval * object,CACHED_VALUE ** table,int * n TSRMLS_DC)170 static HashTable *map_field_get_gc(zval *object, CACHED_VALUE **table,
171                                    int *n TSRMLS_DC) {
172   // TODO(teboring): Unfortunately, zend engine does not support garbage
173   // collection for custom array. We have to use zend engine's native array
174   // instead.
175   *table = NULL;
176   *n = 0;
177   return NULL;
178 }
179 
180 // Define map value element free function.
181 #if PHP_MAJOR_VERSION < 7
php_proto_map_string_release(void * value)182 static inline void php_proto_map_string_release(void *value) {
183   zval_ptr_dtor(value);
184 }
185 
php_proto_map_object_release(void * value)186 static inline void php_proto_map_object_release(void *value) {
187   zval_ptr_dtor(value);
188 }
189 #else
php_proto_map_string_release(void * value)190 static inline void php_proto_map_string_release(void *value) {
191   zend_string* object = *(zend_string**)value;
192   zend_string_release(object);
193 }
php_proto_map_object_release(void * value)194 static inline void php_proto_map_object_release(void *value) {
195   zend_object* object = *(zend_object**)value;
196   GC_DELREF(object);
197   if(GC_REFCOUNT(object) == 0) {
198     zend_objects_store_del(object);
199   }
200 }
201 #endif
202 
203 // Define object free method.
PHP_PROTO_OBJECT_FREE_START(Map,map_field)204 PHP_PROTO_OBJECT_FREE_START(Map, map_field)
205 MapIter it;
206 int len;
207 for (map_begin_internal(intern, &it); !map_done(&it); map_next(&it)) {
208   upb_value value = map_iter_value(&it, &len);
209   void *mem = upb_value_memory(&value);
210   switch (intern->value_type) {
211     case UPB_TYPE_MESSAGE:
212       php_proto_map_object_release(mem);
213       break;
214     case UPB_TYPE_STRING:
215     case UPB_TYPE_BYTES:
216       php_proto_map_string_release(mem);
217       break;
218     default:
219       break;
220   }
221 }
222 upb_strtable_uninit(&intern->table);
223 PHP_PROTO_OBJECT_FREE_END
224 
225 PHP_PROTO_OBJECT_DTOR_START(Map, map_field)
226 PHP_PROTO_OBJECT_DTOR_END
227 
228 // Define object create method.
229 PHP_PROTO_OBJECT_CREATE_START(Map, map_field)
230 // Table value type is always UINT64: this ensures enough space to store the
231 // native_slot value.
232 if (!upb_strtable_init(&intern->table, UPB_CTYPE_UINT64)) {
233   zend_error(E_USER_ERROR, "Could not allocate table.");
234 }
235 PHP_PROTO_OBJECT_CREATE_END(Map, map_field)
236 
237 // Init class entry.
238 PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\MapField", Map,
239                            map_field)
240 zend_class_implements(map_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess,
241                       zend_ce_aggregate, spl_ce_Countable);
242 map_field_handlers->write_dimension = map_field_write_dimension;
243 map_field_handlers->get_gc = map_field_get_gc;
244 PHP_PROTO_INIT_CLASS_END
245 
map_field_create_with_field(const zend_class_entry * ce,const upb_fielddef * field,CACHED_VALUE * map_field PHP_PROTO_TSRMLS_DC)246 void map_field_create_with_field(const zend_class_entry *ce,
247                                  const upb_fielddef *field,
248                                  CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {
249   const upb_fielddef *key_field = map_field_key(field);
250   const upb_fielddef *value_field = map_field_value(field);
251   map_field_create_with_type(
252       ce, upb_fielddef_type(key_field), upb_fielddef_type(value_field),
253       field_type_class(value_field TSRMLS_CC), map_field PHP_PROTO_TSRMLS_CC);
254 }
255 
map_field_create_with_type(const zend_class_entry * ce,upb_fieldtype_t key_type,upb_fieldtype_t value_type,const zend_class_entry * msg_ce,CACHED_VALUE * map_field PHP_PROTO_TSRMLS_DC)256 void map_field_create_with_type(const zend_class_entry *ce,
257                                 upb_fieldtype_t key_type,
258                                 upb_fieldtype_t value_type,
259                                 const zend_class_entry *msg_ce,
260                                 CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {
261   CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(CACHED_PTR_TO_ZVAL_PTR(map_field),
262                                    map_field_type);
263   Map *intern = UNBOX(Map, CACHED_TO_ZVAL_PTR(*map_field));
264   intern->key_type = key_type;
265   intern->value_type = value_type;
266   intern->msg_ce = msg_ce;
267 }
268 
269 // -----------------------------------------------------------------------------
270 // MapField Handlers
271 // -----------------------------------------------------------------------------
272 
map_field_read_dimension(zval * object,zval * key,int type,CACHED_VALUE * retval TSRMLS_DC)273 static bool map_field_read_dimension(zval *object, zval *key, int type,
274                                      CACHED_VALUE *retval TSRMLS_DC) {
275   Map *intern = UNBOX(Map, object);
276 
277   char keybuf[TABLE_KEY_BUF_LENGTH];
278   const char* keyval = NULL;
279   size_t length = 0;
280   upb_value v;
281 #ifndef NDEBUG
282   v.ctype = UPB_CTYPE_UINT64;
283 #endif
284   if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
285     return false;
286   }
287 
288   if (upb_strtable_lookup2(&intern->table, keyval, length, &v)) {
289     void* mem = upb_value_memory(&v);
290     native_slot_get_by_map_value(intern->value_type, mem, retval TSRMLS_CC);
291     return true;
292   } else {
293     zend_error(E_USER_ERROR, "Given key doesn't exist.");
294     return false;
295   }
296 }
297 
map_index_unset(Map * intern,const char * keyval,int length)298 static void map_index_unset(Map *intern, const char* keyval, int length) {
299   upb_value old_value;
300   if (upb_strtable_remove2(&intern->table, keyval, length, &old_value)) {
301     switch (intern->value_type) {
302       case UPB_TYPE_MESSAGE: {
303 #if PHP_MAJOR_VERSION < 7
304         zval_ptr_dtor(upb_value_memory(&old_value));
305 #else
306         zend_object* object = *(zend_object**)upb_value_memory(&old_value);
307         GC_DELREF(object);
308         if(GC_REFCOUNT(object) == 0) {
309           zend_objects_store_del(object);
310         }
311 #endif
312         break;
313       }
314       case UPB_TYPE_STRING:
315       case UPB_TYPE_BYTES: {
316 #if PHP_MAJOR_VERSION < 7
317         zval_ptr_dtor(upb_value_memory(&old_value));
318 #else
319         zend_string* object = *(zend_string**)upb_value_memory(&old_value);
320         zend_string_release(object);
321 #endif
322         break;
323       }
324       default:
325         break;
326     }
327   }
328 }
329 
map_index_set(Map * intern,const char * keyval,int length,upb_value v)330 bool map_index_set(Map *intern, const char* keyval, int length, upb_value v) {
331   // Replace any existing value by issuing a 'remove' operation first.
332   map_index_unset(intern, keyval, length);
333 
334   if (!upb_strtable_insert2(&intern->table, keyval, length, v)) {
335     zend_error(E_USER_ERROR, "Could not insert into table");
336     return false;
337   }
338 
339   return true;
340 }
341 
map_field_write_dimension(zval * object,zval * key,zval * value TSRMLS_DC)342 static void map_field_write_dimension(zval *object, zval *key,
343                                       zval *value TSRMLS_DC) {
344   Map *intern = UNBOX(Map, object);
345 
346   char keybuf[TABLE_KEY_BUF_LENGTH];
347   const char* keyval = NULL;
348   size_t length = 0;
349   upb_value v;
350   void* mem;
351   if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
352     return;
353   }
354 
355   mem = upb_value_memory(&v);
356   memset(mem, 0, native_slot_size(intern->value_type));
357   if (!native_slot_set_by_map(intern->value_type, intern->msg_ce, mem,
358                                 value TSRMLS_CC)) {
359     return;
360   }
361 #ifndef NDEBUG
362   v.ctype = UPB_CTYPE_UINT64;
363 #endif
364 
365   map_index_set(intern, keyval, length, v);
366 }
367 
map_field_unset_dimension(zval * object,zval * key TSRMLS_DC)368 static bool map_field_unset_dimension(zval *object, zval *key TSRMLS_DC) {
369   Map *intern = UNBOX(Map, object);
370 
371   char keybuf[TABLE_KEY_BUF_LENGTH];
372   const char* keyval = NULL;
373   size_t length = 0;
374   upb_value v;
375   if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
376     return false;
377   }
378 #ifndef NDEBUG
379   v.ctype = UPB_CTYPE_UINT64;
380 #endif
381 
382   map_index_unset(intern, keyval, length);
383 
384   return true;
385 }
386 
387 // -----------------------------------------------------------------------------
388 // PHP MapField Methods
389 // -----------------------------------------------------------------------------
390 
PHP_METHOD(MapField,__construct)391 PHP_METHOD(MapField, __construct) {
392   long key_type, value_type;
393   zend_class_entry* klass = NULL;
394 
395   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|C", &key_type,
396                             &value_type, &klass) == FAILURE) {
397     return;
398   }
399 
400   Map *intern = UNBOX(Map, getThis());
401   intern->key_type = to_fieldtype(key_type);
402   intern->value_type = to_fieldtype(value_type);
403   intern->msg_ce = klass;
404 
405   // Check that the key type is an allowed type.
406   switch (intern->key_type) {
407     case UPB_TYPE_INT32:
408     case UPB_TYPE_INT64:
409     case UPB_TYPE_UINT32:
410     case UPB_TYPE_UINT64:
411     case UPB_TYPE_BOOL:
412     case UPB_TYPE_STRING:
413     case UPB_TYPE_BYTES:
414       // These are OK.
415       break;
416     default:
417       zend_error(E_USER_ERROR, "Invalid key type for map.");
418   }
419 }
420 
PHP_METHOD(MapField,offsetExists)421 PHP_METHOD(MapField, offsetExists) {
422   zval *key;
423   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) ==
424       FAILURE) {
425     return;
426   }
427 
428   Map *intern = UNBOX(Map, getThis());
429 
430   char keybuf[TABLE_KEY_BUF_LENGTH];
431   const char* keyval = NULL;
432   size_t length = 0;
433   upb_value v;
434 #ifndef NDEBUG
435   v.ctype = UPB_CTYPE_UINT64;
436 #endif
437   if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
438     RETURN_BOOL(false);
439   }
440 
441   RETURN_BOOL(upb_strtable_lookup2(&intern->table, keyval, length, &v));
442 }
443 
PHP_METHOD(MapField,offsetGet)444 PHP_METHOD(MapField, offsetGet) {
445   zval *index, *value;
446   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
447       FAILURE) {
448     return;
449   }
450   map_field_read_dimension(getThis(), index, BP_VAR_R,
451                            ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
452 }
453 
PHP_METHOD(MapField,offsetSet)454 PHP_METHOD(MapField, offsetSet) {
455   zval *index, *value;
456   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
457       FAILURE) {
458     return;
459   }
460   map_field_write_dimension(getThis(), index, value TSRMLS_CC);
461 }
462 
PHP_METHOD(MapField,offsetUnset)463 PHP_METHOD(MapField, offsetUnset) {
464   zval *index;
465   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
466       FAILURE) {
467     return;
468   }
469   map_field_unset_dimension(getThis(), index TSRMLS_CC);
470 }
471 
PHP_METHOD(MapField,count)472 PHP_METHOD(MapField, count) {
473   Map *intern = UNBOX(Map, getThis());
474 
475   if (zend_parse_parameters_none() == FAILURE) {
476     return;
477   }
478 
479   RETURN_LONG(upb_strtable_count(&intern->table));
480 }
481 
PHP_METHOD(MapField,getIterator)482 PHP_METHOD(MapField, getIterator) {
483   CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(return_value,
484                                    map_field_iter_type);
485 
486   Map *intern = UNBOX(Map, getThis());
487   MapIter *iter = UNBOX(MapIter, return_value);
488   map_begin(getThis(), iter TSRMLS_CC);
489 }
490 
491 // -----------------------------------------------------------------------------
492 // Map Iterator
493 // -----------------------------------------------------------------------------
494 
map_begin(zval * map_php,MapIter * iter TSRMLS_DC)495 void map_begin(zval *map_php, MapIter *iter TSRMLS_DC) {
496   Map *self = UNBOX(Map, map_php);
497   map_begin_internal(self, iter);
498 }
499 
map_next(MapIter * iter)500 void map_next(MapIter *iter) {
501   upb_strtable_next(&iter->it);
502 }
503 
map_done(MapIter * iter)504 bool map_done(MapIter *iter) {
505   return upb_strtable_done(&iter->it);
506 }
507 
map_iter_key(MapIter * iter,int * len)508 const char *map_iter_key(MapIter *iter, int *len) {
509   *len = upb_strtable_iter_keylength(&iter->it);
510   return upb_strtable_iter_key(&iter->it);
511 }
512 
map_iter_value(MapIter * iter,int * len)513 upb_value map_iter_value(MapIter *iter, int *len) {
514   *len = native_slot_size(iter->self->value_type);
515   return upb_strtable_iter_value(&iter->it);
516 }
517 
518 // -----------------------------------------------------------------------------
519 // MapFieldIter methods
520 // -----------------------------------------------------------------------------
521 static zend_function_entry map_field_iter_methods[] = {
522   PHP_ME(MapFieldIter, rewind,      arginfo_void, ZEND_ACC_PUBLIC)
523   PHP_ME(MapFieldIter, current,     arginfo_void, ZEND_ACC_PUBLIC)
524   PHP_ME(MapFieldIter, key,         arginfo_void, ZEND_ACC_PUBLIC)
525   PHP_ME(MapFieldIter, next,        arginfo_void, ZEND_ACC_PUBLIC)
526   PHP_ME(MapFieldIter, valid,       arginfo_void, ZEND_ACC_PUBLIC)
527   ZEND_FE_END
528 };
529 
530 // -----------------------------------------------------------------------------
531 // MapFieldIter creation/desctruction
532 // -----------------------------------------------------------------------------
533 
534 // Define object free method.
535 PHP_PROTO_OBJECT_FREE_START(MapIter, map_field_iter)
536 PHP_PROTO_OBJECT_FREE_END
537 
538 PHP_PROTO_OBJECT_DTOR_START(MapIter, map_field_iter)
539 PHP_PROTO_OBJECT_DTOR_END
540 
541 // Define object create method.
542 PHP_PROTO_OBJECT_CREATE_START(MapIter, map_field_iter)
543 intern->self = NULL;
544 PHP_PROTO_OBJECT_CREATE_END(MapIter, map_field_iter)
545 
546 // Init class entry.
547 PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\MapFieldIter",
548                            MapIter, map_field_iter)
549 zend_class_implements(map_field_iter_type TSRMLS_CC, 1, zend_ce_iterator);
550 PHP_PROTO_INIT_CLASS_END
551 
552 // -----------------------------------------------------------------------------
553 // PHP MapFieldIter Methods
554 // -----------------------------------------------------------------------------
555 
PHP_METHOD(MapFieldIter,rewind)556 PHP_METHOD(MapFieldIter, rewind) {
557   MapIter *intern = UNBOX(MapIter, getThis());
558   map_begin_internal(intern->self, intern);
559 }
560 
PHP_METHOD(MapFieldIter,current)561 PHP_METHOD(MapFieldIter, current) {
562   MapIter *intern = UNBOX(MapIter, getThis());
563   Map *map_field = intern->self;
564 
565   int value_length = 0;
566   upb_value value = map_iter_value(intern, &value_length);
567 
568   void* mem = upb_value_memory(&value);
569   native_slot_get_by_map_value(map_field->value_type, mem,
570                                ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
571 }
572 
PHP_METHOD(MapFieldIter,key)573 PHP_METHOD(MapFieldIter, key) {
574   MapIter *intern = UNBOX(MapIter, getThis());
575   Map *map_field = intern->self;
576 
577   int key_length = 0;
578   const char* key = map_iter_key(intern, &key_length);
579 
580   native_slot_get_by_map_key(map_field->key_type, key, key_length,
581                              ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
582 }
583 
PHP_METHOD(MapFieldIter,next)584 PHP_METHOD(MapFieldIter, next) {
585   MapIter *intern = UNBOX(MapIter, getThis());
586   map_next(intern);
587 }
588 
PHP_METHOD(MapFieldIter,valid)589 PHP_METHOD(MapFieldIter, valid) {
590   MapIter *intern = UNBOX(MapIter, getThis());
591   RETURN_BOOL(!map_done(intern));
592 }
593