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