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