From 7a43e95aa887ebdb03d0c63b09be07b3ba2608b8 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 11 Aug 2020 14:13:11 -0700 Subject: [PATCH] fix crashes when filter strings are too long --- .../android/xw4/DictBrowseDelegate.java | 12 +- .../org/eehouse/android/xw4/jni/XwJNI.java | 6 +- .../app/src/main/res/values/strings.xml | 2 + xwords4/android/jni/xwjni.c | 53 ++-- xwords4/common/dictiter.c | 88 ++++-- xwords4/common/dictiter.h | 2 +- xwords4/linux/linuxmain.c | 284 +++++++++--------- 7 files changed, 259 insertions(+), 188 deletions(-) diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictBrowseDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictBrowseDelegate.java index 4dd84cdda..1bab20f5e 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictBrowseDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictBrowseDelegate.java @@ -700,10 +700,14 @@ public class DictBrowseDelegate extends DelegateBase public void run() { stopProgress(); - m_browseState.onFilterAccepted( m_dict, null ); - initList( wrapper ); - setFindPats( m_browseState.m_pats ); - + if ( null != wrapper ) { + m_browseState.onFilterAccepted( m_dict, null ); + initList( wrapper ); + setFindPats( m_browseState.m_pats ); + } else { + makeOkOnlyBuilder(R.string.alrt_bad_filter ) + .show(); + } newFeatureAlert(); } } ); diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java index a8a4c5660..4e1e546a5 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java @@ -619,9 +619,13 @@ public class XwJNI { new Thread( new Runnable() { @Override public void run() { + IterWrapper wrapper = null; long iterPtr = di_init( jniState, dictPtr, pats, minLen, maxLen ); - callback.onIterReady( new IterWrapper(iterPtr) ); + if ( 0 != iterPtr ) { + wrapper = new IterWrapper(iterPtr); + } + callback.onIterReady( wrapper ); } } ).start(); } diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml index bed4f78f7..9fa8d0d48 100644 --- a/xwords4/android/app/src/main/res/values/strings.xml +++ b/xwords4/android/app/src/main/res/values/strings.xml @@ -1615,6 +1615,8 @@ The minimum length value cannot be greater than the maximum value. + + Unable to apply filter. diff --git a/xwords4/android/jni/xwjni.c b/xwords4/android/jni/xwjni.c index 1ebdb218c..ef1be8d1f 100644 --- a/xwords4/android/jni/xwjni.c +++ b/xwords4/android/jni/xwjni.c @@ -2656,34 +2656,32 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1init DictionaryCtxt* dict = (DictionaryCtxt*)dictPtr; 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]; XP_MEMSET( patDescs, 0, VSIZE(patDescs) * sizeof(patDescs[0]) ); int len = 0; + bool formatOK = true; if ( !!jPatsArr ) { len = (*env)->GetArrayLength( env, jPatsArr ); 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 ); if ( !!jdesc ) { jbyteArray jtiles; if ( getObject( env, jdesc, "tilePat", "[B", &jtiles ) ) { int nTiles = (*env)->GetArrayLength( env, jtiles ); if ( 0 < nTiles ) { - patDescs[ii].nTiles = nTiles; - jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL ); - XP_MEMCPY( &patDescs[ii].tiles[0], tiles, - nTiles * sizeof(patDescs[ii].tiles[0]) ); - (*env)->ReleaseByteArrayElements( env, jtiles, tiles, 0 ); - patDescs[ii].anyOrderOk = getBool( env, jdesc, "anyOrderOk" ); + PatDesc* pd = &patDescs[ii]; + /* If user adds too many tiles, we'll see it here */ + if ( nTiles <= VSIZE(pd->tiles) ) { + pd->nTiles = nTiles; + jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL ); + 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 ); } @@ -2692,14 +2690,27 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1init } } - DIMinMax mm = { .min = minLen, .max = maxLen }; - data->iter = di_makeIter( data->dict, env, &mm, NULL, 0, - !!jPatsArr ? patDescs : NULL, VSIZE(patDescs) ); + DictIter* iter = NULL; + if ( formatOK ) { + DIMinMax mm = { .min = minLen, .max = maxLen }; + iter = di_makeIter( dict, env, &mm, NULL, 0, + !!jPatsArr ? patDescs : NULL, VSIZE(patDescs) ); + } - makeIndex( data ); - (void)di_firstWord( data->iter ); + if ( !!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; } diff --git a/xwords4/common/dictiter.c b/xwords4/common/dictiter.c index 813418ff3..5f0c60e30 100644 --- a/xwords4/common/dictiter.c +++ b/xwords4/common/dictiter.c @@ -145,6 +145,27 @@ typedef enum { PatErrNone, PatErrDupInSet, } 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 { const DictionaryCtxt* dict; const XP_UCHAR* pat; @@ -263,7 +284,19 @@ onFoundTiles( void* closure, const Tile* tiles, int len ) } 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 parseTile( ParseState* ps ) { @@ -390,9 +423,10 @@ compileParent( ParseState* ps ) return err; } -static void +static PatErr initPS( ParseState* ps, const DictionaryCtxt* dict ) { + PatErr result = PatErrNone; XP_MEMSET( ps, 0, sizeof(*ps) ); XP_ASSERT( !!dict ); ps->dict = dict; @@ -407,8 +441,9 @@ initPS( ParseState* ps, const DictionaryCtxt* dict ) #ifdef WITH_START PatElem start = { .typ = START, }; - ps->elems[ps->elemIndex++] = start; + result = addElem( ps, &start ); #endif + return result; } static XP_Bool @@ -416,19 +451,13 @@ compilePat( ParseState* ps, const XP_UCHAR* strPat ) { ps->pat = strPat; 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 ); XP_Bool success = err == PatErrNone && 0 < ps->elemIndex; - /* if ( success ) { */ - /* XP_ASSERT( ps.elemIndex < VSIZE(ps.elems) ); */ - /* replacePat( iter, &ps ); */ - /* } */ + if ( !success ) { + XP_LOGFF( "=> %s", patErrToStr(err) ); + } return success; } @@ -1309,34 +1338,40 @@ firstWord( DictIter* iter, XP_Bool log ) return success; } -static void +static XP_Bool addTilePats( ParseState* ps, const PatDesc* pd ) { + XP_Bool success = XP_TRUE; XP_Bool anyOrderOk = pd->anyOrderOk; PatElem elem = { .typ = CHILD, .minMatched = 1, .maxMatched = 1, }; - for ( int ii = 0; ii < pd->nTiles; ++ii ) { + for ( int ii = 0; success && ii < pd->nTiles; ++ii ) { #ifdef MULTI_SET ++elem.u.child.tiles.cnts[pd->tiles[ii]]; #else elem.u.child.tiles |= 1 << pd->tiles[ii]; #endif if ( !anyOrderOk ) { - ps->elems[ps->elemIndex++] = elem; + success = ps->elemIndex < VSIZE(ps->elems); + if ( success ) { + success = PatErrNone == addElem( ps, &elem ); #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 - elem.u.child.tiles = 0; + elem.u.child.tiles = 0; #endif + } } } if ( anyOrderOk ) { elem.u.child.flags |= FLAG_SINGLE; 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 @@ -1351,7 +1386,12 @@ addWildcard( ParseState* ps ) #else elem.u.child.tiles = ps->blankMask; #endif - ps->elems[ps->elemIndex++] = elem; + +#ifdef DEBUG + PatErr err = +#endif + addElem( ps, &elem ); + XP_ASSERT( err == PatErrNone ); } static void @@ -1404,11 +1444,13 @@ di_makeIter( const DictionaryCtxt* dict, XWEnv xwe, const DIMinMax* minmax, if ( ii != STARTS_WITH ) { addWildcard( &ps ); } - addTilePats( &ps, ta ); - if ( ii != ENDS_WITH ) { - addWildcard( &ps ); + success = addTilePats( &ps, ta ); + if ( success ) { + if ( ii != ENDS_WITH ) { + addWildcard( &ps ); + } + copyParsedPat( dict, &pats[nUsed++], &ps, NULL ); } - copyParsedPat( dict, &pats[nUsed++], &ps, NULL ); } } } diff --git a/xwords4/common/dictiter.h b/xwords4/common/dictiter.h index 14af91d79..de28462dd 100644 --- a/xwords4/common/dictiter.h +++ b/xwords4/common/dictiter.h @@ -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 * of a shipping UI.*/ typedef struct _PatDesc { - Tile tiles[16]; + Tile tiles[MAX_COLS_DICT]; XP_U16 nTiles; XP_Bool anyOrderOk; } PatDesc; diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index f077c888e..eeb82f2c5 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -1948,7 +1948,6 @@ tmp_noop_sigintterm( int XP_UNUSED(sig) ) exit(0); } - typedef struct _FTD { PatDesc* desc; XP_Bool called; @@ -1960,9 +1959,7 @@ onFoundTiles2( void* closure, const Tile* tiles, int nTiles ) FTD* data = (FTD*)closure; if ( data->called ) { XP_LOGFF( "ERROR: called more than once; Hungarian case???" ); - } else if ( nTiles > VSIZE(data->desc->tiles) ) { - XP_ASSERT(0); - } else { + } else if ( nTiles <= VSIZE(data->desc->tiles) ) { data->called = XP_TRUE; data->desc->nTiles = nTiles; 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, - descs, nPatDescs ); + nPatDescs == 0 ? NULL : descs, nPatDescs ); + if ( !iter ) { + XP_LOGFF( "Unable to build iter" ); + } return iter; } @@ -2025,37 +2025,39 @@ testGetNthWord( const LaunchParams* params, const DictionaryCtxt* dict, const IndexData* data ) { DictIter* iter = patsParamsToIter( params, dict ); - XP_U32 half = di_countWords( iter, NULL ) / 2; - XP_U32 interval = half / 100; - const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */ - if ( interval == 0 ) { - ++interval; - } + if ( !!iter ) { + XP_U32 half = di_countWords( iter, NULL ) / 2; + XP_U32 interval = half / 100; + const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */ + if ( interval == 0 ) { + ++interval; + } - XP_UCHAR buf[64]; - int ii, jj; - for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) { - if ( di_getNthWord( iter, NULL_XWE, ii, depth, data ) ) { - XP_UCHAR buf[64]; - di_wordToString( iter, buf, VSIZE(buf), delim ); - XP_ASSERT( 0 == strcmp( buf, words[ii] ) ); + XP_UCHAR buf[64]; + int ii, jj; + for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) { + if ( di_getNthWord( iter, NULL_XWE, ii, depth, data ) ) { + XP_UCHAR buf[64]; + di_wordToString( iter, buf, VSIZE(buf), delim ); + XP_ASSERT( 0 == strcmp( buf, words[ii] ) ); # ifdef PRINT_ALL - XP_LOGFF( "word[%d]: %s", ii, buf ); + XP_LOGFF( "word[%d]: %s", ii, buf ); # endif - } else { - XP_ASSERT( 0 ); - } - if ( di_getNthWord( iter, NULL_XWE, jj, depth, data ) ) { - di_wordToString( iter, buf, VSIZE(buf), delim ); - XP_ASSERT( 0 == strcmp( buf, words[jj] ) ); + } else { + XP_ASSERT( 0 ); + } + if ( di_getNthWord( iter, NULL_XWE, jj, depth, data ) ) { + di_wordToString( iter, buf, VSIZE(buf), delim ); + XP_ASSERT( 0 == strcmp( buf, words[jj] ) ); # ifdef PRINT_ALL - XP_LOGFF( "word[%d]: %s", jj, buf ); + XP_LOGFF( "word[%d]: %s", jj, buf ); # endif - } else { - XP_ASSERT( 0 ); + } else { + XP_ASSERT( 0 ); + } } + di_freeIter( iter, NULL_XWE ); } - di_freeIter( iter, NULL_XWE ); } typedef struct _FTData { @@ -2109,124 +2111,126 @@ walk_dict_test( MPFORMAL const LaunchParams* params, const DictionaryCtxt* dict, { DictIter* iter = patsParamsToIter( params, dict ); - LengthsArray lens; - XP_U32 count = di_countWords( iter, &lens ); + if ( !!iter ) { + LengthsArray lens; + XP_U32 count = di_countWords( iter, &lens ); - XP_U32 sum = 0; - for ( long ii = 0; ii < VSIZE(lens.lens); ++ii ) { - XP_LOGF( "%d words of length %ld", lens.lens[ii], 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_U32 sum = 0; + for ( long ii = 0; ii < VSIZE(lens.lens); ++ii ) { + XP_LOGF( "%d words of length %ld", lens.lens[ii], ii ); + sum += lens.lens[ii]; } - XP_ASSERT( count == jj ); + XP_ASSERT( sum == count ); - 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 ); + 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 ); + 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; + if ( !!words ) { + words[jj] = g_strdup( buf ); + } + ++jj; + } + 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_LOGFF( "FINISHED comparing runs in both directions" ); + XP_ASSERT( count == jj ); + XP_LOGFF( "FINISHED comparing runs in both directions" ); - XP_LOGFF( "testing getNth" ); - testGetNthWord( params, dict, words, 0, NULL ); - XP_LOGFF( "FINISHED testing getNth" ); + XP_LOGFF( "testing getNth" ); + testGetNthWord( params, dict, words, 0, NULL ); + XP_LOGFF( "FINISHED testing getNth" ); - XP_U16 depth = 2; - XP_U16 maxCount = dict_numTileFaces( dict ); - IndexData data; - data.count = maxCount * maxCount; /* squared because depth == 2! */ - data.indices = XP_MALLOC( mpool, - data.count * depth * sizeof(data.indices[0]) ); - data.prefixes = XP_MALLOC( mpool, - depth * data.count * sizeof(data.prefixes[0]) ); + XP_U16 depth = 2; + XP_U16 maxCount = dict_numTileFaces( dict ); + IndexData data; + data.count = maxCount * maxCount; /* squared because depth == 2! */ + data.indices = XP_MALLOC( mpool, + data.count * depth * sizeof(data.indices[0]) ); + data.prefixes = XP_MALLOC( mpool, + depth * data.count * sizeof(data.prefixes[0]) ); - XP_LOGF( "making index..." ); - di_makeIndex( iter, depth, &data ); - XP_LOGF( "DONE making index (have %d indices)", data.count ); + XP_LOGF( "making index..." ); + di_makeIndex( iter, depth, &data ); + XP_LOGF( "DONE making index (have %d indices)", data.count ); - /* Resize 'em in case not all slots filled */ - data.indices = XP_REALLOC( mpool, data.indices, - data.count * depth * sizeof(*data.indices) ); - data.prefixes = XP_REALLOC( mpool, data.prefixes, - depth * data.count * sizeof(*data.prefixes) ); + /* Resize 'em in case not all slots filled */ + data.indices = XP_REALLOC( mpool, data.indices, + data.count * depth * sizeof(*data.indices) ); + data.prefixes = XP_REALLOC( mpool, data.prefixes, + depth * data.count * sizeof(*data.prefixes) ); #if 0 - for ( ii = 0; ii < nIndices; ++ii ) { - if ( !dict_getNthWord( dict, &word, indices[ii] ) ) { - XP_ASSERT( 0 ); + for ( ii = 0; ii < nIndices; ++ii ) { + if ( !dict_getNthWord( dict, &word, indices[ii] ) ) { + 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 - XP_LOGFF( "testing getNth WITH INDEXING" ); - testGetNthWord( params, dict, words, depth, &data ); - XP_LOGFF( "DONE testing getNth WITH INDEXING" ); + XP_LOGFF( "testing getNth WITH INDEXING" ); + testGetNthWord( params, dict, words, depth, &data ); + XP_LOGFF( "DONE testing getNth WITH INDEXING" ); - if ( !!testPrefixes ) { - int ii; - guint count = g_slist_length( testPrefixes ); - for ( ii = 0; ii < count; ++ii ) { - gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii ); - XP_LOGFF( "prefix %d: %s", ii, prefix ); + if ( !!testPrefixes ) { + int ii; + guint count = g_slist_length( testPrefixes ); + for ( ii = 0; ii < count; ++ii ) { + gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii ); + XP_LOGFF( "prefix %d: %s", ii, prefix ); - FTData foundTilesData = { .iter = iter, .words = words, - .depth = depth, .data = &data, - .prefix = prefix, }; - dict_tilesForString( dict, prefix, 0, onFoundTiles, &foundTilesData ); + FTData foundTilesData = { .iter = iter, .words = words, + .depth = depth, .data = &data, + .prefix = prefix, }; + dict_tilesForString( dict, prefix, 0, onFoundTiles, &foundTilesData ); + } } + XP_FREE( mpool, data.indices ); + XP_FREE( mpool, data.prefixes ); } - XP_FREE( mpool, data.indices ); - XP_FREE( mpool, data.prefixes ); + di_freeIter( iter, NULL_XWE ); } - di_freeIter( iter, NULL_XWE ); - XP_LOGF( "done" ); + XP_LOGFF( "done" ); } static void @@ -2253,15 +2257,17 @@ static void dumpDict( const LaunchParams* params, DictionaryCtxt* dict ) { DictIter* iter = patsParamsToIter( params, dict ); - const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */ - for ( XP_Bool result = di_firstWord( iter ); - result; - result = di_getNextWord( iter ) ) { - XP_UCHAR buf[32]; - di_wordToString( iter, buf, VSIZE(buf), delim ); - fprintf( stdout, "%s\n", buf ); + if ( !!iter ) { + const XP_UCHAR* delim = params->dumpDelim; /* NULL is ok */ + for ( XP_Bool result = di_firstWord( iter ); + result; + result = di_getNextWord( iter ) ) { + XP_UCHAR buf[32]; + di_wordToString( iter, buf, VSIZE(buf), delim ); + fprintf( stdout, "%s\n", buf ); + } + di_freeIter( iter, NULL_XWE ); } - di_freeIter( iter, NULL_XWE ); } static void @@ -2662,10 +2668,12 @@ testOneString( const LaunchParams* params, GSList* testDicts ) params->useMmap ); if ( NULL != dict ) { DictIter* iter = patsParamsToIter( params, dict ); - if ( ! di_stringMatches( iter, params->iterTestPatStr ) ) { - result = 1; + if ( !!iter ) { + if ( ! di_stringMatches( iter, params->iterTestPatStr ) ) { + result = 1; + } + di_freeIter( iter, NULL_XWE ); } - di_freeIter( iter, NULL_XWE ); } } return result;