2023-09-20 16:57:22 +02:00
# include <stdlib.h>
2008-08-08 10:09:19 +00:00
# include <string.h>
# include <libxml/parser.h>
# include <libxml/dict.h>
2023-09-12 19:08:07 +02:00
/**** dictionary tests ****/
2023-11-04 19:04:23 +01:00
# ifdef __clang__
2024-01-25 12:52:14 +01:00
# if __clang_major__ >= 12
# define ATTRIBUTE_NO_SANITIZE_INTEGER \
__attribute__ ( ( no_sanitize ( " unsigned-integer-overflow " ) ) ) \
__attribute__ ( ( no_sanitize ( " unsigned-shift-base " ) ) )
# else
# define ATTRIBUTE_NO_SANITIZE_INTEGER \
__attribute__ ( ( no_sanitize ( " unsigned-integer-overflow " ) ) )
# endif
2023-11-04 19:04:23 +01:00
# else
# define ATTRIBUTE_NO_SANITIZE_INTEGER
# endif
2008-08-08 10:09:19 +00:00
/* #define WITH_PRINT */
2008-08-08 12:03:22 +00:00
static const char * seeds1 [ ] = {
2008-08-08 10:09:19 +00:00
" a " , " b " , " c " ,
" d " , " e " , " f " ,
" g " , " h " , " i " ,
" j " , " k " , " l " ,
NULL
} ;
2008-08-08 12:03:22 +00:00
static const char * seeds2 [ ] = {
" m " , " n " , " o " ,
" p " , " q " , " r " ,
" s " , " t " , " u " ,
" v " , " w " , " x " ,
NULL
} ;
2023-09-04 16:07:23 +02:00
# define NB_STRINGS_MAX 100000
# define NB_STRINGS_NS 10000
# define NB_STRINGS_PREFIX (NB_STRINGS_NS / 20)
2008-08-08 10:09:19 +00:00
# define NB_STRINGS_MIN 10
2023-05-03 03:20:14 +02:00
static xmlChar * * strings1 ;
static xmlChar * * strings2 ;
static const xmlChar * * test1 ;
static const xmlChar * * test2 ;
2008-08-08 12:03:22 +00:00
static int nbErrors = 0 ;
2008-08-08 10:09:19 +00:00
2023-05-03 03:20:14 +02:00
static void
fill_string_pool ( xmlChar * * strings , const char * * seeds ) {
2008-08-08 10:09:19 +00:00
int i , j , k ;
2023-05-03 03:20:14 +02:00
int start_ns = NB_STRINGS_MAX - NB_STRINGS_NS ;
2008-08-08 10:09:19 +00:00
/*
* That ' s a bit nasty but the output is fine and it doesn ' t take hours
* there is a small but sufficient number of duplicates , and we have
* " :xxx " and full QNames in the last NB_STRINGS_NS values
*/
2023-05-03 03:20:14 +02:00
for ( i = 0 ; seeds [ i ] ! = NULL ; i + + ) {
strings [ i ] = xmlStrdup ( ( const xmlChar * ) seeds [ i ] ) ;
if ( strings [ i ] = = NULL ) {
fprintf ( stderr , " Out of memory while generating strings \n " ) ;
2008-08-08 10:09:19 +00:00
exit ( 1 ) ;
}
}
2023-05-03 03:20:14 +02:00
for ( j = 0 , k = 0 ; i < start_ns ; i + + ) {
strings [ i ] = xmlStrncatNew ( strings [ j ] , strings [ k ] , - 1 ) ;
if ( strings [ i ] = = NULL ) {
fprintf ( stderr , " Out of memory while generating strings \n " ) ;
2008-08-08 10:09:19 +00:00
exit ( 1 ) ;
}
2023-05-03 03:20:14 +02:00
if ( xmlStrlen ( strings [ i ] ) > 30 ) {
fprintf ( stderr , " ### %s %s \n " , strings [ start_ns + j ] , strings [ k ] ) ;
abort ( ) ;
}
j + + ;
2008-08-08 10:09:19 +00:00
if ( j > = 50 ) {
j = 0 ;
k + + ;
}
}
2023-05-03 03:20:14 +02:00
for ( j = 0 , k = 0 ; ( j < NB_STRINGS_PREFIX ) & & ( i < NB_STRINGS_MAX ) ;
i + + , j + + ) {
strings [ i ] = xmlStrncatNew ( strings [ k ] , ( const xmlChar * ) " : " , - 1 ) ;
if ( strings [ i ] = = NULL ) {
fprintf ( stderr , " Out of memory while generating strings \n " ) ;
2008-08-08 10:09:19 +00:00
exit ( 1 ) ;
}
2023-05-03 03:20:14 +02:00
k + = 1 ;
if ( k > = start_ns ) k = 0 ;
2008-08-08 10:09:19 +00:00
}
2023-05-03 03:20:14 +02:00
for ( j = 0 , k = 0 ; i < NB_STRINGS_MAX ; i + + ) {
strings [ i ] = xmlStrncatNew ( strings [ start_ns + j ] , strings [ k ] , - 1 ) ;
if ( strings [ i ] = = NULL ) {
fprintf ( stderr , " Out of memory while generating strings \n " ) ;
2008-08-08 10:09:19 +00:00
exit ( 1 ) ;
}
2023-05-03 03:20:14 +02:00
j + + ;
if ( j > = NB_STRINGS_PREFIX ) j = 0 ;
k + = 5 ;
if ( k > = start_ns ) k = 0 ;
2008-08-08 10:09:19 +00:00
}
}
# ifdef WITH_PRINT
static void print_strings ( void ) {
int i ;
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
2008-08-08 12:03:22 +00:00
printf ( " %s \n " , strings1 [ i ] ) ;
}
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
printf ( " %s \n " , strings2 [ i ] ) ;
2008-08-08 10:09:19 +00:00
}
}
# endif
static void clean_strings ( void ) {
int i ;
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
2008-08-08 12:03:22 +00:00
if ( strings1 [ i ] ! = NULL ) /* really should not happen */
xmlFree ( strings1 [ i ] ) ;
}
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( strings2 [ i ] ! = NULL ) /* really should not happen */
xmlFree ( strings2 [ i ] ) ;
2008-08-08 10:09:19 +00:00
}
}
2008-08-08 12:03:22 +00:00
/*
* This tests the sub - dictionary support
*/
2023-09-12 19:08:07 +02:00
static int
test_subdict ( xmlDictPtr parent ) {
2008-08-08 12:03:22 +00:00
int i , j ;
xmlDictPtr dict ;
int ret = 0 ;
xmlChar prefix [ 40 ] ;
xmlChar * cur , * pref ;
const xmlChar * tmp ;
dict = xmlDictCreateSub ( parent ) ;
if ( dict = = NULL ) {
fprintf ( stderr , " Out of memory while creating sub-dictionary \n " ) ;
exit ( 1 ) ;
}
2022-11-22 14:50:14 +01:00
/* Cast to avoid buggy warning on MSVC. */
memset ( ( void * ) test2 , 0 , sizeof ( test2 ) ) ;
2008-08-08 12:03:22 +00:00
/*
* Fill in NB_STRINGS_MIN , at this point the dictionary should not grow
* and we allocate all those doing the fast key computations
* All the strings are based on a different seeds subset so we know
* they are allocated in the main dictionary , not coming from the parent
*/
for ( i = 0 ; i < NB_STRINGS_MIN ; i + + ) {
test2 [ i ] = xmlDictLookup ( dict , strings2 [ i ] , - 1 ) ;
if ( test2 [ i ] = = NULL ) {
fprintf ( stderr , " Failed lookup for '%s' \n " , strings2 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
j = NB_STRINGS_MAX - NB_STRINGS_NS ;
/* ":foo" like strings2 */
for ( i = 0 ; i < NB_STRINGS_MIN ; i + + , j + + ) {
test2 [ j ] = xmlDictLookup ( dict , strings2 [ j ] , xmlStrlen ( strings2 [ j ] ) ) ;
if ( test2 [ j ] = = NULL ) {
fprintf ( stderr , " Failed lookup for '%s' \n " , strings2 [ j ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/* "a:foo" like strings2 */
j = NB_STRINGS_MAX - NB_STRINGS_MIN ;
for ( i = 0 ; i < NB_STRINGS_MIN ; i + + , j + + ) {
test2 [ j ] = xmlDictLookup ( dict , strings2 [ j ] , xmlStrlen ( strings2 [ j ] ) ) ;
if ( test2 [ j ] = = NULL ) {
fprintf ( stderr , " Failed lookup for '%s' \n " , strings2 [ j ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* At this point allocate all the strings
* the dictionary will grow in the process , reallocate more string tables
* and switch to the better key generator
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( test2 [ i ] ! = NULL )
continue ;
test2 [ i ] = xmlDictLookup ( dict , strings2 [ i ] , - 1 ) ;
if ( test2 [ i ] = = NULL ) {
fprintf ( stderr , " Failed lookup for '%s' \n " , strings2 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* Now we can start to test things , first that all strings2 belongs to
* the dict , and that none of them was actually allocated in the parent
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( ! xmlDictOwns ( dict , test2 [ i ] ) ) {
fprintf ( stderr , " Failed ownership failure for '%s' \n " ,
strings2 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
if ( xmlDictOwns ( parent , test2 [ i ] ) ) {
fprintf ( stderr , " Failed parent ownership failure for '%s' \n " ,
strings2 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* Also verify that all strings from the parent are seen from the subdict
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( ! xmlDictOwns ( dict , test1 [ i ] ) ) {
fprintf ( stderr , " Failed sub-ownership failure for '%s' \n " ,
strings1 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* Then that another lookup to the string in sub will return the same
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( xmlDictLookup ( dict , strings2 [ i ] , - 1 ) ! = test2 [ i ] ) {
fprintf ( stderr , " Failed re-lookup check for %d, '%s' \n " ,
i , strings2 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* But also that any lookup for a string in the parent will be provided
* as in the parent
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( xmlDictLookup ( dict , strings1 [ i ] , - 1 ) ! = test1 [ i ] ) {
fprintf ( stderr , " Failed parent string lookup check for %d, '%s' \n " ,
i , strings1 [ i ] ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* check the QName lookups
*/
for ( i = NB_STRINGS_MAX - NB_STRINGS_NS ; i < NB_STRINGS_MAX ; i + + ) {
cur = strings2 [ i ] ;
pref = & prefix [ 0 ] ;
while ( * cur ! = ' : ' ) * pref + + = * cur + + ;
cur + + ;
* pref = 0 ;
tmp = xmlDictQLookup ( dict , & prefix [ 0 ] , cur ) ;
2014-10-26 18:08:04 +00:00
if ( tmp ! = test2 [ i ] ) {
2008-08-08 12:03:22 +00:00
fprintf ( stderr , " Failed lookup check for '%s':'%s' \n " ,
& prefix [ 0 ] , cur ) ;
ret = 1 ;
nbErrors + + ;
}
}
/*
* check the QName lookups for strings from the parent
*/
for ( i = NB_STRINGS_MAX - NB_STRINGS_NS ; i < NB_STRINGS_MAX ; i + + ) {
cur = strings1 [ i ] ;
pref = & prefix [ 0 ] ;
while ( * cur ! = ' : ' ) * pref + + = * cur + + ;
cur + + ;
* pref = 0 ;
tmp = xmlDictQLookup ( dict , & prefix [ 0 ] , cur ) ;
if ( xmlDictQLookup ( dict , & prefix [ 0 ] , cur ) ! = test1 [ i ] ) {
fprintf ( stderr , " Failed parent lookup check for '%s':'%s' \n " ,
& prefix [ 0 ] , cur ) ;
ret = 1 ;
nbErrors + + ;
}
}
xmlDictFree ( dict ) ;
return ( ret ) ;
}
/*
* Test a single dictionary
*/
2023-09-12 19:08:07 +02:00
static int
test_dict ( xmlDict * dict ) {
2008-08-08 10:09:19 +00:00
int i , j ;
int ret = 0 ;
xmlChar prefix [ 40 ] ;
2008-08-08 12:03:22 +00:00
xmlChar * cur , * pref ;
const xmlChar * tmp ;
2008-08-08 10:09:19 +00:00
2022-11-22 14:50:14 +01:00
/* Cast to avoid buggy warning on MSVC. */
memset ( ( void * ) test1 , 0 , sizeof ( test1 ) ) ;
2008-08-08 10:09:19 +00:00
/*
* Fill in NB_STRINGS_MIN , at this point the dictionary should not grow
* and we allocate all those doing the fast key computations
*/
for ( i = 0 ; i < NB_STRINGS_MIN ; i + + ) {
2008-08-08 12:03:22 +00:00
test1 [ i ] = xmlDictLookup ( dict , strings1 [ i ] , - 1 ) ;
2008-08-08 10:09:19 +00:00
if ( test1 [ i ] = = NULL ) {
2008-08-08 12:03:22 +00:00
fprintf ( stderr , " Failed lookup for '%s' \n " , strings1 [ i ] ) ;
2008-08-08 10:09:19 +00:00
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
j = NB_STRINGS_MAX - NB_STRINGS_NS ;
2008-08-08 12:03:22 +00:00
/* ":foo" like strings1 */
2008-08-08 10:09:19 +00:00
for ( i = 0 ; i < NB_STRINGS_MIN ; i + + , j + + ) {
2008-08-08 12:03:22 +00:00
test1 [ j ] = xmlDictLookup ( dict , strings1 [ j ] , xmlStrlen ( strings1 [ j ] ) ) ;
2008-08-08 10:09:19 +00:00
if ( test1 [ j ] = = NULL ) {
2008-08-08 12:03:22 +00:00
fprintf ( stderr , " Failed lookup for '%s' \n " , strings1 [ j ] ) ;
2008-08-08 10:09:19 +00:00
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
2008-08-08 12:03:22 +00:00
/* "a:foo" like strings1 */
2008-08-08 10:09:19 +00:00
j = NB_STRINGS_MAX - NB_STRINGS_MIN ;
for ( i = 0 ; i < NB_STRINGS_MIN ; i + + , j + + ) {
2008-08-08 12:03:22 +00:00
test1 [ j ] = xmlDictLookup ( dict , strings1 [ j ] , xmlStrlen ( strings1 [ j ] ) ) ;
2008-08-08 10:09:19 +00:00
if ( test1 [ j ] = = NULL ) {
2008-08-08 12:03:22 +00:00
fprintf ( stderr , " Failed lookup for '%s' \n " , strings1 [ j ] ) ;
2008-08-08 10:09:19 +00:00
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
/*
* At this point allocate all the strings
* the dictionary will grow in the process , reallocate more string tables
* and switch to the better key generator
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( test1 [ i ] ! = NULL )
continue ;
2008-08-08 12:03:22 +00:00
test1 [ i ] = xmlDictLookup ( dict , strings1 [ i ] , - 1 ) ;
2008-08-08 10:09:19 +00:00
if ( test1 [ i ] = = NULL ) {
2008-08-08 12:03:22 +00:00
fprintf ( stderr , " Failed lookup for '%s' \n " , strings1 [ i ] ) ;
2008-08-08 10:09:19 +00:00
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
/*
2008-08-08 12:03:22 +00:00
* Now we can start to test things , first that all strings1 belongs to
2008-08-08 10:09:19 +00:00
* the dict
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
if ( ! xmlDictOwns ( dict , test1 [ i ] ) ) {
fprintf ( stderr , " Failed ownership failure for '%s' \n " ,
2008-08-08 12:03:22 +00:00
strings1 [ i ] ) ;
2008-08-08 10:09:19 +00:00
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
/*
* Then that another lookup to the string will return the same
*/
for ( i = 0 ; i < NB_STRINGS_MAX ; i + + ) {
2008-08-08 12:03:22 +00:00
if ( xmlDictLookup ( dict , strings1 [ i ] , - 1 ) ! = test1 [ i ] ) {
2008-08-08 10:09:19 +00:00
fprintf ( stderr , " Failed re-lookup check for %d, '%s' \n " ,
2008-08-08 12:03:22 +00:00
i , strings1 [ i ] ) ;
2008-08-08 10:09:19 +00:00
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
/*
* More complex , check the QName lookups
*/
for ( i = NB_STRINGS_MAX - NB_STRINGS_NS ; i < NB_STRINGS_MAX ; i + + ) {
2008-08-08 12:03:22 +00:00
cur = strings1 [ i ] ;
2008-08-08 10:09:19 +00:00
pref = & prefix [ 0 ] ;
while ( * cur ! = ' : ' ) * pref + + = * cur + + ;
cur + + ;
* pref = 0 ;
tmp = xmlDictQLookup ( dict , & prefix [ 0 ] , cur ) ;
2014-10-26 18:08:04 +00:00
if ( tmp ! = test1 [ i ] ) {
2008-08-08 10:09:19 +00:00
fprintf ( stderr , " Failed lookup check for '%s':'%s' \n " ,
& prefix [ 0 ] , cur ) ;
ret = 1 ;
2008-08-08 12:03:22 +00:00
nbErrors + + ;
2008-08-08 10:09:19 +00:00
}
}
2008-08-08 12:03:22 +00:00
return ( ret ) ;
2008-08-08 10:09:19 +00:00
}
2023-09-12 19:08:07 +02:00
static int
testall_dict ( void ) {
xmlDictPtr dict ;
int ret = 0 ;
2023-05-03 03:20:14 +02:00
strings1 = xmlMalloc ( NB_STRINGS_MAX * sizeof ( strings1 [ 0 ] ) ) ;
memset ( strings1 , 0 , NB_STRINGS_MAX * sizeof ( strings1 [ 0 ] ) ) ;
strings2 = xmlMalloc ( NB_STRINGS_MAX * sizeof ( strings2 [ 0 ] ) ) ;
memset ( strings2 , 0 , NB_STRINGS_MAX * sizeof ( strings2 [ 0 ] ) ) ;
test1 = xmlMalloc ( NB_STRINGS_MAX * sizeof ( test1 [ 0 ] ) ) ;
memset ( test1 , 0 , NB_STRINGS_MAX * sizeof ( test1 [ 0 ] ) ) ;
test2 = xmlMalloc ( NB_STRINGS_MAX * sizeof ( test2 [ 0 ] ) ) ;
memset ( test2 , 0 , NB_STRINGS_MAX * sizeof ( test2 [ 0 ] ) ) ;
fill_string_pool ( strings1 , seeds1 ) ;
fill_string_pool ( strings2 , seeds2 ) ;
2008-08-08 10:09:19 +00:00
# ifdef WITH_PRINT
print_strings ( ) ;
# endif
2023-09-12 19:08:07 +02:00
dict = xmlDictCreate ( ) ;
if ( dict = = NULL ) {
fprintf ( stderr , " Out of memory while creating dictionary \n " ) ;
exit ( 1 ) ;
}
if ( test_dict ( dict ) ! = 0 ) {
ret = 1 ;
}
if ( test_subdict ( dict ) ! = 0 ) {
ret = 1 ;
2008-08-08 12:03:22 +00:00
}
2023-09-12 19:08:07 +02:00
xmlDictFree ( dict ) ;
2008-08-08 10:09:19 +00:00
clean_strings ( ) ;
2023-05-03 03:20:14 +02:00
xmlFree ( strings1 ) ;
xmlFree ( strings2 ) ;
xmlFree ( test1 ) ;
xmlFree ( test2 ) ;
2023-09-12 19:08:07 +02:00
return ret ;
}
/**** Hash table tests ****/
static unsigned
rng_state [ 2 ] = { 123 , 456 } ;
# define HASH_ROL(x,n) ((x) << (n) | ((x) & 0xFFFFFFFF) >> (32 - (n)))
2023-11-04 19:04:23 +01:00
ATTRIBUTE_NO_SANITIZE_INTEGER
2023-09-12 19:08:07 +02:00
static unsigned
my_rand ( unsigned max ) {
unsigned s0 = rng_state [ 0 ] ;
unsigned s1 = rng_state [ 1 ] ;
unsigned result = HASH_ROL ( s0 * 0x9E3779BB , 5 ) * 5 ;
s1 ^ = s0 ;
rng_state [ 0 ] = HASH_ROL ( s0 , 26 ) ^ s1 ^ ( s1 < < 9 ) ;
rng_state [ 1 ] = HASH_ROL ( s1 , 13 ) ;
return ( ( result & 0xFFFFFFFF ) % max ) ;
}
static xmlChar *
gen_random_string ( xmlChar id ) {
unsigned size = my_rand ( 64 ) + 1 ;
unsigned id_pos = my_rand ( size ) ;
size_t j ;
xmlChar * str = xmlMalloc ( size + 1 ) ;
for ( j = 0 ; j < size ; j + + ) {
str [ j ] = ' a ' + my_rand ( 26 ) ;
}
str [ id_pos ] = id ;
str [ size ] = 0 ;
/* Generate QName in 75% of cases */
if ( size > 3 & & my_rand ( 4 ) > 0 ) {
unsigned colon_pos = my_rand ( size - 3 ) + 1 ;
if ( colon_pos > = id_pos )
colon_pos + + ;
str [ colon_pos ] = ' : ' ;
}
return str ;
}
typedef struct {
xmlChar * * strings ;
size_t num_entries ;
size_t num_keys ;
size_t num_strings ;
size_t index ;
xmlChar id ;
} StringPool ;
static StringPool *
pool_new ( size_t num_entries , size_t num_keys , xmlChar id ) {
StringPool * ret ;
size_t num_strings ;
ret = xmlMalloc ( sizeof ( * ret ) ) ;
ret - > num_entries = num_entries ;
ret - > num_keys = num_keys ;
num_strings = num_entries * num_keys ;
ret - > strings = xmlMalloc ( num_strings * sizeof ( ret - > strings [ 0 ] ) ) ;
memset ( ret - > strings , 0 , num_strings * sizeof ( ret - > strings [ 0 ] ) ) ;
ret - > num_strings = num_strings ;
ret - > index = 0 ;
ret - > id = id ;
return ret ;
}
static void
pool_free ( StringPool * pool ) {
size_t i ;
for ( i = 0 ; i < pool - > num_strings ; i + + ) {
xmlFree ( pool - > strings [ i ] ) ;
}
xmlFree ( pool - > strings ) ;
xmlFree ( pool ) ;
}
static int
pool_done ( StringPool * pool ) {
return pool - > index > = pool - > num_strings ;
}
static void
pool_reset ( StringPool * pool ) {
pool - > index = 0 ;
}
static int
pool_bulk_insert ( StringPool * pool , xmlHashTablePtr hash , size_t num ) {
size_t i , j ;
int ret = 0 ;
for ( i = pool - > index , j = 0 ; i < pool - > num_strings & & j < num ; j + + ) {
xmlChar * str [ 3 ] ;
size_t k ;
while ( 1 ) {
xmlChar tmp_key [ 1 ] ;
int res ;
for ( k = 0 ; k < pool - > num_keys ; k + + )
str [ k ] = gen_random_string ( pool - > id ) ;
switch ( pool - > num_keys ) {
case 1 :
res = xmlHashAddEntry ( hash , str [ 0 ] , tmp_key ) ;
if ( res = = 0 & &
xmlHashUpdateEntry ( hash , str [ 0 ] , str [ 0 ] , NULL ) ! = 0 )
ret = - 1 ;
break ;
case 2 :
res = xmlHashAddEntry2 ( hash , str [ 0 ] , str [ 1 ] , tmp_key ) ;
if ( res = = 0 & &
xmlHashUpdateEntry2 ( hash , str [ 0 ] , str [ 1 ] , str [ 0 ] ,
NULL ) ! = 0 )
ret = - 1 ;
break ;
case 3 :
res = xmlHashAddEntry3 ( hash , str [ 0 ] , str [ 1 ] , str [ 2 ] ,
tmp_key ) ;
if ( res = = 0 & &
xmlHashUpdateEntry3 ( hash , str [ 0 ] , str [ 1 ] , str [ 2 ] ,
str [ 0 ] , NULL ) ! = 0 )
ret = - 1 ;
break ;
}
if ( res = = 0 )
break ;
for ( k = 0 ; k < pool - > num_keys ; k + + )
xmlFree ( str [ k ] ) ;
}
for ( k = 0 ; k < pool - > num_keys ; k + + )
pool - > strings [ i + + ] = str [ k ] ;
}
pool - > index = i ;
return ret ;
}
static xmlChar *
hash_qlookup ( xmlHashTable * hash , xmlChar * * names , size_t num_keys ) {
xmlChar * prefix [ 3 ] ;
const xmlChar * local [ 3 ] ;
xmlChar * res ;
size_t i ;
for ( i = 0 ; i < 3 ; + + i ) {
if ( i > = num_keys ) {
prefix [ i ] = NULL ;
local [ i ] = NULL ;
} else {
const xmlChar * name = names [ i ] ;
const xmlChar * colon = BAD_CAST strchr ( ( const char * ) name , ' : ' ) ;
if ( colon = = NULL ) {
prefix [ i ] = NULL ;
local [ i ] = name ;
} else {
prefix [ i ] = xmlStrndup ( name , colon - name ) ;
local [ i ] = & colon [ 1 ] ;
}
}
}
res = xmlHashQLookup3 ( hash , prefix [ 0 ] , local [ 0 ] , prefix [ 1 ] , local [ 1 ] ,
prefix [ 2 ] , local [ 2 ] ) ;
for ( i = 0 ; i < 3 ; + + i )
xmlFree ( prefix [ i ] ) ;
return res ;
}
static int
pool_bulk_lookup ( StringPool * pool , xmlHashTablePtr hash , size_t num ,
int existing ) {
size_t i , j ;
int ret = 0 ;
for ( i = pool - > index , j = 0 ; i < pool - > num_strings & & j < num ; j + + ) {
xmlChar * * str = & pool - > strings [ i ] ;
int q ;
for ( q = 0 ; q < 2 ; q + + ) {
xmlChar * res = NULL ;
if ( q ) {
res = hash_qlookup ( hash , str , pool - > num_keys ) ;
} else {
switch ( pool - > num_keys ) {
case 1 :
res = xmlHashLookup ( hash , str [ 0 ] ) ;
break ;
case 2 :
res = xmlHashLookup2 ( hash , str [ 0 ] , str [ 1 ] ) ;
break ;
case 3 :
res = xmlHashLookup3 ( hash , str [ 0 ] , str [ 1 ] , str [ 2 ] ) ;
break ;
}
}
if ( existing ) {
if ( res ! = str [ 0 ] )
ret = - 1 ;
} else {
if ( res ! = NULL )
ret = - 1 ;
}
}
i + = pool - > num_keys ;
}
pool - > index = i ;
return ret ;
}
static int
pool_bulk_remove ( StringPool * pool , xmlHashTablePtr hash , size_t num ) {
size_t i , j ;
int ret = 0 ;
for ( i = pool - > index , j = 0 ; i < pool - > num_strings & & j < num ; j + + ) {
xmlChar * * str = & pool - > strings [ i ] ;
int res = - 1 ;
switch ( pool - > num_keys ) {
case 1 :
res = xmlHashRemoveEntry ( hash , str [ 0 ] , NULL ) ;
break ;
case 2 :
res = xmlHashRemoveEntry2 ( hash , str [ 0 ] , str [ 1 ] , NULL ) ;
break ;
case 3 :
res = xmlHashRemoveEntry3 ( hash , str [ 0 ] , str [ 1 ] , str [ 2 ] , NULL ) ;
break ;
}
if ( res ! = 0 )
ret = - 1 ;
i + = pool - > num_keys ;
}
pool - > index = i ;
return ret ;
}
static int
test_hash ( size_t num_entries , size_t num_keys , int use_dict ) {
xmlDict * dict = NULL ;
xmlHashTable * hash ;
StringPool * pool1 , * pool2 ;
int ret = 0 ;
if ( use_dict ) {
dict = xmlDictCreate ( ) ;
hash = xmlHashCreateDict ( 0 , dict ) ;
} else {
hash = xmlHashCreate ( 0 ) ;
}
pool1 = pool_new ( num_entries , num_keys , ' 1 ' ) ;
pool2 = pool_new ( num_entries , num_keys , ' 2 ' ) ;
/* Insert all strings from pool2 and about half of pool1. */
while ( ! pool_done ( pool2 ) ) {
if ( pool_bulk_insert ( pool1 , hash , my_rand ( 50 ) ) ! = 0 ) {
fprintf ( stderr , " pool1: hash insert failed \n " ) ;
ret = 1 ;
}
if ( pool_bulk_insert ( pool2 , hash , my_rand ( 100 ) ) ! = 0 ) {
fprintf ( stderr , " pool1: hash insert failed \n " ) ;
ret = 1 ;
}
}
/* Check existing entries */
pool_reset ( pool2 ) ;
if ( pool_bulk_lookup ( pool2 , hash , pool2 - > num_entries , 1 ) ! = 0 ) {
fprintf ( stderr , " pool2: hash lookup failed \n " ) ;
ret = 1 ;
}
/* Remove all strings from pool2 and insert the rest of pool1. */
pool_reset ( pool2 ) ;
while ( ! pool_done ( pool1 ) | | ! pool_done ( pool2 ) ) {
if ( pool_bulk_insert ( pool1 , hash , my_rand ( 50 ) ) ! = 0 ) {
fprintf ( stderr , " pool1: hash insert failed \n " ) ;
ret = 1 ;
}
if ( pool_bulk_remove ( pool2 , hash , my_rand ( 100 ) ) ! = 0 ) {
fprintf ( stderr , " pool2: hash remove failed \n " ) ;
ret = 1 ;
}
}
/* Check existing entries */
pool_reset ( pool1 ) ;
if ( pool_bulk_lookup ( pool1 , hash , pool1 - > num_entries , 1 ) ! = 0 ) {
fprintf ( stderr , " pool1: hash lookup failed \n " ) ;
ret = 1 ;
}
/* Check removed entries */
pool_reset ( pool2 ) ;
if ( pool_bulk_lookup ( pool2 , hash , pool2 - > num_entries , 0 ) ! = 0 ) {
fprintf ( stderr , " pool2: hash lookup succeeded unexpectedly \n " ) ;
ret = 1 ;
}
pool_free ( pool1 ) ;
pool_free ( pool2 ) ;
xmlHashFree ( hash , NULL ) ;
xmlDictFree ( dict ) ;
return ret ;
}
static int
testall_hash ( void ) {
size_t num_keys ;
for ( num_keys = 1 ; num_keys < = 3 ; num_keys + + ) {
size_t num_strings ;
size_t max_strings = num_keys = = 1 ? 100000 : 1000 ;
for ( num_strings = 10 ; num_strings < = max_strings ; num_strings * = 10 ) {
size_t reps , i ;
reps = 1000 / num_strings ;
if ( reps = = 0 )
reps = 1 ;
for ( i = 0 ; i < reps ; i + + ) {
if ( test_hash ( num_strings , num_keys , /* use_dict */ 0 ) ! = 0 )
return ( 1 ) ;
}
if ( test_hash ( num_strings , num_keys , /* use_dict */ 1 ) ! = 0 )
return ( 1 ) ;
}
}
return ( 0 ) ;
}
/**** main ****/
int
main ( void ) {
int ret = 0 ;
LIBXML_TEST_VERSION
if ( testall_dict ( ) ! = 0 ) {
fprintf ( stderr , " dictionary tests failed \n " ) ;
ret = 1 ;
}
if ( testall_hash ( ) ! = 0 ) {
fprintf ( stderr , " hash tests failed \n " ) ;
ret = 1 ;
}
2008-08-08 10:09:19 +00:00
xmlCleanupParser ( ) ;
return ( ret ) ;
}