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