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