1ffe3c632Sopenharmony_ci// Protocol Buffers - Google's data interchange format
2ffe3c632Sopenharmony_ci// Copyright 2008 Google Inc.  All rights reserved.
3ffe3c632Sopenharmony_ci// https://developers.google.com/protocol-buffers/
4ffe3c632Sopenharmony_ci//
5ffe3c632Sopenharmony_ci// Redistribution and use in source and binary forms, with or without
6ffe3c632Sopenharmony_ci// modification, are permitted provided that the following conditions are
7ffe3c632Sopenharmony_ci// met:
8ffe3c632Sopenharmony_ci//
9ffe3c632Sopenharmony_ci//     * Redistributions of source code must retain the above copyright
10ffe3c632Sopenharmony_ci// notice, this list of conditions and the following disclaimer.
11ffe3c632Sopenharmony_ci//     * Redistributions in binary form must reproduce the above
12ffe3c632Sopenharmony_ci// copyright notice, this list of conditions and the following disclaimer
13ffe3c632Sopenharmony_ci// in the documentation and/or other materials provided with the
14ffe3c632Sopenharmony_ci// distribution.
15ffe3c632Sopenharmony_ci//     * Neither the name of Google Inc. nor the names of its
16ffe3c632Sopenharmony_ci// contributors may be used to endorse or promote products derived from
17ffe3c632Sopenharmony_ci// this software without specific prior written permission.
18ffe3c632Sopenharmony_ci//
19ffe3c632Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20ffe3c632Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21ffe3c632Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22ffe3c632Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23ffe3c632Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24ffe3c632Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25ffe3c632Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26ffe3c632Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27ffe3c632Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28ffe3c632Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29ffe3c632Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30ffe3c632Sopenharmony_ci
31ffe3c632Sopenharmony_ci#include "array.h"
32ffe3c632Sopenharmony_ci
33ffe3c632Sopenharmony_ci#include <Zend/zend_API.h>
34ffe3c632Sopenharmony_ci#include <Zend/zend_interfaces.h>
35ffe3c632Sopenharmony_ci
36ffe3c632Sopenharmony_ci#include <ext/spl/spl_iterators.h>
37ffe3c632Sopenharmony_ci
38ffe3c632Sopenharmony_ci// This is not self-contained: it must be after other Zend includes.
39ffe3c632Sopenharmony_ci#include <Zend/zend_exceptions.h>
40ffe3c632Sopenharmony_ci
41ffe3c632Sopenharmony_ci#include "arena.h"
42ffe3c632Sopenharmony_ci#include "convert.h"
43ffe3c632Sopenharmony_ci#include "def.h"
44ffe3c632Sopenharmony_ci#include "php-upb.h"
45ffe3c632Sopenharmony_ci#include "protobuf.h"
46ffe3c632Sopenharmony_ci
47ffe3c632Sopenharmony_cistatic void RepeatedFieldIter_make(zval *val, zval *repeated_field);
48ffe3c632Sopenharmony_ci
49ffe3c632Sopenharmony_ci// -----------------------------------------------------------------------------
50ffe3c632Sopenharmony_ci// RepeatedField
51ffe3c632Sopenharmony_ci// -----------------------------------------------------------------------------
52ffe3c632Sopenharmony_ci
53ffe3c632Sopenharmony_citypedef struct {
54ffe3c632Sopenharmony_ci  zend_object std;
55ffe3c632Sopenharmony_ci  zval arena;
56ffe3c632Sopenharmony_ci  upb_array *array;
57ffe3c632Sopenharmony_ci  upb_fieldtype_t type;
58ffe3c632Sopenharmony_ci  const Descriptor* desc;  // When values are messages.
59ffe3c632Sopenharmony_ci} RepeatedField;
60ffe3c632Sopenharmony_ci
61ffe3c632Sopenharmony_cizend_class_entry *RepeatedField_class_entry;
62ffe3c632Sopenharmony_cistatic zend_object_handlers RepeatedField_object_handlers;
63ffe3c632Sopenharmony_ci
64ffe3c632Sopenharmony_ci// PHP Object Handlers /////////////////////////////////////////////////////////
65ffe3c632Sopenharmony_ci
66ffe3c632Sopenharmony_ci/**
67ffe3c632Sopenharmony_ci * RepeatedField_create()
68ffe3c632Sopenharmony_ci *
69ffe3c632Sopenharmony_ci * PHP class entry function to allocate and initialize a new RepeatedField
70ffe3c632Sopenharmony_ci * object.
71ffe3c632Sopenharmony_ci */
72ffe3c632Sopenharmony_cistatic zend_object* RepeatedField_create(zend_class_entry *class_type) {
73ffe3c632Sopenharmony_ci  RepeatedField *intern = emalloc(sizeof(RepeatedField));
74ffe3c632Sopenharmony_ci  zend_object_std_init(&intern->std, class_type);
75ffe3c632Sopenharmony_ci  intern->std.handlers = &RepeatedField_object_handlers;
76ffe3c632Sopenharmony_ci  Arena_Init(&intern->arena);
77ffe3c632Sopenharmony_ci  intern->array = NULL;
78ffe3c632Sopenharmony_ci  intern->desc = NULL;
79ffe3c632Sopenharmony_ci  // Skip object_properties_init(), we don't allow derived classes.
80ffe3c632Sopenharmony_ci  return &intern->std;
81ffe3c632Sopenharmony_ci}
82ffe3c632Sopenharmony_ci
83ffe3c632Sopenharmony_ci/**
84ffe3c632Sopenharmony_ci * RepeatedField_dtor()
85ffe3c632Sopenharmony_ci *
86ffe3c632Sopenharmony_ci * Object handler to destroy a RepeatedField. This releases all resources
87ffe3c632Sopenharmony_ci * associated with the message. Note that it is possible to access a destroyed
88ffe3c632Sopenharmony_ci * object from PHP in rare cases.
89ffe3c632Sopenharmony_ci */
90ffe3c632Sopenharmony_cistatic void RepeatedField_destructor(zend_object* obj) {
91ffe3c632Sopenharmony_ci  RepeatedField* intern = (RepeatedField*)obj;
92ffe3c632Sopenharmony_ci  ObjCache_Delete(intern->array);
93ffe3c632Sopenharmony_ci  zval_ptr_dtor(&intern->arena);
94ffe3c632Sopenharmony_ci  zend_object_std_dtor(&intern->std);
95ffe3c632Sopenharmony_ci}
96ffe3c632Sopenharmony_ci
97ffe3c632Sopenharmony_cistatic HashTable *RepeatedField_GetProperties(PROTO_VAL *object) {
98ffe3c632Sopenharmony_ci  return NULL;  // We do not have a properties table.
99ffe3c632Sopenharmony_ci}
100ffe3c632Sopenharmony_ci
101ffe3c632Sopenharmony_cistatic zval *RepeatedField_GetPropertyPtrPtr(PROTO_VAL *object,
102ffe3c632Sopenharmony_ci                                             PROTO_STR *member,
103ffe3c632Sopenharmony_ci                                             int type, void **cache_slot) {
104ffe3c632Sopenharmony_ci  return NULL;  // We don't offer direct references to our properties.
105ffe3c632Sopenharmony_ci}
106ffe3c632Sopenharmony_ci
107ffe3c632Sopenharmony_ci// C Functions from array.h ////////////////////////////////////////////////////
108ffe3c632Sopenharmony_ci
109ffe3c632Sopenharmony_ci// These are documented in the header file.
110ffe3c632Sopenharmony_ci
111ffe3c632Sopenharmony_civoid RepeatedField_GetPhpWrapper(zval *val, upb_array *arr,
112ffe3c632Sopenharmony_ci                                 const upb_fielddef *f, zval *arena) {
113ffe3c632Sopenharmony_ci  if (!arr) {
114ffe3c632Sopenharmony_ci    ZVAL_NULL(val);
115ffe3c632Sopenharmony_ci    return;
116ffe3c632Sopenharmony_ci  }
117ffe3c632Sopenharmony_ci
118ffe3c632Sopenharmony_ci  if (!ObjCache_Get(arr, val)) {
119ffe3c632Sopenharmony_ci    RepeatedField *intern = emalloc(sizeof(RepeatedField));
120ffe3c632Sopenharmony_ci    zend_object_std_init(&intern->std, RepeatedField_class_entry);
121ffe3c632Sopenharmony_ci    intern->std.handlers = &RepeatedField_object_handlers;
122ffe3c632Sopenharmony_ci    ZVAL_COPY(&intern->arena, arena);
123ffe3c632Sopenharmony_ci    intern->array = arr;
124ffe3c632Sopenharmony_ci    intern->type = upb_fielddef_type(f);
125ffe3c632Sopenharmony_ci    intern->desc = Descriptor_GetFromFieldDef(f);
126ffe3c632Sopenharmony_ci    // Skip object_properties_init(), we don't allow derived classes.
127ffe3c632Sopenharmony_ci    ObjCache_Add(intern->array, &intern->std);
128ffe3c632Sopenharmony_ci    ZVAL_OBJ(val, &intern->std);
129ffe3c632Sopenharmony_ci  }
130ffe3c632Sopenharmony_ci}
131ffe3c632Sopenharmony_ci
132ffe3c632Sopenharmony_ciupb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f,
133ffe3c632Sopenharmony_ci                                     upb_arena *arena) {
134ffe3c632Sopenharmony_ci  if (Z_ISREF_P(val)) {
135ffe3c632Sopenharmony_ci    ZVAL_DEREF(val);
136ffe3c632Sopenharmony_ci  }
137ffe3c632Sopenharmony_ci
138ffe3c632Sopenharmony_ci  if (Z_TYPE_P(val) == IS_ARRAY) {
139ffe3c632Sopenharmony_ci    // Auto-construct, eg. [1, 2, 3] -> upb_array([1, 2, 3]).
140ffe3c632Sopenharmony_ci    upb_array *arr = upb_array_new(arena, upb_fielddef_type(f));
141ffe3c632Sopenharmony_ci    HashTable *table = HASH_OF(val);
142ffe3c632Sopenharmony_ci    HashPosition pos;
143ffe3c632Sopenharmony_ci    upb_fieldtype_t type = upb_fielddef_type(f);
144ffe3c632Sopenharmony_ci    const Descriptor *desc = Descriptor_GetFromFieldDef(f);
145ffe3c632Sopenharmony_ci
146ffe3c632Sopenharmony_ci    zend_hash_internal_pointer_reset_ex(table, &pos);
147ffe3c632Sopenharmony_ci
148ffe3c632Sopenharmony_ci    while (true) {
149ffe3c632Sopenharmony_ci      zval *zv = zend_hash_get_current_data_ex(table, &pos);
150ffe3c632Sopenharmony_ci      upb_msgval val;
151ffe3c632Sopenharmony_ci
152ffe3c632Sopenharmony_ci      if (!zv) return arr;
153ffe3c632Sopenharmony_ci
154ffe3c632Sopenharmony_ci      if (!Convert_PhpToUpbAutoWrap(zv, &val, type, desc, arena)) {
155ffe3c632Sopenharmony_ci        return NULL;
156ffe3c632Sopenharmony_ci      }
157ffe3c632Sopenharmony_ci
158ffe3c632Sopenharmony_ci      upb_array_append(arr, val, arena);
159ffe3c632Sopenharmony_ci      zend_hash_move_forward_ex(table, &pos);
160ffe3c632Sopenharmony_ci    }
161ffe3c632Sopenharmony_ci  } else if (Z_TYPE_P(val) == IS_OBJECT &&
162ffe3c632Sopenharmony_ci             Z_OBJCE_P(val) == RepeatedField_class_entry) {
163ffe3c632Sopenharmony_ci    // Unwrap existing RepeatedField object to get the upb_array* inside.
164ffe3c632Sopenharmony_ci    RepeatedField *intern = (RepeatedField*)Z_OBJ_P(val);
165ffe3c632Sopenharmony_ci    const Descriptor *desc = Descriptor_GetFromFieldDef(f);
166ffe3c632Sopenharmony_ci
167ffe3c632Sopenharmony_ci    if (intern->type != upb_fielddef_type(f) || intern->desc != desc) {
168ffe3c632Sopenharmony_ci      php_error_docref(NULL, E_USER_ERROR,
169ffe3c632Sopenharmony_ci                       "Wrong type for this repeated field.");
170ffe3c632Sopenharmony_ci    }
171ffe3c632Sopenharmony_ci
172ffe3c632Sopenharmony_ci    upb_arena_fuse(arena, Arena_Get(&intern->arena));
173ffe3c632Sopenharmony_ci    return intern->array;
174ffe3c632Sopenharmony_ci  } else {
175ffe3c632Sopenharmony_ci    php_error_docref(NULL, E_USER_ERROR, "Must be a repeated field");
176ffe3c632Sopenharmony_ci    return NULL;
177ffe3c632Sopenharmony_ci  }
178ffe3c632Sopenharmony_ci}
179ffe3c632Sopenharmony_ci
180ffe3c632Sopenharmony_ci// RepeatedField PHP methods ///////////////////////////////////////////////////
181ffe3c632Sopenharmony_ci
182ffe3c632Sopenharmony_ci/**
183ffe3c632Sopenharmony_ci * RepeatedField::__construct()
184ffe3c632Sopenharmony_ci *
185ffe3c632Sopenharmony_ci * Constructs an instance of RepeatedField.
186ffe3c632Sopenharmony_ci * @param long Type of the stored element.
187ffe3c632Sopenharmony_ci * @param string Message/Enum class.
188ffe3c632Sopenharmony_ci */
189ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, __construct) {
190ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
191ffe3c632Sopenharmony_ci  upb_arena *arena = Arena_Get(&intern->arena);
192ffe3c632Sopenharmony_ci  zend_long type;
193ffe3c632Sopenharmony_ci  zend_class_entry* klass = NULL;
194ffe3c632Sopenharmony_ci
195ffe3c632Sopenharmony_ci  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|C", &type, &klass) != SUCCESS) {
196ffe3c632Sopenharmony_ci    return;
197ffe3c632Sopenharmony_ci  }
198ffe3c632Sopenharmony_ci
199ffe3c632Sopenharmony_ci  intern->type = pbphp_dtype_to_type(type);
200ffe3c632Sopenharmony_ci  intern->desc = Descriptor_GetFromClassEntry(klass);
201ffe3c632Sopenharmony_ci
202ffe3c632Sopenharmony_ci  if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) {
203ffe3c632Sopenharmony_ci    php_error_docref(NULL, E_USER_ERROR,
204ffe3c632Sopenharmony_ci                     "Message/enum type must have concrete class.");
205ffe3c632Sopenharmony_ci    return;
206ffe3c632Sopenharmony_ci  }
207ffe3c632Sopenharmony_ci
208ffe3c632Sopenharmony_ci  intern->array = upb_array_new(arena, intern->type);
209ffe3c632Sopenharmony_ci  ObjCache_Add(intern->array, &intern->std);
210ffe3c632Sopenharmony_ci}
211ffe3c632Sopenharmony_ci
212ffe3c632Sopenharmony_ci/**
213ffe3c632Sopenharmony_ci * RepeatedField::append()
214ffe3c632Sopenharmony_ci *
215ffe3c632Sopenharmony_ci * Append element to the end of the repeated field.
216ffe3c632Sopenharmony_ci * @param object The element to be added.
217ffe3c632Sopenharmony_ci */
218ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, append) {
219ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
220ffe3c632Sopenharmony_ci  upb_arena *arena = Arena_Get(&intern->arena);
221ffe3c632Sopenharmony_ci  zval *php_val;
222ffe3c632Sopenharmony_ci  upb_msgval msgval;
223ffe3c632Sopenharmony_ci
224ffe3c632Sopenharmony_ci  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &php_val) != SUCCESS ||
225ffe3c632Sopenharmony_ci      !Convert_PhpToUpb(php_val, &msgval, intern->type, intern->desc, arena)) {
226ffe3c632Sopenharmony_ci    return;
227ffe3c632Sopenharmony_ci  }
228ffe3c632Sopenharmony_ci
229ffe3c632Sopenharmony_ci  upb_array_append(intern->array, msgval, arena);
230ffe3c632Sopenharmony_ci}
231ffe3c632Sopenharmony_ci
232ffe3c632Sopenharmony_ci/**
233ffe3c632Sopenharmony_ci * RepeatedField::offsetExists()
234ffe3c632Sopenharmony_ci *
235ffe3c632Sopenharmony_ci * Implements the ArrayAccess interface. Invoked when PHP code calls:
236ffe3c632Sopenharmony_ci *
237ffe3c632Sopenharmony_ci *   isset($arr[$idx]);
238ffe3c632Sopenharmony_ci *   empty($arr[$idx]);
239ffe3c632Sopenharmony_ci *
240ffe3c632Sopenharmony_ci * @param long The index to be checked.
241ffe3c632Sopenharmony_ci * @return bool True if the element at the given index exists.
242ffe3c632Sopenharmony_ci */
243ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, offsetExists) {
244ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
245ffe3c632Sopenharmony_ci  zend_long index;
246ffe3c632Sopenharmony_ci
247ffe3c632Sopenharmony_ci  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
248ffe3c632Sopenharmony_ci    return;
249ffe3c632Sopenharmony_ci  }
250ffe3c632Sopenharmony_ci
251ffe3c632Sopenharmony_ci  RETURN_BOOL(index >= 0 && index < upb_array_size(intern->array));
252ffe3c632Sopenharmony_ci}
253ffe3c632Sopenharmony_ci
254ffe3c632Sopenharmony_ci/**
255ffe3c632Sopenharmony_ci * RepeatedField::offsetGet()
256ffe3c632Sopenharmony_ci *
257ffe3c632Sopenharmony_ci * Implements the ArrayAccess interface. Invoked when PHP code calls:
258ffe3c632Sopenharmony_ci *
259ffe3c632Sopenharmony_ci *   $x = $arr[$idx];
260ffe3c632Sopenharmony_ci *
261ffe3c632Sopenharmony_ci * @param long The index of the element to be fetched.
262ffe3c632Sopenharmony_ci * @return object The stored element at given index.
263ffe3c632Sopenharmony_ci * @exception Invalid type for index.
264ffe3c632Sopenharmony_ci * @exception Non-existing index.
265ffe3c632Sopenharmony_ci */
266ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, offsetGet) {
267ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
268ffe3c632Sopenharmony_ci  zend_long index;
269ffe3c632Sopenharmony_ci  upb_msgval msgval;
270ffe3c632Sopenharmony_ci  zval ret;
271ffe3c632Sopenharmony_ci
272ffe3c632Sopenharmony_ci  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
273ffe3c632Sopenharmony_ci    return;
274ffe3c632Sopenharmony_ci  }
275ffe3c632Sopenharmony_ci
276ffe3c632Sopenharmony_ci  if (index < 0 || index >= upb_array_size(intern->array)) {
277ffe3c632Sopenharmony_ci    zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index);
278ffe3c632Sopenharmony_ci    return;
279ffe3c632Sopenharmony_ci  }
280ffe3c632Sopenharmony_ci
281ffe3c632Sopenharmony_ci  msgval = upb_array_get(intern->array, index);
282ffe3c632Sopenharmony_ci  Convert_UpbToPhp(msgval, &ret, intern->type, intern->desc, &intern->arena);
283ffe3c632Sopenharmony_ci  RETURN_ZVAL(&ret, 0, 1);
284ffe3c632Sopenharmony_ci}
285ffe3c632Sopenharmony_ci
286ffe3c632Sopenharmony_ci/**
287ffe3c632Sopenharmony_ci * RepeatedField::offsetSet()
288ffe3c632Sopenharmony_ci *
289ffe3c632Sopenharmony_ci * Implements the ArrayAccess interface. Invoked when PHP code calls:
290ffe3c632Sopenharmony_ci *
291ffe3c632Sopenharmony_ci *   $arr[$idx] = $x;
292ffe3c632Sopenharmony_ci *   $arr []= $x;  // Append
293ffe3c632Sopenharmony_ci *
294ffe3c632Sopenharmony_ci * @param long The index of the element to be assigned.
295ffe3c632Sopenharmony_ci * @param object The element to be assigned.
296ffe3c632Sopenharmony_ci * @exception Invalid type for index.
297ffe3c632Sopenharmony_ci * @exception Non-existing index.
298ffe3c632Sopenharmony_ci * @exception Incorrect type of the element.
299ffe3c632Sopenharmony_ci */
300ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, offsetSet) {
301ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
302ffe3c632Sopenharmony_ci  upb_arena *arena = Arena_Get(&intern->arena);
303ffe3c632Sopenharmony_ci  size_t size = upb_array_size(intern->array);
304ffe3c632Sopenharmony_ci  zval *offset, *val;
305ffe3c632Sopenharmony_ci  int64_t index;
306ffe3c632Sopenharmony_ci  upb_msgval msgval;
307ffe3c632Sopenharmony_ci
308ffe3c632Sopenharmony_ci  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &offset, &val) != SUCCESS) {
309ffe3c632Sopenharmony_ci    return;
310ffe3c632Sopenharmony_ci  }
311ffe3c632Sopenharmony_ci
312ffe3c632Sopenharmony_ci  if (Z_TYPE_P(offset) == IS_NULL) {
313ffe3c632Sopenharmony_ci    index = size;
314ffe3c632Sopenharmony_ci  } else if (!Convert_PhpToInt64(offset, &index)) {
315ffe3c632Sopenharmony_ci    return;
316ffe3c632Sopenharmony_ci  }
317ffe3c632Sopenharmony_ci
318ffe3c632Sopenharmony_ci  if (!Convert_PhpToUpb(val, &msgval, intern->type, intern->desc, arena)) {
319ffe3c632Sopenharmony_ci    return;
320ffe3c632Sopenharmony_ci  }
321ffe3c632Sopenharmony_ci
322ffe3c632Sopenharmony_ci  if (index > size) {
323ffe3c632Sopenharmony_ci    zend_error(E_USER_ERROR, "Element at index %ld doesn't exist.\n", index);
324ffe3c632Sopenharmony_ci  } else if (index == size) {
325ffe3c632Sopenharmony_ci    upb_array_append(intern->array, msgval, Arena_Get(&intern->arena));
326ffe3c632Sopenharmony_ci  } else {
327ffe3c632Sopenharmony_ci    upb_array_set(intern->array, index, msgval);
328ffe3c632Sopenharmony_ci  }
329ffe3c632Sopenharmony_ci}
330ffe3c632Sopenharmony_ci
331ffe3c632Sopenharmony_ci/**
332ffe3c632Sopenharmony_ci * RepeatedField::offsetUnset()
333ffe3c632Sopenharmony_ci *
334ffe3c632Sopenharmony_ci * Implements the ArrayAccess interface. Invoked when PHP code calls:
335ffe3c632Sopenharmony_ci *
336ffe3c632Sopenharmony_ci *   unset($arr[$idx]);
337ffe3c632Sopenharmony_ci *
338ffe3c632Sopenharmony_ci * @param long The index of the element to be removed.
339ffe3c632Sopenharmony_ci * @exception Invalid type for index.
340ffe3c632Sopenharmony_ci * @exception The element to be removed is not at the end of the RepeatedField.
341ffe3c632Sopenharmony_ci */
342ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, offsetUnset) {
343ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
344ffe3c632Sopenharmony_ci  zend_long index;
345ffe3c632Sopenharmony_ci  zend_long size = upb_array_size(intern->array);
346ffe3c632Sopenharmony_ci
347ffe3c632Sopenharmony_ci  // Only the element at the end of the array can be removed.
348ffe3c632Sopenharmony_ci  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) != SUCCESS) {
349ffe3c632Sopenharmony_ci    return;
350ffe3c632Sopenharmony_ci  }
351ffe3c632Sopenharmony_ci
352ffe3c632Sopenharmony_ci  if (size == 0 || index != size - 1) {
353ffe3c632Sopenharmony_ci    php_error_docref(NULL, E_USER_ERROR, "Cannot remove element at %ld.\n",
354ffe3c632Sopenharmony_ci                     index);
355ffe3c632Sopenharmony_ci    return;
356ffe3c632Sopenharmony_ci  }
357ffe3c632Sopenharmony_ci
358ffe3c632Sopenharmony_ci  upb_array_resize(intern->array, size - 1, Arena_Get(&intern->arena));
359ffe3c632Sopenharmony_ci}
360ffe3c632Sopenharmony_ci
361ffe3c632Sopenharmony_ci/**
362ffe3c632Sopenharmony_ci * RepeatedField::count()
363ffe3c632Sopenharmony_ci *
364ffe3c632Sopenharmony_ci * Implements the Countable interface. Invoked when PHP code calls:
365ffe3c632Sopenharmony_ci *
366ffe3c632Sopenharmony_ci *   $len = count($arr);
367ffe3c632Sopenharmony_ci * Return the number of stored elements.
368ffe3c632Sopenharmony_ci * This will also be called for: count($arr)
369ffe3c632Sopenharmony_ci * @return long The number of stored elements.
370ffe3c632Sopenharmony_ci */
371ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, count) {
372ffe3c632Sopenharmony_ci  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
373ffe3c632Sopenharmony_ci
374ffe3c632Sopenharmony_ci  if (zend_parse_parameters_none() == FAILURE) {
375ffe3c632Sopenharmony_ci    return;
376ffe3c632Sopenharmony_ci  }
377ffe3c632Sopenharmony_ci
378ffe3c632Sopenharmony_ci  RETURN_LONG(upb_array_size(intern->array));
379ffe3c632Sopenharmony_ci}
380ffe3c632Sopenharmony_ci
381ffe3c632Sopenharmony_ci/**
382ffe3c632Sopenharmony_ci * RepeatedField::getIterator()
383ffe3c632Sopenharmony_ci *
384ffe3c632Sopenharmony_ci * Implements the IteratorAggregate interface. Invoked when PHP code calls:
385ffe3c632Sopenharmony_ci *
386ffe3c632Sopenharmony_ci *   foreach ($arr) {}
387ffe3c632Sopenharmony_ci *
388ffe3c632Sopenharmony_ci * @return object Beginning iterator.
389ffe3c632Sopenharmony_ci */
390ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedField, getIterator) {
391ffe3c632Sopenharmony_ci  zval ret;
392ffe3c632Sopenharmony_ci  RepeatedFieldIter_make(&ret, getThis());
393ffe3c632Sopenharmony_ci  RETURN_ZVAL(&ret, 0, 1);
394ffe3c632Sopenharmony_ci}
395ffe3c632Sopenharmony_ci
396ffe3c632Sopenharmony_ciZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 1)
397ffe3c632Sopenharmony_ci  ZEND_ARG_INFO(0, type)
398ffe3c632Sopenharmony_ci  ZEND_ARG_INFO(0, class)
399ffe3c632Sopenharmony_ciZEND_END_ARG_INFO()
400ffe3c632Sopenharmony_ci
401ffe3c632Sopenharmony_ciZEND_BEGIN_ARG_INFO_EX(arginfo_append, 0, 0, 1)
402ffe3c632Sopenharmony_ci  ZEND_ARG_INFO(0, newval)
403ffe3c632Sopenharmony_ciZEND_END_ARG_INFO()
404ffe3c632Sopenharmony_ci
405ffe3c632Sopenharmony_ciZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
406ffe3c632Sopenharmony_ci  ZEND_ARG_INFO(0, index)
407ffe3c632Sopenharmony_ciZEND_END_ARG_INFO()
408ffe3c632Sopenharmony_ci
409ffe3c632Sopenharmony_ciZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
410ffe3c632Sopenharmony_ci  ZEND_ARG_INFO(0, index)
411ffe3c632Sopenharmony_ci  ZEND_ARG_INFO(0, newval)
412ffe3c632Sopenharmony_ciZEND_END_ARG_INFO()
413ffe3c632Sopenharmony_ci
414ffe3c632Sopenharmony_ciZEND_BEGIN_ARG_INFO(arginfo_void, 0)
415ffe3c632Sopenharmony_ciZEND_END_ARG_INFO()
416ffe3c632Sopenharmony_ci
417ffe3c632Sopenharmony_cistatic zend_function_entry repeated_field_methods[] = {
418ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, __construct,  arginfo_construct, ZEND_ACC_PUBLIC)
419ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, append,       arginfo_append,    ZEND_ACC_PUBLIC)
420ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
421ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
422ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
423ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
424ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
425ffe3c632Sopenharmony_ci  PHP_ME(RepeatedField, getIterator,  arginfo_void,      ZEND_ACC_PUBLIC)
426ffe3c632Sopenharmony_ci  ZEND_FE_END
427ffe3c632Sopenharmony_ci};
428ffe3c632Sopenharmony_ci
429ffe3c632Sopenharmony_ci// -----------------------------------------------------------------------------
430ffe3c632Sopenharmony_ci// PHP RepeatedFieldIter
431ffe3c632Sopenharmony_ci// -----------------------------------------------------------------------------
432ffe3c632Sopenharmony_ci
433ffe3c632Sopenharmony_citypedef struct {
434ffe3c632Sopenharmony_ci  zend_object std;
435ffe3c632Sopenharmony_ci  zval repeated_field;
436ffe3c632Sopenharmony_ci  zend_long position;
437ffe3c632Sopenharmony_ci} RepeatedFieldIter;
438ffe3c632Sopenharmony_ci
439ffe3c632Sopenharmony_cizend_class_entry *RepeatedFieldIter_class_entry;
440ffe3c632Sopenharmony_cistatic zend_object_handlers repeated_field_iter_object_handlers;
441ffe3c632Sopenharmony_ci
442ffe3c632Sopenharmony_ci/**
443ffe3c632Sopenharmony_ci * RepeatedFieldIter_create()
444ffe3c632Sopenharmony_ci *
445ffe3c632Sopenharmony_ci * PHP class entry function to allocate and initialize a new RepeatedFieldIter
446ffe3c632Sopenharmony_ci * object.
447ffe3c632Sopenharmony_ci */
448ffe3c632Sopenharmony_cizend_object* RepeatedFieldIter_create(zend_class_entry *class_type) {
449ffe3c632Sopenharmony_ci  RepeatedFieldIter *intern = emalloc(sizeof(RepeatedFieldIter));
450ffe3c632Sopenharmony_ci  zend_object_std_init(&intern->std, class_type);
451ffe3c632Sopenharmony_ci  intern->std.handlers = &repeated_field_iter_object_handlers;
452ffe3c632Sopenharmony_ci  ZVAL_NULL(&intern->repeated_field);
453ffe3c632Sopenharmony_ci  intern->position = 0;
454ffe3c632Sopenharmony_ci  // Skip object_properties_init(), we don't allow derived classes.
455ffe3c632Sopenharmony_ci  return &intern->std;
456ffe3c632Sopenharmony_ci}
457ffe3c632Sopenharmony_ci
458ffe3c632Sopenharmony_ci/**
459ffe3c632Sopenharmony_ci * RepeatedFieldIter_dtor()
460ffe3c632Sopenharmony_ci *
461ffe3c632Sopenharmony_ci * Object handler to destroy a RepeatedFieldIter. This releases all resources
462ffe3c632Sopenharmony_ci * associated with the message. Note that it is possible to access a destroyed
463ffe3c632Sopenharmony_ci * object from PHP in rare cases.
464ffe3c632Sopenharmony_ci */
465ffe3c632Sopenharmony_cistatic void RepeatedFieldIter_dtor(zend_object* obj) {
466ffe3c632Sopenharmony_ci  RepeatedFieldIter* intern = (RepeatedFieldIter*)obj;
467ffe3c632Sopenharmony_ci  zval_ptr_dtor(&intern->repeated_field);
468ffe3c632Sopenharmony_ci  zend_object_std_dtor(&intern->std);
469ffe3c632Sopenharmony_ci}
470ffe3c632Sopenharmony_ci
471ffe3c632Sopenharmony_ci/**
472ffe3c632Sopenharmony_ci * RepeatedFieldIter_make()
473ffe3c632Sopenharmony_ci *
474ffe3c632Sopenharmony_ci * C function to create a RepeatedFieldIter.
475ffe3c632Sopenharmony_ci */
476ffe3c632Sopenharmony_cistatic void RepeatedFieldIter_make(zval *val, zval *repeated_field) {
477ffe3c632Sopenharmony_ci  RepeatedFieldIter *iter;
478ffe3c632Sopenharmony_ci  ZVAL_OBJ(val, RepeatedFieldIter_class_entry->create_object(
479ffe3c632Sopenharmony_ci                    RepeatedFieldIter_class_entry));
480ffe3c632Sopenharmony_ci  iter = (RepeatedFieldIter*)Z_OBJ_P(val);
481ffe3c632Sopenharmony_ci  ZVAL_COPY(&iter->repeated_field, repeated_field);
482ffe3c632Sopenharmony_ci}
483ffe3c632Sopenharmony_ci
484ffe3c632Sopenharmony_ci/*
485ffe3c632Sopenharmony_ci * When a user writes:
486ffe3c632Sopenharmony_ci *
487ffe3c632Sopenharmony_ci *   foreach($arr as $key => $val) {}
488ffe3c632Sopenharmony_ci *
489ffe3c632Sopenharmony_ci * PHP's iterator protocol is:
490ffe3c632Sopenharmony_ci *
491ffe3c632Sopenharmony_ci *   $iter = $arr->getIterator();
492ffe3c632Sopenharmony_ci *   for ($iter->rewind(); $iter->valid(); $iter->next()) {
493ffe3c632Sopenharmony_ci *     $key = $iter->key();
494ffe3c632Sopenharmony_ci *     $val = $iter->current();
495ffe3c632Sopenharmony_ci *   }
496ffe3c632Sopenharmony_ci */
497ffe3c632Sopenharmony_ci
498ffe3c632Sopenharmony_ci/**
499ffe3c632Sopenharmony_ci * RepeatedFieldIter::rewind()
500ffe3c632Sopenharmony_ci *
501ffe3c632Sopenharmony_ci * Implements the Iterator interface. Sets the iterator to the first element.
502ffe3c632Sopenharmony_ci */
503ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedFieldIter, rewind) {
504ffe3c632Sopenharmony_ci  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
505ffe3c632Sopenharmony_ci  intern->position = 0;
506ffe3c632Sopenharmony_ci}
507ffe3c632Sopenharmony_ci
508ffe3c632Sopenharmony_ci/**
509ffe3c632Sopenharmony_ci * RepeatedFieldIter::current()
510ffe3c632Sopenharmony_ci *
511ffe3c632Sopenharmony_ci * Implements the Iterator interface. Returns the current value.
512ffe3c632Sopenharmony_ci */
513ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedFieldIter, current) {
514ffe3c632Sopenharmony_ci  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
515ffe3c632Sopenharmony_ci  RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field);
516ffe3c632Sopenharmony_ci  upb_array *array = field->array;
517ffe3c632Sopenharmony_ci  zend_long index = intern->position;
518ffe3c632Sopenharmony_ci  upb_msgval msgval;
519ffe3c632Sopenharmony_ci  zval ret;
520ffe3c632Sopenharmony_ci
521ffe3c632Sopenharmony_ci  if (index < 0 || index >= upb_array_size(array)) {
522ffe3c632Sopenharmony_ci    zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index);
523ffe3c632Sopenharmony_ci  }
524ffe3c632Sopenharmony_ci
525ffe3c632Sopenharmony_ci  msgval = upb_array_get(array, index);
526ffe3c632Sopenharmony_ci
527ffe3c632Sopenharmony_ci  Convert_UpbToPhp(msgval, &ret, field->type, field->desc, &field->arena);
528ffe3c632Sopenharmony_ci  RETURN_ZVAL(&ret, 0, 1);
529ffe3c632Sopenharmony_ci}
530ffe3c632Sopenharmony_ci
531ffe3c632Sopenharmony_ci/**
532ffe3c632Sopenharmony_ci * RepeatedFieldIter::key()
533ffe3c632Sopenharmony_ci *
534ffe3c632Sopenharmony_ci * Implements the Iterator interface. Returns the current key.
535ffe3c632Sopenharmony_ci */
536ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedFieldIter, key) {
537ffe3c632Sopenharmony_ci  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
538ffe3c632Sopenharmony_ci  RETURN_LONG(intern->position);
539ffe3c632Sopenharmony_ci}
540ffe3c632Sopenharmony_ci
541ffe3c632Sopenharmony_ci/**
542ffe3c632Sopenharmony_ci * RepeatedFieldIter::next()
543ffe3c632Sopenharmony_ci *
544ffe3c632Sopenharmony_ci * Implements the Iterator interface. Advances to the next element.
545ffe3c632Sopenharmony_ci */
546ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedFieldIter, next) {
547ffe3c632Sopenharmony_ci  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
548ffe3c632Sopenharmony_ci  ++intern->position;
549ffe3c632Sopenharmony_ci}
550ffe3c632Sopenharmony_ci
551ffe3c632Sopenharmony_ci/**
552ffe3c632Sopenharmony_ci * RepeatedFieldIter::valid()
553ffe3c632Sopenharmony_ci *
554ffe3c632Sopenharmony_ci * Implements the Iterator interface. Returns true if this is a valid element.
555ffe3c632Sopenharmony_ci */
556ffe3c632Sopenharmony_ciPHP_METHOD(RepeatedFieldIter, valid) {
557ffe3c632Sopenharmony_ci  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
558ffe3c632Sopenharmony_ci  RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field);
559ffe3c632Sopenharmony_ci  RETURN_BOOL(intern->position < upb_array_size(field->array));
560ffe3c632Sopenharmony_ci}
561ffe3c632Sopenharmony_ci
562ffe3c632Sopenharmony_cistatic zend_function_entry repeated_field_iter_methods[] = {
563ffe3c632Sopenharmony_ci  PHP_ME(RepeatedFieldIter, rewind,      arginfo_void, ZEND_ACC_PUBLIC)
564ffe3c632Sopenharmony_ci  PHP_ME(RepeatedFieldIter, current,     arginfo_void, ZEND_ACC_PUBLIC)
565ffe3c632Sopenharmony_ci  PHP_ME(RepeatedFieldIter, key,         arginfo_void, ZEND_ACC_PUBLIC)
566ffe3c632Sopenharmony_ci  PHP_ME(RepeatedFieldIter, next,        arginfo_void, ZEND_ACC_PUBLIC)
567ffe3c632Sopenharmony_ci  PHP_ME(RepeatedFieldIter, valid,       arginfo_void, ZEND_ACC_PUBLIC)
568ffe3c632Sopenharmony_ci  ZEND_FE_END
569ffe3c632Sopenharmony_ci};
570ffe3c632Sopenharmony_ci
571ffe3c632Sopenharmony_ci// -----------------------------------------------------------------------------
572ffe3c632Sopenharmony_ci// Module init.
573ffe3c632Sopenharmony_ci// -----------------------------------------------------------------------------
574ffe3c632Sopenharmony_ci
575ffe3c632Sopenharmony_ci/**
576ffe3c632Sopenharmony_ci * Array_ModuleInit()
577ffe3c632Sopenharmony_ci *
578ffe3c632Sopenharmony_ci * Called when the C extension is loaded to register all types.
579ffe3c632Sopenharmony_ci */
580ffe3c632Sopenharmony_civoid Array_ModuleInit() {
581ffe3c632Sopenharmony_ci  zend_class_entry tmp_ce;
582ffe3c632Sopenharmony_ci  zend_object_handlers *h;
583ffe3c632Sopenharmony_ci
584ffe3c632Sopenharmony_ci  // RepeatedField.
585ffe3c632Sopenharmony_ci  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedField",
586ffe3c632Sopenharmony_ci                   repeated_field_methods);
587ffe3c632Sopenharmony_ci
588ffe3c632Sopenharmony_ci  RepeatedField_class_entry = zend_register_internal_class(&tmp_ce);
589ffe3c632Sopenharmony_ci  zend_class_implements(RepeatedField_class_entry, 3, spl_ce_ArrayAccess,
590ffe3c632Sopenharmony_ci                        zend_ce_aggregate, spl_ce_Countable);
591ffe3c632Sopenharmony_ci  RepeatedField_class_entry->ce_flags |= ZEND_ACC_FINAL;
592ffe3c632Sopenharmony_ci  RepeatedField_class_entry->create_object = RepeatedField_create;
593ffe3c632Sopenharmony_ci
594ffe3c632Sopenharmony_ci  h = &RepeatedField_object_handlers;
595ffe3c632Sopenharmony_ci  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
596ffe3c632Sopenharmony_ci  h->dtor_obj = RepeatedField_destructor;
597ffe3c632Sopenharmony_ci  h->get_properties = RepeatedField_GetProperties;
598ffe3c632Sopenharmony_ci  h->get_property_ptr_ptr = RepeatedField_GetPropertyPtrPtr;
599ffe3c632Sopenharmony_ci
600ffe3c632Sopenharmony_ci  // RepeatedFieldIter
601ffe3c632Sopenharmony_ci  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedFieldIter",
602ffe3c632Sopenharmony_ci                   repeated_field_iter_methods);
603ffe3c632Sopenharmony_ci
604ffe3c632Sopenharmony_ci  RepeatedFieldIter_class_entry = zend_register_internal_class(&tmp_ce);
605ffe3c632Sopenharmony_ci  zend_class_implements(RepeatedFieldIter_class_entry, 1, zend_ce_iterator);
606ffe3c632Sopenharmony_ci  RepeatedFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
607ffe3c632Sopenharmony_ci  RepeatedFieldIter_class_entry->create_object = RepeatedFieldIter_create;
608ffe3c632Sopenharmony_ci
609ffe3c632Sopenharmony_ci  h = &repeated_field_iter_object_handlers;
610ffe3c632Sopenharmony_ci  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
611ffe3c632Sopenharmony_ci  h->dtor_obj = RepeatedFieldIter_dtor;
612ffe3c632Sopenharmony_ci}
613