xref: /PHP-7.4/ext/intl/collator/collator_sort.c (revision 83804519)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Vadim Savchuk <vsavchuk@productengine.com>                  |
14    |          Dmitry Lakhtyuk <dlakhtyuk@productengine.com>               |
15    +----------------------------------------------------------------------+
16  */
17 
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include "php_intl.h"
23 #include "collator.h"
24 #include "collator_class.h"
25 #include "collator_sort.h"
26 #include "collator_convert.h"
27 #include "intl_convert.h"
28 
29 #if !defined(HAVE_PTRDIFF_T) && !defined(_PTRDIFF_T_DEFINED)
30 typedef zend_long ptrdiff_t;
31 #endif
32 
33 /**
34  * Declare 'index' which will point to sort key in sort key
35  * buffer.
36  */
37 typedef struct _collator_sort_key_index {
38 	char* key;       /* pointer to sort key */
39 	zval* zstr;     /* pointer to original string(hash-item) */
40 } collator_sort_key_index_t;
41 
42 ZEND_EXTERN_MODULE_GLOBALS( intl )
43 
44 static const size_t DEF_SORT_KEYS_BUF_SIZE = 1048576;
45 static const size_t DEF_SORT_KEYS_BUF_INCREMENT = 1048576;
46 
47 static const size_t DEF_SORT_KEYS_INDX_BUF_SIZE = 1048576;
48 static const size_t DEF_SORT_KEYS_INDX_BUF_INCREMENT = 1048576;
49 
50 static const size_t DEF_UTF16_BUF_SIZE = 1024;
51 
52 /* {{{ collator_regular_compare_function */
collator_regular_compare_function(zval * result,zval * op1,zval * op2)53 static int collator_regular_compare_function(zval *result, zval *op1, zval *op2)
54 {
55 	Collator_object* co = NULL;
56 	int rc = SUCCESS;
57 	zval str1, str2;
58 	zval num1, num2;
59 	zval norm1, norm2;
60 	zval *num1_p = NULL, *num2_p = NULL;
61 	zval *norm1_p = NULL, *norm2_p = NULL;
62 	zval *str1_p, *str2_p;
63 
64 	ZVAL_NULL(&str1);
65 	str1_p  = collator_convert_object_to_string( op1, &str1 );
66 	ZVAL_NULL(&str2);
67 	str2_p  = collator_convert_object_to_string( op2, &str2 );
68 
69 	/* If both args are strings AND either of args is not numeric string
70 	 * then use ICU-compare. Otherwise PHP-compare. */
71 	if( Z_TYPE_P(str1_p) == IS_STRING && Z_TYPE_P(str2_p) == IS_STRING &&
72 		( str1_p == ( num1_p = collator_convert_string_to_number_if_possible( str1_p, &num1 ) ) ||
73 		  str2_p == ( num2_p = collator_convert_string_to_number_if_possible( str2_p, &num2 ) ) ) )
74 	{
75 		/* Fetch collator object. */
76 		co = Z_INTL_COLLATOR_P(&INTL_G(current_collator));
77 
78 		if (!co || !co->ucoll) {
79 			intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
80 			intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
81 				"Object not initialized", 0 );
82 			zend_throw_error(NULL, "Object not initialized");
83 			rc = FAILURE;
84 			goto cleanup;
85 		}
86 
87 		/* Compare the strings using ICU. */
88 		ZVAL_LONG(result, ucol_strcoll(
89 					co->ucoll,
90 					INTL_Z_STRVAL_P(str1_p), INTL_Z_STRLEN_P(str1_p),
91 					INTL_Z_STRVAL_P(str2_p), INTL_Z_STRLEN_P(str2_p) ));
92 	}
93 	else
94 	{
95 		/* num1 is set if str1 and str2 are strings. */
96 		if( num1_p )
97 		{
98 			if( num1_p == str1_p )
99 			{
100 				/* str1 is string but not numeric string
101 				 * just convert it to utf8.
102 				 */
103 				norm1_p = collator_convert_zstr_utf16_to_utf8( str1_p, &norm1 );
104 
105 				/* num2 is not set but str2 is string => do normalization. */
106 				norm2_p = collator_normalize_sort_argument( str2_p, &norm2 );
107 			}
108 			else
109 			{
110 				/* str1 is numeric strings => passthru to PHP-compare. */
111 				Z_TRY_ADDREF_P(num1_p);
112 				norm1_p = num1_p;
113 
114 				/* str2 is numeric strings => passthru to PHP-compare. */
115 				Z_TRY_ADDREF_P(num2_p);
116 				norm2_p = num2_p;
117 			}
118 		}
119 		else
120 		{
121 			/* num1 is not set if str1 or str2 is not a string => do normalization. */
122 			norm1_p = collator_normalize_sort_argument( str1_p, &norm1 );
123 
124 			/* if num1 is not set then num2 is not set as well => do normalization. */
125 			norm2_p = collator_normalize_sort_argument( str2_p, &norm2 );
126 		}
127 
128 		rc = compare_function( result, norm1_p, norm2_p );
129 
130 		zval_ptr_dtor( norm1_p );
131 		zval_ptr_dtor( norm2_p );
132 	}
133 
134 cleanup:
135 	if( num1_p )
136 		zval_ptr_dtor( num1_p );
137 
138 	if( num2_p )
139 		zval_ptr_dtor( num2_p );
140 
141 	zval_ptr_dtor( str1_p );
142 	zval_ptr_dtor( str2_p );
143 
144 	return rc;
145 }
146 /* }}} */
147 
148 /* {{{ collator_numeric_compare_function
149  * Convert input args to double and compare it.
150  */
collator_numeric_compare_function(zval * result,zval * op1,zval * op2)151 static int collator_numeric_compare_function(zval *result, zval *op1, zval *op2)
152 {
153 	zval num1, num2;
154 	zval *num1_p = NULL;
155 	zval *num2_p = NULL;
156 
157 	if( Z_TYPE_P(op1) == IS_STRING )
158 	{
159 		num1_p = collator_convert_string_to_double( op1, &num1 );
160 		op1 = num1_p;
161 	}
162 
163 	if( Z_TYPE_P(op2) == IS_STRING )
164 	{
165 		num2_p = collator_convert_string_to_double( op2, &num2 );
166 		op2 = num2_p;
167 	}
168 
169 	ZVAL_LONG(result, numeric_compare_function(op1, op2));
170 
171 	if( num1_p )
172 		zval_ptr_dtor( num1_p );
173 	if( num2_p )
174 		zval_ptr_dtor( num2_p );
175 
176 	return SUCCESS;
177 }
178 /* }}} */
179 
180 /* {{{ collator_icu_compare_function
181  * Direct use of ucol_strcoll.
182 */
collator_icu_compare_function(zval * result,zval * op1,zval * op2)183 static int collator_icu_compare_function(zval *result, zval *op1, zval *op2)
184 {
185 	zval str1, str2;
186 	int rc              = SUCCESS;
187 	Collator_object* co = NULL;
188 	zval *str1_p        = NULL;
189 	zval *str2_p        = NULL;
190 
191 	str1_p = collator_make_printable_zval( op1, &str1);
192 	str2_p = collator_make_printable_zval( op2, &str2 );
193 
194 	/* Fetch collator object. */
195 	co = Z_INTL_COLLATOR_P(&INTL_G(current_collator));
196 
197 	/* Compare the strings using ICU. */
198 	ZVAL_LONG(result, ucol_strcoll(
199 				co->ucoll,
200 				INTL_Z_STRVAL_P(str1_p), INTL_Z_STRLEN_P(str1_p),
201 				INTL_Z_STRVAL_P(str2_p), INTL_Z_STRLEN_P(str2_p) ));
202 
203 	zval_ptr_dtor( str1_p );
204 	zval_ptr_dtor( str2_p );
205 
206 	return rc;
207 }
208 /* }}} */
209 
210 /* {{{ collator_compare_func
211  * Taken from PHP7 source (array_data_compare).
212  */
collator_compare_func(const void * a,const void * b)213 static int collator_compare_func( const void* a, const void* b )
214 {
215 	Bucket *f;
216 	Bucket *s;
217 	zval result;
218 	zval *first;
219 	zval *second;
220 
221 	f = (Bucket *) a;
222 	s = (Bucket *) b;
223 
224 	first = &f->val;
225 	second = &s->val;
226 
227 	if( INTL_G(compare_func)( &result, first, second) == FAILURE )
228 		return 0;
229 
230 	if( Z_TYPE(result) == IS_DOUBLE )
231 	{
232 		if( Z_DVAL(result) < 0 )
233 			return -1;
234 		else if( Z_DVAL(result) > 0 )
235 			return 1;
236 		else
237 			return 0;
238 	}
239 
240 	convert_to_long(&result);
241 
242 	if( Z_LVAL(result) < 0 )
243 		return -1;
244 	else if( Z_LVAL(result) > 0 )
245 		return 1;
246 
247 	return 0;
248 }
249 /* }}} */
250 
251 /* {{{ collator_cmp_sort_keys
252  * Compare sort keys
253  */
collator_cmp_sort_keys(const void * p1,const void * p2)254 static int collator_cmp_sort_keys( const void *p1, const void *p2 )
255 {
256 	char* key1 = ((collator_sort_key_index_t*)p1)->key;
257 	char* key2 = ((collator_sort_key_index_t*)p2)->key;
258 
259 	return strcmp( key1, key2 );
260 }
261 /* }}} */
262 
263 /* {{{ collator_get_compare_function
264  * Choose compare function according to sort flags.
265  */
collator_get_compare_function(const zend_long sort_flags)266 static collator_compare_func_t collator_get_compare_function( const zend_long sort_flags )
267 {
268 	collator_compare_func_t func;
269 
270 	switch( sort_flags )
271 	{
272 		case COLLATOR_SORT_NUMERIC:
273 			func = collator_numeric_compare_function;
274 			break;
275 
276 		case COLLATOR_SORT_STRING:
277 			func = collator_icu_compare_function;
278 			break;
279 
280 		case COLLATOR_SORT_REGULAR:
281 		default:
282 			func = collator_regular_compare_function;
283 			break;
284 	}
285 
286 	return func;
287 }
288 /* }}} */
289 
290 /* {{{ collator_sort_internal
291  * Common code shared by collator_sort() and collator_asort() API functions.
292  */
collator_sort_internal(int renumber,INTERNAL_FUNCTION_PARAMETERS)293 static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
294 {
295 	zval           saved_collator;
296 	zval*          array            = NULL;
297 	HashTable*     hash             = NULL;
298 	zend_long           sort_flags       = COLLATOR_SORT_REGULAR;
299 
300 	COLLATOR_METHOD_INIT_VARS
301 
302 	/* Parse parameters. */
303 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Oa/|l",
304 		&object, Collator_ce_ptr, &array, &sort_flags ) == FAILURE )
305 	{
306 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
307 			"collator_sort_internal: unable to parse input params", 0 );
308 
309 		RETURN_FALSE;
310 	}
311 
312 	/* Fetch the object. */
313 	COLLATOR_METHOD_FETCH_OBJECT;
314 
315 	/* Set 'compare function' according to sort flags. */
316 	INTL_G(compare_func) = collator_get_compare_function( sort_flags );
317 
318 	hash = Z_ARRVAL_P( array );
319 
320 	/* Convert strings in the specified array from UTF-8 to UTF-16. */
321 	collator_convert_hash_from_utf8_to_utf16( hash, COLLATOR_ERROR_CODE_P( co ) );
322 	COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-8 to UTF-16" );
323 
324 	/* Save specified collator in the request-global (?) variable. */
325 	ZVAL_COPY_VALUE(&saved_collator, &INTL_G( current_collator ));
326 	ZVAL_OBJ(&INTL_G( current_collator ), Z_OBJ_P(object));
327 
328 	/* Sort specified array. */
329 	zend_hash_sort(hash, collator_compare_func, renumber);
330 
331 	/* Restore saved collator. */
332 	ZVAL_COPY_VALUE(&INTL_G( current_collator ), &saved_collator);
333 
334 	/* Convert strings in the specified array back to UTF-8. */
335 	collator_convert_hash_from_utf16_to_utf8( hash, COLLATOR_ERROR_CODE_P( co ) );
336 	COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-16 to UTF-8" );
337 
338 	RETURN_TRUE;
339 }
340 /* }}} */
341 
342 /* {{{ proto bool Collator::sort( Collator $coll, array(string) $arr [, int $sort_flags] )
343  * Sort array using specified collator. }}} */
344 /* {{{ proto bool collator_sort(  Collator $coll, array(string) $arr [, int $sort_flags] )
345  * Sort array using specified collator.
346  */
PHP_FUNCTION(collator_sort)347 PHP_FUNCTION( collator_sort )
348 {
349 	collator_sort_internal( TRUE, INTERNAL_FUNCTION_PARAM_PASSTHRU );
350 }
351 /* }}} */
352 
collator_sortkey_swap(collator_sort_key_index_t * p,collator_sort_key_index_t * q)353 static void collator_sortkey_swap(collator_sort_key_index_t *p, collator_sort_key_index_t *q) /* {{{ */
354 {
355 	collator_sort_key_index_t t;
356 	t = *p;
357 	*p = *q;
358 	*q = t;
359 }
360 /* }}} */
361 
362 /* {{{ proto bool Collator::sortWithSortKeys( Collator $coll, array(string) $arr )
363  * Equivalent to standard PHP sort using Collator.
364  * Uses ICU ucol_getSortKey for performance. }}} */
365 /* {{{ proto bool collator_sort_with_sort_keys( Collator $coll, array(string) $arr )
366  * Equivalent to standard PHP sort using Collator.
367  * Uses ICU ucol_getSortKey for performance.
368  */
PHP_FUNCTION(collator_sort_with_sort_keys)369 PHP_FUNCTION( collator_sort_with_sort_keys )
370 {
371 	zval*       array                = NULL;
372 	zval        garbage;
373 	HashTable*  hash                 = NULL;
374 	zval*       hashData             = NULL;                     /* currently processed item of input hash */
375 
376 	char*       sortKeyBuf           = NULL;                     /* buffer to store sort keys */
377 	uint32_t    sortKeyBufSize       = DEF_SORT_KEYS_BUF_SIZE;   /* buffer size */
378 	ptrdiff_t   sortKeyBufOffset     = 0;                        /* pos in buffer to store sort key */
379 	uint32_t    sortKeyLen           = 0;                        /* the length of currently processing key */
380 	uint32_t    bufLeft              = 0;
381 	uint32_t    bufIncrement         = 0;
382 
383 	collator_sort_key_index_t* sortKeyIndxBuf = NULL;            /* buffer to store 'indexes' which will be passed to 'qsort' */
384 	uint32_t    sortKeyIndxBufSize   = DEF_SORT_KEYS_INDX_BUF_SIZE;
385 	uint32_t    sortKeyIndxSize      = sizeof( collator_sort_key_index_t );
386 
387 	uint32_t    sortKeyCount         = 0;
388 	uint32_t    j                    = 0;
389 
390 	UChar*      utf16_buf            = NULL;                     /* tmp buffer to hold current processing string in utf-16 */
391 	int         utf16_buf_size       = DEF_UTF16_BUF_SIZE;       /* the length of utf16_buf */
392 	int         utf16_len            = 0;                        /* length of converted string */
393 
394 	COLLATOR_METHOD_INIT_VARS
395 
396 	/* Parse parameters. */
397 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Oa",
398 		&object, Collator_ce_ptr, &array ) == FAILURE )
399 	{
400 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
401 			"collator_sort_with_sort_keys: unable to parse input params", 0 );
402 
403 		RETURN_FALSE;
404 	}
405 
406 	/* Fetch the object. */
407 	COLLATOR_METHOD_FETCH_OBJECT;
408 
409 	if (!co || !co->ucoll) {
410 		intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
411 		intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
412 			"Object not initialized", 0 );
413 		zend_throw_error(NULL, "Object not initialized");
414 
415 		RETURN_FALSE;
416 	}
417 
418 	/*
419 	 * Sort specified array.
420 	 */
421 	hash = Z_ARRVAL_P( array );
422 
423 	if( !hash || zend_hash_num_elements( hash ) == 0 )
424 		RETURN_TRUE;
425 
426 	/* Create bufers */
427 	sortKeyBuf     = ecalloc( sortKeyBufSize,     sizeof( char    ) );
428 	sortKeyIndxBuf = ecalloc( sortKeyIndxBufSize, sizeof( uint8_t ) );
429 	utf16_buf      = eumalloc( utf16_buf_size );
430 
431 	/* Iterate through input hash and create a sort key for each value. */
432 	ZEND_HASH_FOREACH_VAL(hash, hashData) {
433 		/* Convert current hash item from UTF-8 to UTF-16LE and save the result to utf16_buf. */
434 
435 		utf16_len = utf16_buf_size;
436 
437 		/* Process string values only. */
438 		if( Z_TYPE_P( hashData ) == IS_STRING )
439 		{
440 			intl_convert_utf8_to_utf16( &utf16_buf, &utf16_len, Z_STRVAL_P( hashData ), Z_STRLEN_P( hashData ), COLLATOR_ERROR_CODE_P( co ) );
441 
442 			if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
443 			{
444 				intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
445 				intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), "Sort with sort keys failed", 0 );
446 
447 				if( utf16_buf )
448 					efree( utf16_buf );
449 
450 				efree( sortKeyIndxBuf );
451 				efree( sortKeyBuf );
452 
453 				RETURN_FALSE;
454 			}
455 		}
456 		else
457 		{
458 			/* Set empty string */
459 			utf16_len = 0;
460 			utf16_buf[utf16_len] = 0;
461 		}
462 
463 		if( (utf16_len + 1) > utf16_buf_size )
464 			utf16_buf_size = utf16_len + 1;
465 
466 		/* Get sort key, reallocating the buffer if needed. */
467 		bufLeft = sortKeyBufSize - sortKeyBufOffset;
468 
469 		sortKeyLen = ucol_getSortKey( co->ucoll,
470 									  utf16_buf,
471 									  utf16_len,
472 									  (uint8_t*)sortKeyBuf + sortKeyBufOffset,
473 									  bufLeft );
474 
475 		/* check for sortKeyBuf overflow, increasing its size of the buffer if needed */
476 		if( sortKeyLen > bufLeft )
477 		{
478 			bufIncrement = ( sortKeyLen > DEF_SORT_KEYS_BUF_INCREMENT ) ? sortKeyLen : DEF_SORT_KEYS_BUF_INCREMENT;
479 
480 			sortKeyBufSize += bufIncrement;
481 			bufLeft += bufIncrement;
482 
483 			sortKeyBuf = erealloc( sortKeyBuf, sortKeyBufSize );
484 
485 			sortKeyLen = ucol_getSortKey( co->ucoll, utf16_buf, utf16_len, (uint8_t*)sortKeyBuf + sortKeyBufOffset, bufLeft );
486 		}
487 
488 		/*  check sortKeyIndxBuf overflow, increasing its size of the buffer if needed */
489 		if( ( sortKeyCount + 1 ) * sortKeyIndxSize > sortKeyIndxBufSize )
490 		{
491 			bufIncrement = ( sortKeyIndxSize > DEF_SORT_KEYS_INDX_BUF_INCREMENT ) ? sortKeyIndxSize : DEF_SORT_KEYS_INDX_BUF_INCREMENT;
492 
493 			sortKeyIndxBufSize += bufIncrement;
494 
495 			sortKeyIndxBuf = erealloc( sortKeyIndxBuf, sortKeyIndxBufSize );
496 		}
497 
498 		sortKeyIndxBuf[sortKeyCount].key = (char*)sortKeyBufOffset;    /* remember just offset, cause address */
499 		                                                               /* of 'sortKeyBuf' may be changed due to realloc. */
500 		sortKeyIndxBuf[sortKeyCount].zstr = hashData;
501 
502 		sortKeyBufOffset += sortKeyLen;
503 		++sortKeyCount;
504 
505 	} ZEND_HASH_FOREACH_END();
506 
507 	/* update ptrs to point to valid keys. */
508 	for( j = 0; j < sortKeyCount; j++ )
509 		sortKeyIndxBuf[j].key = sortKeyBuf + (ptrdiff_t)sortKeyIndxBuf[j].key;
510 
511 	/* sort it */
512 	zend_sort( sortKeyIndxBuf, sortKeyCount,
513 			sortKeyIndxSize, collator_cmp_sort_keys, (swap_func_t)collator_sortkey_swap);
514 
515 	ZVAL_COPY_VALUE(&garbage, array);
516 	/* for resulting hash we'll assign new hash keys rather then reordering */
517 	array_init(array);
518 
519 	for( j = 0; j < sortKeyCount; j++ )
520 	{
521 		Z_TRY_ADDREF_P( sortKeyIndxBuf[j].zstr );
522 		zend_hash_next_index_insert( Z_ARRVAL_P(array), sortKeyIndxBuf[j].zstr);
523 	}
524 
525 	if( utf16_buf )
526 		efree( utf16_buf );
527 
528 	zval_ptr_dtor(&garbage);
529 	efree( sortKeyIndxBuf );
530 	efree( sortKeyBuf );
531 
532 	RETURN_TRUE;
533 }
534 /* }}} */
535 
536 /* {{{ proto bool Collator::asort( Collator $coll, array(string) $arr )
537  * Sort array using specified collator, maintaining index association. }}} */
538 /* {{{ proto bool collator_asort( Collator $coll, array(string) $arr )
539  * Sort array using specified collator, maintaining index association.
540  */
PHP_FUNCTION(collator_asort)541 PHP_FUNCTION( collator_asort )
542 {
543 	collator_sort_internal( FALSE, INTERNAL_FUNCTION_PARAM_PASSTHRU );
544 }
545 /* }}} */
546 
547 /* {{{ proto bool Collator::getSortKey( Collator $coll, string $str )
548  * Get a sort key for a string from a Collator. }}} */
549 /* {{{ proto bool collator_get_sort_key( Collator $coll, string $str )
550  * Get a sort key for a string from a Collator. */
PHP_FUNCTION(collator_get_sort_key)551 PHP_FUNCTION( collator_get_sort_key )
552 {
553 	char*            str      = NULL;
554 	size_t           str_len  = 0;
555 	UChar*           ustr     = NULL;
556 	int32_t          ustr_len = 0;
557 	int              key_len = 0;
558 	zend_string*     key_str;
559 
560 	COLLATOR_METHOD_INIT_VARS
561 
562 	/* Parse parameters. */
563 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Os",
564 		&object, Collator_ce_ptr, &str, &str_len ) == FAILURE )
565 	{
566 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
567 			 "collator_get_sort_key: unable to parse input params", 0 );
568 
569 		RETURN_FALSE;
570 	}
571 
572 	/* Fetch the object. */
573 	COLLATOR_METHOD_FETCH_OBJECT;
574 
575 	if (!co || !co->ucoll) {
576 		intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
577 		intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
578 			"Object not initialized", 0 );
579 		zend_throw_error(NULL, "Object not initialized");
580 
581 		RETURN_FALSE;
582 	}
583 
584 	/*
585 	 * Compare given strings (converting them to UTF-16 first).
586 	 */
587 
588 	/* First convert the strings to UTF-16. */
589 	intl_convert_utf8_to_utf16(
590 		&ustr, &ustr_len, str, str_len, COLLATOR_ERROR_CODE_P( co ) );
591 	if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
592 	{
593 		/* Set global error code. */
594 		intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
595 
596 		/* Set error messages. */
597 		intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
598 			"Error converting first argument to UTF-16", 0 );
599 		efree( ustr );
600 		RETURN_FALSE;
601 	}
602 
603 	/* ucol_getSortKey is exception in that the key length includes the
604 	 * NUL terminator*/
605 	key_len = ucol_getSortKey(co->ucoll, ustr, ustr_len, NULL, 0);
606 	if(!key_len) {
607 		efree( ustr );
608 		RETURN_FALSE;
609 	}
610 	key_str = zend_string_alloc(key_len, 0);
611 	key_len = ucol_getSortKey(co->ucoll, ustr, ustr_len, (uint8_t*)ZSTR_VAL(key_str), key_len);
612 	efree( ustr );
613 	if(!key_len) {
614 		RETURN_FALSE;
615 	}
616 	ZSTR_LEN(key_str) = key_len - 1;
617 	RETVAL_NEW_STR(key_str);
618 }
619 /* }}} */
620