1 /*
2    +----------------------------------------------------------------------+
3    | This source file is subject to version 3.01 of the PHP license,      |
4    | that is bundled with this package in the file LICENSE, and is        |
5    | available through the world-wide-web at the following url:           |
6    | https://www.php.net/license/3_01.txt                                 |
7    | If you did not receive a copy of the PHP license and are unable to   |
8    | obtain it through the world-wide-web, please send a note to          |
9    | license@php.net so we can mail you a copy immediately.               |
10    +----------------------------------------------------------------------+
11    | Authors: Gustavo Lopes <cataphract@php.net>                          |
12    +----------------------------------------------------------------------+
13 */
14 
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18 
19 #include <unicode/brkiter.h>
20 
21 #include "breakiterator_iterators.h"
22 #include "../common/common_enum.h"
23 
24 extern "C" {
25 #define USE_BREAKITERATOR_POINTER
26 #include "breakiterator_class.h"
27 #include "breakiterator_iterators_arginfo.h"
28 #include "../intl_convert.h"
29 #include "../locale/locale.h"
30 #include <zend_exceptions.h>
31 #include <zend_interfaces.h>
32 }
33 
34 static zend_class_entry *IntlPartsIterator_ce_ptr;
35 
36 /* BreakIterator's iterator */
37 
_breakiter_prolog(zend_object_iterator * iter)38 inline BreakIterator *_breakiter_prolog(zend_object_iterator *iter)
39 {
40 	BreakIterator_object *bio;
41 	bio = Z_INTL_BREAKITERATOR_P(&iter->data);
42 	intl_errors_reset(BREAKITER_ERROR_P(bio));
43 	if (bio->biter == NULL) {
44 		intl_errors_set(BREAKITER_ERROR_P(bio), U_INVALID_STATE_ERROR,
45 			"The BreakIterator object backing the PHP iterator is not "
46 			"properly constructed", 0);
47 	}
48 	return bio->biter;
49 }
50 
_breakiterator_destroy_it(zend_object_iterator * iter)51 static void _breakiterator_destroy_it(zend_object_iterator *iter)
52 {
53 	zval_ptr_dtor(&iter->data);
54 }
55 
_breakiterator_move_forward(zend_object_iterator * iter)56 static void _breakiterator_move_forward(zend_object_iterator *iter)
57 {
58 	BreakIterator *biter = _breakiter_prolog(iter);
59 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
60 
61 	iter->funcs->invalidate_current(iter);
62 
63 	if (biter == NULL) {
64 		return;
65 	}
66 
67 	int32_t pos = biter->next();
68 	if (pos != BreakIterator::DONE) {
69 		ZVAL_LONG(&zoi_iter->current, (zend_long)pos);
70 	} //else we've reached the end of the enum, nothing more is required
71 }
72 
_breakiterator_rewind(zend_object_iterator * iter)73 static void _breakiterator_rewind(zend_object_iterator *iter)
74 {
75 	BreakIterator *biter = _breakiter_prolog(iter);
76 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
77 
78 	int32_t pos = biter->first();
79 	ZVAL_LONG(&zoi_iter->current, (zend_long)pos);
80 }
81 
82 static const zend_object_iterator_funcs breakiterator_iterator_funcs = {
83 	zoi_with_current_dtor,
84 	zoi_with_current_valid,
85 	zoi_with_current_get_current_data,
86 	NULL,
87 	_breakiterator_move_forward,
88 	_breakiterator_rewind,
89 	zoi_with_current_invalidate_current,
90 	NULL, /* get_gc */
91 };
92 
_breakiterator_get_iterator(zend_class_entry * ce,zval * object,int by_ref)93 U_CFUNC zend_object_iterator *_breakiterator_get_iterator(
94 	zend_class_entry *ce, zval *object, int by_ref)
95 {
96 	BreakIterator_object *bio;
97 	if (by_ref) {
98 		zend_throw_exception(NULL,
99 			"Iteration by reference is not supported", 0);
100 		return NULL;
101 	}
102 
103 	bio = Z_INTL_BREAKITERATOR_P(object);
104 	BreakIterator *biter = bio->biter;
105 
106 	if (biter == NULL) {
107 		zend_throw_exception(NULL,
108 			"The BreakIterator is not properly constructed", 0);
109 		return NULL;
110 	}
111 
112 	zoi_with_current *zoi_iter = static_cast<zoi_with_current*>(emalloc(sizeof *zoi_iter));
113 	zend_iterator_init(&zoi_iter->zoi);
114 	ZVAL_OBJ_COPY(&zoi_iter->zoi.data, Z_OBJ_P(object));
115 	zoi_iter->zoi.funcs = &breakiterator_iterator_funcs;
116 	zoi_iter->zoi.index = 0;
117 	zoi_iter->destroy_it = _breakiterator_destroy_it;
118 	ZVAL_UNDEF(&zoi_iter->wrapping_obj); /* not used; object is in zoi.data */
119 	ZVAL_UNDEF(&zoi_iter->current);
120 
121 	return reinterpret_cast<zend_object_iterator *>(zoi_iter);
122 }
123 
124 /* BreakIterator parts iterator */
125 
126 typedef struct zoi_break_iter_parts {
127 	zoi_with_current zoi_cur;
128 	parts_iter_key_type key_type;
129 	BreakIterator_object *bio; /* so we don't have to fetch it all the time */
130 	zend_ulong index_right;
131 } zoi_break_iter_parts;
132 
_breakiterator_parts_destroy_it(zend_object_iterator * iter)133 static void _breakiterator_parts_destroy_it(zend_object_iterator *iter)
134 {
135 	zval_ptr_dtor(&iter->data);
136 }
137 
_breakiterator_parts_get_current_key(zend_object_iterator * iter,zval * key)138 static void _breakiterator_parts_get_current_key(zend_object_iterator *iter, zval *key)
139 {
140 	// The engine resets the iterator index to -1 after rewinding. When using
141 	// PARTS_ITERATOR_KEY_RIGHT we store it in zoi_break_iter_parts.index_right
142 	// so it doesn't get lost.
143 	zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
144 
145 	if (zoi_bit->key_type == PARTS_ITERATOR_KEY_RIGHT && iter->index == 0) {
146 		ZVAL_LONG(key, zoi_bit->index_right);
147 	} else {
148 		ZVAL_LONG(key, iter->index);
149 	}
150 }
151 
_breakiterator_parts_move_forward(zend_object_iterator * iter)152 static void _breakiterator_parts_move_forward(zend_object_iterator *iter)
153 {
154 	zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
155 	BreakIterator_object *bio = zoi_bit->bio;
156 
157 	iter->funcs->invalidate_current(iter);
158 
159 	int32_t cur,
160 			next;
161 
162 	cur = bio->biter->current();
163 	if (cur == BreakIterator::DONE) {
164 		return;
165 	}
166 	next = bio->biter->next();
167 	if (next == BreakIterator::DONE) {
168 		return;
169 	}
170 
171 	if (zoi_bit->key_type == PARTS_ITERATOR_KEY_LEFT) {
172 		iter->index = cur;
173 	} else if (zoi_bit->key_type == PARTS_ITERATOR_KEY_RIGHT) {
174 		iter->index = next;
175 		zoi_bit->index_right = next;
176 	}
177 	/* else zoi_bit->key_type == PARTS_ITERATOR_KEY_SEQUENTIAL
178 	 * No need to do anything, the engine increments ->index */
179 
180 	const char	*s = Z_STRVAL(bio->text);
181 	zend_string	*res;
182 
183 	assert(next <= Z_STRLEN(bio->text) && next >= cur);
184 	res = zend_string_alloc(next - cur, 0);
185 
186 	memcpy(ZSTR_VAL(res), &s[cur], ZSTR_LEN(res));
187 	ZSTR_VAL(res)[ZSTR_LEN(res)] = '\0';
188 
189 	ZVAL_STR(&zoi_bit->zoi_cur.current, res);
190 }
191 
_breakiterator_parts_rewind(zend_object_iterator * iter)192 static void _breakiterator_parts_rewind(zend_object_iterator *iter)
193 {
194 	zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
195 	BreakIterator_object *bio = zoi_bit->bio;
196 
197 	if (!Z_ISUNDEF(zoi_bit->zoi_cur.current)) {
198 		iter->funcs->invalidate_current(iter);
199 	}
200 
201 	bio->biter->first();
202 
203 	iter->funcs->move_forward(iter);
204 }
205 
206 static const zend_object_iterator_funcs breakiterator_parts_it_funcs = {
207 	zoi_with_current_dtor,
208 	zoi_with_current_valid,
209 	zoi_with_current_get_current_data,
210 	_breakiterator_parts_get_current_key,
211 	_breakiterator_parts_move_forward,
212 	_breakiterator_parts_rewind,
213 	zoi_with_current_invalidate_current,
214 	NULL, /* get_gc */
215 };
216 
IntlIterator_from_BreakIterator_parts(zval * break_iter_zv,zval * object,parts_iter_key_type key_type)217 void IntlIterator_from_BreakIterator_parts(zval *break_iter_zv,
218 										   zval *object,
219 										   parts_iter_key_type key_type)
220 {
221 	IntlIterator_object *ii;
222 
223 	object_init_ex(object, IntlPartsIterator_ce_ptr);
224 	ii = Z_INTL_ITERATOR_P(object);
225 
226 	ii->iterator = (zend_object_iterator*)emalloc(sizeof(zoi_break_iter_parts));
227 	zend_iterator_init(ii->iterator);
228 
229 	ZVAL_COPY(&ii->iterator->data, break_iter_zv);
230 	ii->iterator->funcs = &breakiterator_parts_it_funcs;
231 	ii->iterator->index = 0;
232 
233 	((zoi_with_current*)ii->iterator)->destroy_it = _breakiterator_parts_destroy_it;
234 	ZVAL_OBJ(&((zoi_with_current*)ii->iterator)->wrapping_obj, Z_OBJ_P(object));
235 	ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->current);
236 
237 	((zoi_break_iter_parts*)ii->iterator)->bio = Z_INTL_BREAKITERATOR_P(break_iter_zv);
238 
239 	assert(((zoi_break_iter_parts*)ii->iterator)->bio->biter != NULL);
240 
241 	((zoi_break_iter_parts*)ii->iterator)->key_type = key_type;
242 	((zoi_break_iter_parts*)ii->iterator)->index_right = 0;
243 }
244 
PHP_METHOD(IntlPartsIterator,getBreakIterator)245 U_CFUNC PHP_METHOD(IntlPartsIterator, getBreakIterator)
246 {
247 	INTLITERATOR_METHOD_INIT_VARS;
248 
249 	if (zend_parse_parameters_none() == FAILURE) {
250 		RETURN_THROWS();
251 	}
252 
253 	INTLITERATOR_METHOD_FETCH_OBJECT;
254 
255 	RETURN_COPY_DEREF(&ii->iterator->data);
256 }
257 
PHP_METHOD(IntlPartsIterator,getRuleStatus)258 U_CFUNC PHP_METHOD(IntlPartsIterator, getRuleStatus)
259 {
260 	INTLITERATOR_METHOD_INIT_VARS;
261 
262 	if (zend_parse_parameters_none() == FAILURE) {
263 		RETURN_THROWS();
264 	}
265 
266 	INTLITERATOR_METHOD_FETCH_OBJECT;
267 
268 	zval *iter = &ii->iterator->data;
269 	ZEND_ASSERT(Z_TYPE_P(iter) == IS_OBJECT);
270 	zend_call_method_with_0_params(
271 			Z_OBJ_P(iter), Z_OBJCE_P(iter), NULL, "getrulestatus", return_value);
272 }
273 
breakiterator_register_IntlPartsIterator_class(void)274 U_CFUNC void breakiterator_register_IntlPartsIterator_class(void)
275 {
276 	/* Create and register 'BreakIterator' class. */
277 	IntlPartsIterator_ce_ptr = register_class_IntlPartsIterator(IntlIterator_ce_ptr);
278 
279 #define PARTSITER_DECL_LONG_CONST(name) \
280 	zend_declare_class_constant_long(IntlPartsIterator_ce_ptr, #name, \
281 		sizeof(#name) - 1, PARTS_ITERATOR_ ## name)
282 
283 	PARTSITER_DECL_LONG_CONST(KEY_SEQUENTIAL);
284 	PARTSITER_DECL_LONG_CONST(KEY_LEFT);
285 	PARTSITER_DECL_LONG_CONST(KEY_RIGHT);
286 
287 #undef PARTSITER_DECL_LONG_CONST
288 }
289