fix crashes when filter strings are too long

This commit is contained in:
Eric House 2020-08-11 14:13:11 -07:00
parent b6a74b7d15
commit 7a43e95aa8
7 changed files with 259 additions and 188 deletions

View file

@ -700,10 +700,14 @@ public class DictBrowseDelegate extends DelegateBase
public void run() { public void run() {
stopProgress(); stopProgress();
m_browseState.onFilterAccepted( m_dict, null ); if ( null != wrapper ) {
initList( wrapper ); m_browseState.onFilterAccepted( m_dict, null );
setFindPats( m_browseState.m_pats ); initList( wrapper );
setFindPats( m_browseState.m_pats );
} else {
makeOkOnlyBuilder(R.string.alrt_bad_filter )
.show();
}
newFeatureAlert(); newFeatureAlert();
} }
} ); } );

View file

@ -619,9 +619,13 @@ public class XwJNI {
new Thread( new Runnable() { new Thread( new Runnable() {
@Override @Override
public void run() { public void run() {
IterWrapper wrapper = null;
long iterPtr = di_init( jniState, dictPtr, pats, long iterPtr = di_init( jniState, dictPtr, pats,
minLen, maxLen ); minLen, maxLen );
callback.onIterReady( new IterWrapper(iterPtr) ); if ( 0 != iterPtr ) {
wrapper = new IterWrapper(iterPtr);
}
callback.onIterReady( wrapper );
} }
} ).start(); } ).start();
} }

View file

@ -1615,6 +1615,8 @@
<!-- Shown when user tries to make a wordlist filter where max < min --> <!-- Shown when user tries to make a wordlist filter where max < min -->
<string name="error_min_gt_max">The minimum length value cannot be <string name="error_min_gt_max">The minimum length value cannot be
greater than the maximum value.</string> greater than the maximum value.</string>
<!-- shown when something goes wrong building a filter -->
<string name="alrt_bad_filter">Unable to apply filter.</string>
<!-- Title of progress alert shown while wordlist is loading. For <!-- Title of progress alert shown while wordlist is loading. For
huge lists like Polish this can take a few seconds. --> huge lists like Polish this can take a few seconds. -->

View file

@ -2656,34 +2656,32 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1init
DictionaryCtxt* dict = (DictionaryCtxt*)dictPtr; DictionaryCtxt* dict = (DictionaryCtxt*)dictPtr;
if ( !!dict ) { if ( !!dict ) {
DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) );
data->globalState = globalState;
data->dict = dict_ref( dict, env );
data->depth = 2;
#ifdef DEBUG
data->guard = GI_GUARD;
#endif
PatDesc patDescs[3]; PatDesc patDescs[3];
XP_MEMSET( patDescs, 0, VSIZE(patDescs) * sizeof(patDescs[0]) ); XP_MEMSET( patDescs, 0, VSIZE(patDescs) * sizeof(patDescs[0]) );
int len = 0; int len = 0;
bool formatOK = true;
if ( !!jPatsArr ) { if ( !!jPatsArr ) {
len = (*env)->GetArrayLength( env, jPatsArr ); len = (*env)->GetArrayLength( env, jPatsArr );
XP_ASSERT( len == 3 ); XP_ASSERT( len == 3 );
for ( int ii = 0; ii < len ; ++ii ) { for ( int ii = 0; formatOK && ii < len ; ++ii ) {
jobject jdesc = (*env)->GetObjectArrayElement( env, jPatsArr, ii ); jobject jdesc = (*env)->GetObjectArrayElement( env, jPatsArr, ii );
if ( !!jdesc ) { if ( !!jdesc ) {
jbyteArray jtiles; jbyteArray jtiles;
if ( getObject( env, jdesc, "tilePat", "[B", &jtiles ) ) { if ( getObject( env, jdesc, "tilePat", "[B", &jtiles ) ) {
int nTiles = (*env)->GetArrayLength( env, jtiles ); int nTiles = (*env)->GetArrayLength( env, jtiles );
if ( 0 < nTiles ) { if ( 0 < nTiles ) {
patDescs[ii].nTiles = nTiles; PatDesc* pd = &patDescs[ii];
jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL ); /* If user adds too many tiles, we'll see it here */
XP_MEMCPY( &patDescs[ii].tiles[0], tiles, if ( nTiles <= VSIZE(pd->tiles) ) {
nTiles * sizeof(patDescs[ii].tiles[0]) ); pd->nTiles = nTiles;
(*env)->ReleaseByteArrayElements( env, jtiles, tiles, 0 ); jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL );
patDescs[ii].anyOrderOk = getBool( env, jdesc, "anyOrderOk" ); XP_MEMCPY( &pd->tiles[0], tiles, nTiles * sizeof(pd->tiles[0]) );
(*env)->ReleaseByteArrayElements( env, jtiles, tiles, 0 );
pd->anyOrderOk = getBool( env, jdesc, "anyOrderOk" );
} else {
formatOK = false;
}
} }
deleteLocalRef( env, jtiles ); deleteLocalRef( env, jtiles );
} }
@ -2692,14 +2690,27 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1init
} }
} }
DIMinMax mm = { .min = minLen, .max = maxLen }; DictIter* iter = NULL;
data->iter = di_makeIter( data->dict, env, &mm, NULL, 0, if ( formatOK ) {
!!jPatsArr ? patDescs : NULL, VSIZE(patDescs) ); DIMinMax mm = { .min = minLen, .max = maxLen };
iter = di_makeIter( dict, env, &mm, NULL, 0,
!!jPatsArr ? patDescs : NULL, VSIZE(patDescs) );
}
makeIndex( data ); if ( !!iter ) {
(void)di_firstWord( data->iter ); DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) );
data->iter = iter;
data->globalState = globalState;
data->dict = dict_ref( dict, env );
data->depth = 2;
#ifdef DEBUG
data->guard = GI_GUARD;
#endif
makeIndex( data );
(void)di_firstWord( data->iter );
closure = (jlong)data; closure = (jlong)data;
}
} }
return closure; return closure;
} }

View file

@ -145,6 +145,27 @@ typedef enum { PatErrNone,
PatErrDupInSet, PatErrDupInSet,
} PatErr; } PatErr;
#ifdef DEBUG
static const XP_UCHAR*
patErrToStr( PatErr err )
{
const XP_UCHAR* result = NULL;
# define CASESTR(s) case s: result = #s; break
switch ( err ) {
CASESTR(PatErrNone);
CASESTR(PatErrMissingClose);
CASESTR(PatErrMultipleSpellings);
CASESTR(PatErrBadCountTerm);
CASESTR(PatErrNoDigit);
CASESTR(PatErrTooComplex);
CASESTR(PatErrBogusTiles);
CASESTR(PatErrDupInSet);
}
# undef CASESTR
return result;
}
#endif
typedef struct _ParseState { typedef struct _ParseState {
const DictionaryCtxt* dict; const DictionaryCtxt* dict;
const XP_UCHAR* pat; const XP_UCHAR* pat;
@ -263,7 +284,19 @@ onFoundTiles( void* closure, const Tile* tiles, int len )
} }
return 1 == len && PatErrNone == data->err; return 1 == len && PatErrNone == data->err;
} }
static PatErr
addElem( ParseState* ps, PatElem* elem )
{
PatErr err = PatErrNone;
if ( ps->elemIndex < VSIZE(ps->elems) ) {
ps->elems[ps->elemIndex++] = *elem;
} else {
err = PatErrTooComplex;
}
return err;
}
static PatErr static PatErr
parseTile( ParseState* ps ) parseTile( ParseState* ps )
{ {
@ -390,9 +423,10 @@ compileParent( ParseState* ps )
return err; return err;
} }
static void static PatErr
initPS( ParseState* ps, const DictionaryCtxt* dict ) initPS( ParseState* ps, const DictionaryCtxt* dict )
{ {
PatErr result = PatErrNone;
XP_MEMSET( ps, 0, sizeof(*ps) ); XP_MEMSET( ps, 0, sizeof(*ps) );
XP_ASSERT( !!dict ); XP_ASSERT( !!dict );
ps->dict = dict; ps->dict = dict;
@ -407,8 +441,9 @@ initPS( ParseState* ps, const DictionaryCtxt* dict )
#ifdef WITH_START #ifdef WITH_START
PatElem start = { .typ = START, }; PatElem start = { .typ = START, };
ps->elems[ps->elemIndex++] = start; result = addElem( ps, &start );
#endif #endif
return result;
} }
static XP_Bool static XP_Bool
@ -416,19 +451,13 @@ compilePat( ParseState* ps, const XP_UCHAR* strPat )
{ {
ps->pat = strPat; ps->pat = strPat;
ps->patIndex = 0; ps->patIndex = 0;
// XP_ASSERT( !!iter->dict );
/* ParseState ps = { .dict = iter->dict, */
/* .pat = strPat, */
/* .blankMask = ((TileSet)1) << dict_getBlankTile( iter->dict ), */
/* }; */
PatErr err = compileParent( ps ); PatErr err = compileParent( ps );
XP_Bool success = err == PatErrNone && 0 < ps->elemIndex; XP_Bool success = err == PatErrNone && 0 < ps->elemIndex;
/* if ( success ) { */ if ( !success ) {
/* XP_ASSERT( ps.elemIndex < VSIZE(ps.elems) ); */ XP_LOGFF( "=> %s", patErrToStr(err) );
/* replacePat( iter, &ps ); */ }
/* } */
return success; return success;
} }
@ -1309,34 +1338,40 @@ firstWord( DictIter* iter, XP_Bool log )
return success; return success;
} }
static void static XP_Bool
addTilePats( ParseState* ps, const PatDesc* pd ) addTilePats( ParseState* ps, const PatDesc* pd )
{ {
XP_Bool success = XP_TRUE;
XP_Bool anyOrderOk = pd->anyOrderOk; XP_Bool anyOrderOk = pd->anyOrderOk;
PatElem elem = { .typ = CHILD, PatElem elem = { .typ = CHILD,
.minMatched = 1, .minMatched = 1,
.maxMatched = 1, .maxMatched = 1,
}; };
for ( int ii = 0; ii < pd->nTiles; ++ii ) { for ( int ii = 0; success && ii < pd->nTiles; ++ii ) {
#ifdef MULTI_SET #ifdef MULTI_SET
++elem.u.child.tiles.cnts[pd->tiles[ii]]; ++elem.u.child.tiles.cnts[pd->tiles[ii]];
#else #else
elem.u.child.tiles |= 1 << pd->tiles[ii]; elem.u.child.tiles |= 1 << pd->tiles[ii];
#endif #endif
if ( !anyOrderOk ) { if ( !anyOrderOk ) {
ps->elems[ps->elemIndex++] = elem; success = ps->elemIndex < VSIZE(ps->elems);
if ( success ) {
success = PatErrNone == addElem( ps, &elem );
#ifdef MULTI_SET #ifdef MULTI_SET
XP_MEMSET( &elem.u.child.tiles, 0, sizeof(elem.u.child.tiles) ); XP_MEMSET( &elem.u.child.tiles, 0, sizeof(elem.u.child.tiles) );
#else #else
elem.u.child.tiles = 0; elem.u.child.tiles = 0;
#endif #endif
}
} }
} }
if ( anyOrderOk ) { if ( anyOrderOk ) {
elem.u.child.flags |= FLAG_SINGLE; elem.u.child.flags |= FLAG_SINGLE;
elem.minMatched = elem.maxMatched = pd->nTiles; elem.minMatched = elem.maxMatched = pd->nTiles;
ps->elems[ps->elemIndex++] = elem; success = PatErrNone == addElem( ps, &elem );
} }
LOG_RETURNF( "%s", boolToStr(success) );
return success;
} }
static void static void
@ -1351,7 +1386,12 @@ addWildcard( ParseState* ps )
#else #else
elem.u.child.tiles = ps->blankMask; elem.u.child.tiles = ps->blankMask;
#endif #endif
ps->elems[ps->elemIndex++] = elem;
#ifdef DEBUG
PatErr err =
#endif
addElem( ps, &elem );
XP_ASSERT( err == PatErrNone );
} }
static void static void
@ -1404,11 +1444,13 @@ di_makeIter( const DictionaryCtxt* dict, XWEnv xwe, const DIMinMax* minmax,
if ( ii != STARTS_WITH ) { if ( ii != STARTS_WITH ) {
addWildcard( &ps ); addWildcard( &ps );
} }
addTilePats( &ps, ta ); success = addTilePats( &ps, ta );
if ( ii != ENDS_WITH ) { if ( success ) {
addWildcard( &ps ); if ( ii != ENDS_WITH ) {
addWildcard( &ps );
}
copyParsedPat( dict, &pats[nUsed++], &ps, NULL );
} }
copyParsedPat( dict, &pats[nUsed++], &ps, NULL );
} }
} }
} }

View file

@ -77,7 +77,7 @@ typedef struct DictIter DictIter;
* ends-with. The first is more powerful but I'm not sure it'll ever be part * ends-with. The first is more powerful but I'm not sure it'll ever be part
* of a shipping UI.*/ * of a shipping UI.*/
typedef struct _PatDesc { typedef struct _PatDesc {
Tile tiles[16]; Tile tiles[MAX_COLS_DICT];
XP_U16 nTiles; XP_U16 nTiles;
XP_Bool anyOrderOk; XP_Bool anyOrderOk;
} PatDesc; } PatDesc;

View file

@ -1948,7 +1948,6 @@ tmp_noop_sigintterm( int XP_UNUSED(sig) )
exit(0); exit(0);
} }
typedef struct _FTD { typedef struct _FTD {
PatDesc* desc; PatDesc* desc;
XP_Bool called; XP_Bool called;
@ -1960,9 +1959,7 @@ onFoundTiles2( void* closure, const Tile* tiles, int nTiles )
FTD* data = (FTD*)closure; FTD* data = (FTD*)closure;
if ( data->called ) { if ( data->called ) {
XP_LOGFF( "ERROR: called more than once; Hungarian case???" ); XP_LOGFF( "ERROR: called more than once; Hungarian case???" );
} else if ( nTiles > VSIZE(data->desc->tiles) ) { } else if ( nTiles <= VSIZE(data->desc->tiles) ) {
XP_ASSERT(0);
} else {
data->called = XP_TRUE; data->called = XP_TRUE;
data->desc->nTiles = nTiles; data->desc->nTiles = nTiles;
XP_MEMCPY( &data->desc->tiles[0], tiles, nTiles * sizeof(tiles[0]) ); XP_MEMCPY( &data->desc->tiles[0], tiles, nTiles * sizeof(tiles[0]) );
@ -2014,7 +2011,10 @@ patsParamsToIter( const LaunchParams* params, const DictionaryCtxt* dict )
} }
DictIter* iter = di_makeIter( dict, NULL_XWE, dimmp, strPats, nStrPats, DictIter* iter = di_makeIter( dict, NULL_XWE, dimmp, strPats, nStrPats,
descs, nPatDescs ); nPatDescs == 0 ? NULL : descs, nPatDescs );
if ( !iter ) {
XP_LOGFF( "Unable to build iter" );
}
return iter; return iter;
} }
@ -2025,37 +2025,39 @@ testGetNthWord( const LaunchParams* params, const DictionaryCtxt* dict,
const IndexData* data ) const IndexData* data )
{ {
DictIter* iter = patsParamsToIter( params, dict ); DictIter* iter = patsParamsToIter( params, dict );
XP_U32 half = di_countWords( iter, NULL ) / 2; if ( !!iter ) {
XP_U32 interval = half / 100; XP_U32 half = di_countWords( iter, NULL ) / 2;
const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */ XP_U32 interval = half / 100;
if ( interval == 0 ) { const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */
++interval; if ( interval == 0 ) {
} ++interval;
}
XP_UCHAR buf[64]; XP_UCHAR buf[64];
int ii, jj; int ii, jj;
for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) { for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) {
if ( di_getNthWord( iter, NULL_XWE, ii, depth, data ) ) { if ( di_getNthWord( iter, NULL_XWE, ii, depth, data ) ) {
XP_UCHAR buf[64]; XP_UCHAR buf[64];
di_wordToString( iter, buf, VSIZE(buf), delim ); di_wordToString( iter, buf, VSIZE(buf), delim );
XP_ASSERT( 0 == strcmp( buf, words[ii] ) ); XP_ASSERT( 0 == strcmp( buf, words[ii] ) );
# ifdef PRINT_ALL # ifdef PRINT_ALL
XP_LOGFF( "word[%d]: %s", ii, buf ); XP_LOGFF( "word[%d]: %s", ii, buf );
# endif # endif
} else { } else {
XP_ASSERT( 0 ); XP_ASSERT( 0 );
} }
if ( di_getNthWord( iter, NULL_XWE, jj, depth, data ) ) { if ( di_getNthWord( iter, NULL_XWE, jj, depth, data ) ) {
di_wordToString( iter, buf, VSIZE(buf), delim ); di_wordToString( iter, buf, VSIZE(buf), delim );
XP_ASSERT( 0 == strcmp( buf, words[jj] ) ); XP_ASSERT( 0 == strcmp( buf, words[jj] ) );
# ifdef PRINT_ALL # ifdef PRINT_ALL
XP_LOGFF( "word[%d]: %s", jj, buf ); XP_LOGFF( "word[%d]: %s", jj, buf );
# endif # endif
} else { } else {
XP_ASSERT( 0 ); XP_ASSERT( 0 );
}
} }
di_freeIter( iter, NULL_XWE );
} }
di_freeIter( iter, NULL_XWE );
} }
typedef struct _FTData { typedef struct _FTData {
@ -2109,124 +2111,126 @@ walk_dict_test( MPFORMAL const LaunchParams* params, const DictionaryCtxt* dict,
{ {
DictIter* iter = patsParamsToIter( params, dict ); DictIter* iter = patsParamsToIter( params, dict );
LengthsArray lens; if ( !!iter ) {
XP_U32 count = di_countWords( iter, &lens ); LengthsArray lens;
XP_U32 count = di_countWords( iter, &lens );
XP_U32 sum = 0; XP_U32 sum = 0;
for ( long ii = 0; ii < VSIZE(lens.lens); ++ii ) { for ( long ii = 0; ii < VSIZE(lens.lens); ++ii ) {
XP_LOGF( "%d words of length %ld", lens.lens[ii], ii ); XP_LOGF( "%d words of length %ld", lens.lens[ii], ii );
sum += lens.lens[ii]; sum += lens.lens[ii];
}
XP_ASSERT( sum == count );
if ( count > 0 ) {
const XP_UCHAR* delim = params->dumpDelim;
XP_Bool gotOne;
long jj;
char** words = g_malloc( count * sizeof(char*) );
XP_ASSERT( !!words );
for ( jj = 0, gotOne = di_firstWord( iter );
gotOne;
gotOne = di_getNextWord( iter ) ) {
XP_ASSERT( di_getPosition( iter ) == jj );
XP_UCHAR buf[64];
di_wordToString( iter, buf, VSIZE(buf), delim );
# ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif
if ( !!words ) {
words[jj] = g_strdup( buf );
}
++jj;
} }
XP_ASSERT( count == jj ); XP_ASSERT( sum == count );
XP_LOGFF( "comparing runs in both directions" ); if ( count > 0 ) {
for ( jj = 0, gotOne = di_lastWord( iter ); const XP_UCHAR* delim = params->dumpDelim;
gotOne; XP_Bool gotOne;
++jj, gotOne = di_getPrevWord( iter ) ) { long jj;
XP_ASSERT( di_getPosition(iter) == count-jj-1 ); char** words = g_malloc( count * sizeof(char*) );
XP_UCHAR buf[64]; XP_ASSERT( !!words );
di_wordToString( iter, buf, VSIZE(buf), delim );
for ( jj = 0, gotOne = di_firstWord( iter );
gotOne;
gotOne = di_getNextWord( iter ) ) {
XP_ASSERT( di_getPosition( iter ) == jj );
XP_UCHAR buf[64];
di_wordToString( iter, buf, VSIZE(buf), delim );
# ifdef PRINT_ALL # ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf ); fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif # endif
if ( !!words ) { if ( !!words ) {
if ( strcmp( buf, words[count-jj-1] ) ) { words[jj] = g_strdup( buf );
fprintf( stderr, "failure at %ld: %s going forward; %s " }
"going backward\n", jj, words[count-jj-1], buf ); ++jj;
break; }
XP_ASSERT( count == jj );
XP_LOGFF( "comparing runs in both directions" );
for ( jj = 0, gotOne = di_lastWord( iter );
gotOne;
++jj, gotOne = di_getPrevWord( iter ) ) {
XP_ASSERT( di_getPosition(iter) == count-jj-1 );
XP_UCHAR buf[64];
di_wordToString( iter, buf, VSIZE(buf), delim );
# ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif
if ( !!words ) {
if ( strcmp( buf, words[count-jj-1] ) ) {
fprintf( stderr, "failure at %ld: %s going forward; %s "
"going backward\n", jj, words[count-jj-1], buf );
break;
}
} }
} }
} XP_ASSERT( count == jj );
XP_ASSERT( count == jj ); XP_LOGFF( "FINISHED comparing runs in both directions" );
XP_LOGFF( "FINISHED comparing runs in both directions" );
XP_LOGFF( "testing getNth" ); XP_LOGFF( "testing getNth" );
testGetNthWord( params, dict, words, 0, NULL ); testGetNthWord( params, dict, words, 0, NULL );
XP_LOGFF( "FINISHED testing getNth" ); XP_LOGFF( "FINISHED testing getNth" );
XP_U16 depth = 2; XP_U16 depth = 2;
XP_U16 maxCount = dict_numTileFaces( dict ); XP_U16 maxCount = dict_numTileFaces( dict );
IndexData data; IndexData data;
data.count = maxCount * maxCount; /* squared because depth == 2! */ data.count = maxCount * maxCount; /* squared because depth == 2! */
data.indices = XP_MALLOC( mpool, data.indices = XP_MALLOC( mpool,
data.count * depth * sizeof(data.indices[0]) ); data.count * depth * sizeof(data.indices[0]) );
data.prefixes = XP_MALLOC( mpool, data.prefixes = XP_MALLOC( mpool,
depth * data.count * sizeof(data.prefixes[0]) ); depth * data.count * sizeof(data.prefixes[0]) );
XP_LOGF( "making index..." ); XP_LOGF( "making index..." );
di_makeIndex( iter, depth, &data ); di_makeIndex( iter, depth, &data );
XP_LOGF( "DONE making index (have %d indices)", data.count ); XP_LOGF( "DONE making index (have %d indices)", data.count );
/* Resize 'em in case not all slots filled */ /* Resize 'em in case not all slots filled */
data.indices = XP_REALLOC( mpool, data.indices, data.indices = XP_REALLOC( mpool, data.indices,
data.count * depth * sizeof(*data.indices) ); data.count * depth * sizeof(*data.indices) );
data.prefixes = XP_REALLOC( mpool, data.prefixes, data.prefixes = XP_REALLOC( mpool, data.prefixes,
depth * data.count * sizeof(*data.prefixes) ); depth * data.count * sizeof(*data.prefixes) );
#if 0 #if 0
for ( ii = 0; ii < nIndices; ++ii ) { for ( ii = 0; ii < nIndices; ++ii ) {
if ( !dict_getNthWord( dict, &word, indices[ii] ) ) { if ( !dict_getNthWord( dict, &word, indices[ii] ) ) {
XP_ASSERT( 0 ); XP_ASSERT( 0 );
}
XP_ASSERT( word.index == indices[ii] );
XP_UCHAR buf1[64];
dict_wordToString( dict, &word, buf1, VSIZE(buf1), delim );
XP_UCHAR buf2[64] = {0};
if ( ii > 0 && dict_getNthWord( dict, &word, indices[ii]-1 ) ) {
dict_wordToString( dict, &word, buf2, VSIZE(buf2), delim );
}
char prfx[8];
dict_tilesToString( dict, &prefixes[depth*ii], depth, prfx,
VSIZE(prfx), NULL );
fprintf( stderr, "%d: index: %ld; prefix: %s; word: %s (prev: %s)\n",
ii, indices[ii], prfx, buf1, buf2 );
} }
XP_ASSERT( word.index == indices[ii] );
XP_UCHAR buf1[64];
dict_wordToString( dict, &word, buf1, VSIZE(buf1), delim );
XP_UCHAR buf2[64] = {0};
if ( ii > 0 && dict_getNthWord( dict, &word, indices[ii]-1 ) ) {
dict_wordToString( dict, &word, buf2, VSIZE(buf2), delim );
}
char prfx[8];
dict_tilesToString( dict, &prefixes[depth*ii], depth, prfx,
VSIZE(prfx), NULL );
fprintf( stderr, "%d: index: %ld; prefix: %s; word: %s (prev: %s)\n",
ii, indices[ii], prfx, buf1, buf2 );
}
#endif #endif
XP_LOGFF( "testing getNth WITH INDEXING" ); XP_LOGFF( "testing getNth WITH INDEXING" );
testGetNthWord( params, dict, words, depth, &data ); testGetNthWord( params, dict, words, depth, &data );
XP_LOGFF( "DONE testing getNth WITH INDEXING" ); XP_LOGFF( "DONE testing getNth WITH INDEXING" );
if ( !!testPrefixes ) { if ( !!testPrefixes ) {
int ii; int ii;
guint count = g_slist_length( testPrefixes ); guint count = g_slist_length( testPrefixes );
for ( ii = 0; ii < count; ++ii ) { for ( ii = 0; ii < count; ++ii ) {
gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii ); gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii );
XP_LOGFF( "prefix %d: %s", ii, prefix ); XP_LOGFF( "prefix %d: %s", ii, prefix );
FTData foundTilesData = { .iter = iter, .words = words, FTData foundTilesData = { .iter = iter, .words = words,
.depth = depth, .data = &data, .depth = depth, .data = &data,
.prefix = prefix, }; .prefix = prefix, };
dict_tilesForString( dict, prefix, 0, onFoundTiles, &foundTilesData ); dict_tilesForString( dict, prefix, 0, onFoundTiles, &foundTilesData );
}
} }
XP_FREE( mpool, data.indices );
XP_FREE( mpool, data.prefixes );
} }
XP_FREE( mpool, data.indices ); di_freeIter( iter, NULL_XWE );
XP_FREE( mpool, data.prefixes );
} }
di_freeIter( iter, NULL_XWE ); XP_LOGFF( "done" );
XP_LOGF( "done" );
} }
static void static void
@ -2253,15 +2257,17 @@ static void
dumpDict( const LaunchParams* params, DictionaryCtxt* dict ) dumpDict( const LaunchParams* params, DictionaryCtxt* dict )
{ {
DictIter* iter = patsParamsToIter( params, dict ); DictIter* iter = patsParamsToIter( params, dict );
const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */ if ( !!iter ) {
for ( XP_Bool result = di_firstWord( iter ); const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */
result; for ( XP_Bool result = di_firstWord( iter );
result = di_getNextWord( iter ) ) { result;
XP_UCHAR buf[32]; result = di_getNextWord( iter ) ) {
di_wordToString( iter, buf, VSIZE(buf), delim ); XP_UCHAR buf[32];
fprintf( stdout, "%s\n", buf ); di_wordToString( iter, buf, VSIZE(buf), delim );
fprintf( stdout, "%s\n", buf );
}
di_freeIter( iter, NULL_XWE );
} }
di_freeIter( iter, NULL_XWE );
} }
static void static void
@ -2662,10 +2668,12 @@ testOneString( const LaunchParams* params, GSList* testDicts )
params->useMmap ); params->useMmap );
if ( NULL != dict ) { if ( NULL != dict ) {
DictIter* iter = patsParamsToIter( params, dict ); DictIter* iter = patsParamsToIter( params, dict );
if ( ! di_stringMatches( iter, params->iterTestPatStr ) ) { if ( !!iter ) {
result = 1; if ( ! di_stringMatches( iter, params->iterTestPatStr ) ) {
result = 1;
}
di_freeIter( iter, NULL_XWE );
} }
di_freeIter( iter, NULL_XWE );
} }
} }
return result; return result;