mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-27 09:58:45 +01:00
1281 lines
37 KiB
C
1281 lines
37 KiB
C
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
|
/*
|
|
* Copyright 1997 - 2022 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef USE_STDIO
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#include "comtypes.h"
|
|
#include "dictnryp.h"
|
|
#include "dictiter.h"
|
|
#include "xwstream.h"
|
|
#include "strutils.h"
|
|
#include "dictiter.h"
|
|
#include "game.h"
|
|
#include "dbgutil.h"
|
|
|
|
#ifdef CPLUS
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
****************************************************************************/
|
|
|
|
static XP_Bool makeBitmap( XP_U8 const** ptrp, const XP_U8* end );
|
|
static XP_U8* getCountsFor( const DictionaryCtxt* dict, XP_U16 nCols );
|
|
|
|
const DictionaryCtxt*
|
|
p_dict_ref( const DictionaryCtxt* dict, XWEnv XP_UNUSED(xwe)
|
|
#ifdef DEBUG_REF
|
|
,const char* func, const char* file, int line
|
|
#endif
|
|
)
|
|
{
|
|
if ( !!dict ) {
|
|
DictionaryCtxt* _dict = (DictionaryCtxt*)dict;
|
|
pthread_mutex_lock( &_dict->mutex );
|
|
++_dict->refCount;
|
|
#ifdef DEBUG_REF
|
|
XP_LOGFF( "(dict=%p): refCount now %d (from line %d of %s() in %s)",
|
|
dict, dict->refCount, line, func, file );
|
|
#endif
|
|
pthread_mutex_unlock( &_dict->mutex );
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
void
|
|
p_dict_unref( const DictionaryCtxt* dict, XWEnv xwe
|
|
#ifdef DEBUG_REF
|
|
,const char* func, const char* file, int line
|
|
#endif
|
|
)
|
|
{
|
|
if ( !!dict ) {
|
|
DictionaryCtxt* _dict = (DictionaryCtxt*)dict;
|
|
pthread_mutex_lock( &_dict->mutex );
|
|
XP_ASSERT( 0 != _dict->refCount );
|
|
--_dict->refCount;
|
|
XP_ASSERT( 0 <= _dict->refCount );
|
|
#ifdef DEBUG_REF
|
|
XP_LOGFF( "(dict=%p): refCount now %d (from line %d of %s() in %s)",
|
|
dict, dict->refCount, line, func, file );
|
|
#endif
|
|
pthread_mutex_unlock( &_dict->mutex );
|
|
if ( 0 == _dict->refCount ) {
|
|
/* There's a race here. If another thread locks the mutex we'll
|
|
still destroy the dict (and the locked mutex!!!) PENDING */
|
|
pthread_mutex_destroy( &_dict->mutex );
|
|
(*dict->destructor)( _dict, xwe );
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
dict_unref_all( PlayerDicts* pd, XWEnv xwe )
|
|
{
|
|
XP_U16 ii;
|
|
for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
|
|
dict_unref( pd->dicts[ii], xwe );
|
|
}
|
|
}
|
|
|
|
static XP_Bool
|
|
getNullTermParam( DictionaryCtxt* XP_UNUSED_DBG(dctx), XP_UCHAR** out,
|
|
const XP_U8** ptrp, const XP_U8* end )
|
|
{
|
|
const XP_U8* ptr = *ptrp;
|
|
XP_Bool success = ptr < end;
|
|
if ( success ) {
|
|
XP_U16 len = 1 + XP_STRLEN( (XP_UCHAR*)ptr );
|
|
success = ptr + len <= end;
|
|
if ( success ) {
|
|
*out = XP_MALLOC( dctx->mpool, len );
|
|
XP_MEMCPY( *out, ptr, len );
|
|
*ptrp += len;
|
|
}
|
|
}
|
|
XP_ASSERT( success || NULL == *out );
|
|
return success;
|
|
}
|
|
|
|
static XP_Bool
|
|
loadSpecialData( DictionaryCtxt* ctxt, XP_U8 const** ptrp,
|
|
const XP_U8* end )
|
|
{
|
|
LOG_FUNC();
|
|
XP_Bool success = XP_TRUE;
|
|
XP_U16 nSpecials = countSpecials( ctxt );
|
|
XP_U8 const* ptr = *ptrp;
|
|
XP_UCHAR** texts;
|
|
XP_UCHAR** textEnds;
|
|
SpecialBitmaps* bitmaps;
|
|
|
|
texts = (XP_UCHAR**)XP_MALLOC( ctxt->mpool,
|
|
nSpecials * sizeof(*texts) );
|
|
textEnds = (XP_UCHAR**)XP_MALLOC( ctxt->mpool,
|
|
nSpecials * sizeof(*textEnds) );
|
|
|
|
bitmaps = (SpecialBitmaps*)
|
|
XP_CALLOC( ctxt->mpool, nSpecials * sizeof(*bitmaps) );
|
|
|
|
for ( Tile ii = 0; ii < ctxt->nFaces; ++ii ) {
|
|
const XP_UCHAR* facep = ctxt->facePtrs[(short)ii];
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
/* get the string */
|
|
CHECK_PTR( ptr, 1, end, error );
|
|
XP_U8 txtlen = *ptr++;
|
|
CHECK_PTR( ptr, txtlen, end, error );
|
|
XP_UCHAR* text = (XP_UCHAR*)XP_MALLOC(ctxt->mpool, txtlen+1);
|
|
texts[(int)*facep] = text;
|
|
textEnds[(int)*facep] = text + txtlen + 1;
|
|
XP_MEMCPY( text, ptr, txtlen );
|
|
ptr += txtlen;
|
|
text[txtlen] = '\0';
|
|
XP_ASSERT( *facep < nSpecials ); /* firing */
|
|
|
|
/* This little hack is safe because all bytes but the first in a
|
|
multi-byte utf-8 char have the high bit set. SYNONYM_DELIM
|
|
does not have its high bit set */
|
|
XP_ASSERT( 0 == (SYNONYM_DELIM & 0x80) );
|
|
for ( ; '\0' != *text; ++text ) {
|
|
if ( *text == SYNONYM_DELIM ) {
|
|
*text = '\0';
|
|
}
|
|
}
|
|
|
|
if ( !makeBitmap( &ptr, end ) ) {
|
|
goto error;
|
|
}
|
|
if ( !makeBitmap( &ptr, end ) ) {
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto done;
|
|
error:
|
|
success = XP_FALSE;
|
|
done:
|
|
ctxt->chars = texts;
|
|
ctxt->charEnds = textEnds;
|
|
ctxt->bitmaps = bitmaps;
|
|
|
|
*ptrp = ptr;
|
|
return success;
|
|
} /* loadSpecialData */
|
|
|
|
XP_Bool
|
|
parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* end )
|
|
{
|
|
const XP_U8* ptr = *ptrp;
|
|
XP_Bool hasHeader = XP_FALSE;
|
|
XP_Bool isUTF8 = XP_FALSE;
|
|
XP_U16 charSize;
|
|
|
|
XP_U16 flags;
|
|
XP_Bool formatOk = sizeof(flags) <= end - ptr;
|
|
if ( formatOk ) {
|
|
XP_MEMCPY( &flags, ptr, sizeof(flags) );
|
|
ptr += sizeof( flags );
|
|
flags = XP_NTOHS(flags);
|
|
|
|
XP_LOGFF( "flags=0X%X", flags );
|
|
hasHeader = 0 != (DICT_HEADER_MASK & flags);
|
|
/* if ( hasHeader ) { */
|
|
/* flags &= ~DICT_HEADER_MASK; */
|
|
/* } */
|
|
|
|
XP_U8 nodeSize = 4;
|
|
switch ( flags & 0x0007 ) {
|
|
case 0x0001:
|
|
nodeSize = 3;
|
|
charSize = 1;
|
|
dctx->is_4_byte = XP_FALSE;
|
|
break;
|
|
case 0x0002:
|
|
nodeSize = 3;
|
|
charSize = 2;
|
|
dctx->is_4_byte = XP_FALSE;
|
|
break;
|
|
case 0x0003:
|
|
charSize = 2;
|
|
dctx->is_4_byte = XP_TRUE;
|
|
break;
|
|
case 0x0004:
|
|
nodeSize = 3;
|
|
isUTF8 = XP_TRUE;
|
|
dctx->is_4_byte = XP_FALSE;
|
|
break;
|
|
case 0x0005:
|
|
isUTF8 = XP_TRUE;
|
|
dctx->is_4_byte = XP_TRUE;
|
|
break;
|
|
default:
|
|
formatOk = XP_FALSE;
|
|
break;
|
|
}
|
|
dctx->isUTF8 = isUTF8;
|
|
dctx->nodeSize = nodeSize;
|
|
}
|
|
|
|
if ( formatOk ) {
|
|
XP_U8 numFaceBytes, numFaces;
|
|
|
|
if ( hasHeader ) {
|
|
const XP_U8* headerEnd;
|
|
{
|
|
XP_U16 headerLen;
|
|
XP_MEMCPY( &headerLen, ptr, sizeof(headerLen) );
|
|
ptr += sizeof(headerLen);
|
|
headerLen = XP_NTOHS( headerLen );
|
|
headerEnd = ptr + headerLen;
|
|
}
|
|
|
|
XP_U32 wordCount;
|
|
XP_MEMCPY( &wordCount, ptr, sizeof(wordCount) );
|
|
ptr += sizeof(wordCount);
|
|
dctx->nWords = XP_NTOHL( wordCount );
|
|
XP_DEBUGF( "dict contains %d words", dctx->nWords );
|
|
|
|
XP_ASSERT( ptr <= headerEnd );
|
|
if ( !getNullTermParam( dctx, &dctx->desc, &ptr, headerEnd ) ) {
|
|
XP_LOGFF( "no note" );
|
|
goto done;
|
|
}
|
|
XP_ASSERT( ptr < headerEnd );
|
|
|
|
if ( !getNullTermParam( dctx, &dctx->md5Sum, &ptr, headerEnd ) ) {
|
|
XP_LOGFF( "no md5Sum" );
|
|
goto done;
|
|
}
|
|
XP_U16 headerFlags = 0;
|
|
if ( ptr + sizeof(headerFlags) > headerEnd ) {
|
|
goto done;
|
|
}
|
|
XP_MEMCPY( &headerFlags, ptr, sizeof(headerFlags) );
|
|
headerFlags = XP_NTOHS( headerFlags );
|
|
ptr += sizeof(headerFlags);
|
|
|
|
XP_LOGFF( "setting headerFlags: 0x%x", headerFlags );
|
|
dctx->headerFlags = headerFlags;
|
|
|
|
if ( ptr == headerEnd ) {
|
|
goto done;
|
|
}
|
|
if ( getNullTermParam( dctx, &dctx->isoCode, &ptr, headerEnd )
|
|
&& getNullTermParam( dctx, &dctx->langName, &ptr, headerEnd ) ) {
|
|
XP_LOGFF( "got langName: %s; isoCode: %s", dctx->langName,
|
|
dctx->isoCode );
|
|
} else {
|
|
goto done;
|
|
}
|
|
XP_U8 othersLen = *ptr++;
|
|
if ( 0 < othersLen ) {
|
|
XP_ASSERT( ptr + othersLen <= headerEnd );
|
|
if ( ptr + othersLen <= headerEnd ) {
|
|
dctx->otherCounts = XP_MALLOC( dctx->mpool, othersLen );
|
|
dctx->otherCountsEnd = dctx->otherCounts + othersLen;
|
|
XP_MEMCPY( dctx->otherCounts, ptr, othersLen );
|
|
ptr += othersLen;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if ( ptr < headerEnd ) {
|
|
XP_LOGFF( "skipping %zu bytes of header", headerEnd - ptr );
|
|
}
|
|
ptr = headerEnd;
|
|
}
|
|
|
|
if ( isUTF8 ) {
|
|
numFaceBytes = *ptr++;
|
|
}
|
|
numFaces = *ptr++;
|
|
if ( !isUTF8 ) {
|
|
numFaceBytes = numFaces * charSize;
|
|
}
|
|
|
|
if ( NULL == dctx->md5Sum
|
|
#ifdef DEBUG
|
|
|| XP_TRUE
|
|
#endif
|
|
) {
|
|
XP_UCHAR checksum[256];
|
|
// XP_LOGFF( "figuring checksum with len: %uz", end - ptr );
|
|
computeChecksum( dctx, xwe, ptr, end - ptr, checksum );
|
|
if ( NULL == dctx->md5Sum ) {
|
|
dctx->md5Sum = copyString( dctx->mpool, checksum );
|
|
} else {
|
|
#ifndef PLATFORM_WASM
|
|
XP_ASSERT( 0 == XP_STRCMP( dctx->md5Sum, checksum ) );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
dctx->nFaces = numFaces;
|
|
|
|
dctx->values = XP_MALLOC( dctx->mpool, numFaces);
|
|
XP_U8* counts15 = XP_MALLOC( dctx->mpool, numFaces);
|
|
dctx->counts[15>>1] = counts15;
|
|
|
|
XP_U16 facesSize = numFaceBytes;
|
|
if ( !isUTF8 ) {
|
|
facesSize /= 2;
|
|
}
|
|
|
|
XP_U8 tmp[numFaceBytes];
|
|
XP_MEMCPY( tmp, ptr, numFaceBytes );
|
|
ptr += numFaceBytes;
|
|
|
|
dict_splitFaces( dctx, xwe, tmp, numFaceBytes, numFaces );
|
|
|
|
unsigned short xloc;
|
|
XP_MEMCPY( &xloc, ptr, sizeof(xloc) );
|
|
ptr += sizeof(xloc);
|
|
|
|
for ( int ii = 0; ii < numFaces; ++ii ) {
|
|
counts15[ii] = *ptr++;
|
|
dctx->values[ii] = *ptr++;
|
|
}
|
|
|
|
if ( NULL == dctx->isoCode ) {
|
|
XP_LangCode langCode = xloc & 0x7F;
|
|
const XP_UCHAR* isoCode = lcToLocale( langCode );
|
|
XP_ASSERT( !!isoCode );
|
|
dctx->isoCode = copyString( dctx->mpool, isoCode );
|
|
XP_LOGFF( "looked up isoCode %s for langCode %d", isoCode,
|
|
langCode );
|
|
}
|
|
#ifdef DEBUG
|
|
const XP_UCHAR* locale = lcToLocale( xloc & 0x7F );
|
|
XP_ASSERT( !locale || !XP_STRCMP( locale, dctx->isoCode ) );
|
|
#endif
|
|
}
|
|
|
|
if ( formatOk ) {
|
|
formatOk = loadSpecialData( dctx, &ptr, end );
|
|
}
|
|
|
|
if ( formatOk ) {
|
|
XP_ASSERT( ptr < end );
|
|
*ptrp = ptr;
|
|
}
|
|
|
|
LOG_RETURNF( "%s", boolToStr(formatOk) );
|
|
return formatOk;
|
|
} /* parseCommon */
|
|
|
|
static XP_Bool
|
|
makeBitmap( XP_U8 const** ptrp, const XP_U8* end )
|
|
{
|
|
XP_Bool success = XP_TRUE;
|
|
XP_U8 const* ptr = *ptrp;
|
|
CHECK_PTR( ptr, 1, end, error );
|
|
XP_U8 nCols = *ptr++;
|
|
if ( nCols > 0 ) {
|
|
CHECK_PTR( ptr, 1, end, error );
|
|
XP_U8 nRows = *ptr++;
|
|
CHECK_PTR( ptr, ((nRows*nCols)+7) / 8, end, error );
|
|
#ifdef DROP_BITMAPS
|
|
ptr += ((nRows*nCols)+7) / 8;
|
|
#else
|
|
do not compile
|
|
#endif
|
|
}
|
|
goto done;
|
|
error:
|
|
success = XP_FALSE;
|
|
done:
|
|
*ptrp = ptr;
|
|
return success;
|
|
}
|
|
|
|
XP_U16
|
|
countSpecials( DictionaryCtxt* ctxt )
|
|
{
|
|
XP_U16 result = 0;
|
|
|
|
for ( int ii = 0; ii < ctxt->nFaces; ++ii ) {
|
|
if ( IS_SPECIAL( ctxt->facePtrs[ii][0] ) ) {
|
|
++result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} /* countSpecials */
|
|
|
|
void
|
|
setBlankTile( DictionaryCtxt* dict )
|
|
{
|
|
dict->blankTile = -1; /* no known blank */
|
|
XP_U16 maxLen = 0;
|
|
for ( int tile = 0; tile < dict->nFaces; ++tile ) {
|
|
const XP_UCHAR* facePtr = dict->facePtrs[tile];
|
|
if ( facePtr[0] == 0 ) {
|
|
XP_ASSERT( dict->blankTile == -1 ); /* only one passes test? */
|
|
dict->blankTile = (XP_S8)tile;
|
|
}
|
|
|
|
if ( IS_SPECIAL( *facePtr ) ) {
|
|
facePtr = dict_getTileString( dict, tile );
|
|
}
|
|
XP_U16 thisLen = XP_STRLEN( facePtr );
|
|
if ( thisLen > maxLen ) {
|
|
maxLen = thisLen;
|
|
}
|
|
}
|
|
dict->maxChars = maxLen;
|
|
} /* setBlankTile */
|
|
|
|
/* #if defined BLANKS_FIRST || defined DEBUG */
|
|
XP_Bool
|
|
dict_hasBlankTile( const DictionaryCtxt* dict )
|
|
{
|
|
return dict->blankTile >= 0;
|
|
} /* dict_hasBlankTile */
|
|
/* #endif */
|
|
|
|
Tile
|
|
dict_getBlankTile( const DictionaryCtxt* dict )
|
|
{
|
|
XP_ASSERT( dict_hasBlankTile(dict) );
|
|
return (Tile)dict->blankTile;
|
|
} /* dict_getBlankTile */
|
|
|
|
XP_U16
|
|
dict_getTileValue( const DictionaryCtxt* dict, const Tile tile )
|
|
{
|
|
XP_ASSERT( !!dict );
|
|
if ( (tile & TILE_VALUE_MASK) != tile ) {
|
|
XP_ASSERT( tile == 32 &&
|
|
tile == dict_getBlankTile( dict ) );
|
|
}
|
|
XP_ASSERT( tile < dict->nFaces );
|
|
XP_ASSERT( !!dict->values );
|
|
return dict->values[tile];
|
|
} /* dict_getTileValue */
|
|
|
|
static const XP_UCHAR*
|
|
dict_getTileStringRaw( const DictionaryCtxt* dict, Tile tile )
|
|
{
|
|
XP_ASSERT( tile < dict->nFaces );
|
|
return dict->facePtrs[tile];
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getTileString( const DictionaryCtxt* dict, Tile tile )
|
|
{
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
facep = dict->chars[(XP_U16)*facep];
|
|
}
|
|
return facep;
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getNextTileString( const DictionaryCtxt* dict, Tile tile,
|
|
const XP_UCHAR* cur )
|
|
{
|
|
const XP_UCHAR* result = NULL;
|
|
if ( NULL == cur ) {
|
|
result = dict_getTileString( dict, tile );
|
|
} else {
|
|
cur += XP_STRLEN( cur ) + 1;
|
|
XP_Bool isSpecial = dict_faceIsBitmap( dict, tile );
|
|
if ( isSpecial || tile == dict->blankTile ) {
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
if ( cur < dict->charEnds[(XP_U16)*facep] ) {
|
|
result = cur;
|
|
}
|
|
} else {
|
|
/* use cur only if it is is not now off the end or pointing to to the
|
|
next tile */
|
|
if ( ++tile == dict->nFaces ) {
|
|
if ( cur < dict->facesEnd ) {
|
|
result = cur;
|
|
}
|
|
} else {
|
|
const XP_UCHAR* nxt = dict_getTileStringRaw( dict, tile );
|
|
if ( nxt != cur ) {
|
|
result = cur;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
XP_U16
|
|
dict_numTilesForSize( const DictionaryCtxt* dict, Tile tile, XP_U16 nCols )
|
|
{
|
|
XP_U8* counts = getCountsFor( dict, nCols );
|
|
return counts[tile];
|
|
}
|
|
|
|
/* Older wordlists are built assuming 15x15 boards. Different sized boards
|
|
need different numbers of tiles. The wordlist might provide for the size we
|
|
have, in which case we use it. Otherwise we extrapolate.
|
|
*/
|
|
static XP_U8*
|
|
getCountsFor( const DictionaryCtxt* dict, XP_U16 nCols )
|
|
{
|
|
int offset = nCols >> 1;
|
|
XP_U8* counts = dict->counts[offset];
|
|
if ( !counts ) {
|
|
counts = XP_MALLOC( dict->mpool, dict->nFaces );
|
|
((DictionaryCtxt*)dict)->counts[offset] = counts;
|
|
|
|
XP_U8* ptr = dict->otherCounts;
|
|
XP_Bool found = XP_FALSE;
|
|
while ( !found && !!ptr && ptr < dict->otherCountsEnd ) {
|
|
XP_U8 siz = *ptr++;
|
|
found = siz == nCols;
|
|
if ( found ) {
|
|
XP_MEMCPY( counts, ptr, dict->nFaces );
|
|
} else {
|
|
ptr += dict->nFaces;
|
|
}
|
|
}
|
|
|
|
/* don't have it in the wordlist? Extrapolate */
|
|
if ( !found ) {
|
|
const Tile blank = dict_getBlankTile( dict );
|
|
|
|
XP_U16 pct = (nCols * nCols * 100) / (15 * 15);
|
|
XP_U8* src15 = dict->counts[15>>1];
|
|
XP_ASSERT( !!src15 );
|
|
|
|
for ( Tile tile = 0; tile < dict->nFaces; ++tile ) {
|
|
XP_U16 count = src15[tile];
|
|
XP_U16 newCount = count * pct / 100;
|
|
if ( 50 < (count * pct) % 100 ) {
|
|
++newCount;
|
|
}
|
|
if ( tile == blank && newCount > MAX_NUM_BLANKS ) {
|
|
newCount = MAX_NUM_BLANKS;
|
|
}
|
|
XP_ASSERT( tile != blank || newCount <= MAX_NUM_BLANKS );
|
|
counts[tile] = newCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
// XP_LOGFF( "(tile=%d, ncols=%d) => %d", tile, nCols, count );
|
|
return counts;
|
|
} /* getCountsFor */
|
|
|
|
XP_U16
|
|
dict_numTileFaces( const DictionaryCtxt* dict )
|
|
{
|
|
return dict->nFaces;
|
|
} /* dict_numTileFaces */
|
|
|
|
XP_U16
|
|
dict_getMaxTileChars( const DictionaryCtxt* ctxt )
|
|
{
|
|
XP_ASSERT( 0 != ctxt->maxChars );
|
|
return ctxt->maxChars;
|
|
}
|
|
|
|
static void
|
|
appendIfSpace( XP_UCHAR** bufp, const XP_UCHAR* end, const XP_UCHAR* newtxt )
|
|
{
|
|
XP_U16 len = XP_STRLEN( newtxt );
|
|
if ( *bufp + len < end ) {
|
|
XP_MEMCPY( *bufp, newtxt, len );
|
|
*bufp += len;
|
|
} else {
|
|
*bufp = NULL;
|
|
}
|
|
}
|
|
|
|
XP_U16
|
|
dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles,
|
|
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize,
|
|
const XP_UCHAR* delim )
|
|
{
|
|
XP_UCHAR* bufp = buf;
|
|
const XP_UCHAR* end = bufp + bufSize;
|
|
XP_U16 delimLen = NULL == delim ? 0 : XP_STRLEN(delim);
|
|
|
|
for ( int ii = 0; ii < nTiles && !!bufp; ++ii ) {
|
|
|
|
if ( 0 < delimLen && 0 < ii ) {
|
|
appendIfSpace( &bufp, end, delim );
|
|
}
|
|
|
|
Tile tile = tiles[ii];
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
XP_UCHAR* chars = dict->chars[(XP_U16)*facep];
|
|
appendIfSpace( &bufp, end, chars );
|
|
} else {
|
|
XP_ASSERT ( tile != dict->blankTile ); /* printing blank should be
|
|
handled by specials
|
|
mechanism */
|
|
appendIfSpace( &bufp, end, facep );
|
|
}
|
|
}
|
|
|
|
XP_U16 result = 0;
|
|
if ( !!bufp && bufp < end ) {
|
|
*bufp = '\0';
|
|
result = bufp - buf;
|
|
}
|
|
return result;
|
|
} /* dict_tilesToString */
|
|
|
|
/* Convert str to an array of tiles, continuing until we fail to match or we
|
|
* run out of room in which to return tiles. Failure to match means return of
|
|
* XP_FALSE, but if we run out of room before failing we return XP_TRUE.
|
|
*/
|
|
|
|
static XP_Bool
|
|
tilesForStringImpl( const DictionaryCtxt* dict,
|
|
const XP_UCHAR* str, XP_U16 strLen,
|
|
Tile* tiles, XP_U16 nTiles, XP_U16 nFound,
|
|
OnFoundTiles proc, void* closure )
|
|
{
|
|
XP_Bool goOn;
|
|
if ( nFound == nTiles || 0 == strLen ) {
|
|
/* We've recursed to the end and have found a tile! */
|
|
goOn = (*proc)( closure, tiles, nFound );
|
|
} else {
|
|
goOn = XP_TRUE;
|
|
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
for ( Tile tile = 0; goOn && tile < nFaces; ++tile ) {
|
|
for ( const XP_UCHAR* facep = NULL; ; ) {
|
|
facep = dict_getNextTileString( dict, tile, facep );
|
|
if ( !facep ) {
|
|
break;
|
|
}
|
|
XP_U16 faceLen = XP_STRLEN( facep );
|
|
if ( 0 == XP_STRNCMP( facep, str, faceLen ) ) {
|
|
tiles[nFound] = tile;
|
|
goOn = tilesForStringImpl( dict, str + faceLen,
|
|
strLen - faceLen,
|
|
tiles, nTiles, nFound + 1,
|
|
proc, closure );
|
|
break; /* impossible to have than one match per tile */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return goOn;
|
|
} /* tilesForStringImpl */
|
|
|
|
void
|
|
dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* str,
|
|
XP_U16 strLen, OnFoundTiles proc, void* closure )
|
|
{
|
|
Tile tiles[32];
|
|
if ( 0 == strLen ) {
|
|
strLen = XP_STRLEN( str );
|
|
}
|
|
tilesForStringImpl( dict, str, strLen, tiles, VSIZE(tiles), 0, proc, closure );
|
|
} /* dict_tilesForString */
|
|
|
|
XP_Bool
|
|
dict_tilesAreSame( const DictionaryCtxt* dict1, const DictionaryCtxt* dict2 )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
|
|
XP_ASSERT( !!dict1 );
|
|
XP_ASSERT( !!dict2 );
|
|
|
|
Tile ii;
|
|
XP_U16 nTileFaces = dict_numTileFaces( dict1 );
|
|
|
|
if ( nTileFaces == dict_numTileFaces( dict2 ) ) {
|
|
for ( ii = 0; ii < nTileFaces; ++ii ) {
|
|
|
|
const XP_UCHAR* face1;
|
|
const XP_UCHAR* face2;
|
|
|
|
if ( dict_getTileValue( dict1, ii )
|
|
!= dict_getTileValue( dict2, ii ) ){
|
|
break;
|
|
}
|
|
face1 = dict_getTileStringRaw( dict1, ii );
|
|
face2 = dict_getTileStringRaw( dict2, ii );
|
|
if ( IS_SPECIAL(*face1) != IS_SPECIAL(*face2) ) {
|
|
break;
|
|
}
|
|
if ( IS_SPECIAL(*face1) ) {
|
|
XP_UCHAR* chars1 = dict1->chars[(int)*face1];
|
|
XP_UCHAR* chars2 = dict2->chars[(int)*face2];
|
|
XP_U16 len = XP_STRLEN(chars1);
|
|
if ( 0 != XP_STRNCMP( chars1, chars2, len ) ) {
|
|
break;
|
|
}
|
|
} else if ( 0 != XP_STRCMP( face1, face2 ) ) {
|
|
break;
|
|
}
|
|
if ( dict_numTilesForSize( dict1, ii, 15 ) != dict_numTilesForSize( dict2, ii, 15 ) ) {
|
|
break;
|
|
}
|
|
}
|
|
result = ii == nTileFaces; /* did we get that far */
|
|
}
|
|
return result;
|
|
} /* dict_tilesAreSame */
|
|
|
|
/* Summarize tile info in a way it can be presented to users */
|
|
void
|
|
dict_writeTilesInfo( const DictionaryCtxt* dict, XP_U16 boardSize, XWStreamCtxt* stream )
|
|
{
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
for ( Tile tile = 0; tile < nFaces; ++tile ) {
|
|
XP_U16 val = dict_getTileValue( dict, tile );
|
|
XP_U16 count = dict_numTilesForSize( dict, tile, boardSize );
|
|
const XP_UCHAR* face = dict_getTileString( dict, tile );
|
|
XP_UCHAR buf[32];
|
|
XP_SNPRINTF( buf, VSIZE(buf), "%s\t%d\t%d\n", face, count, val );
|
|
stream_catString( stream, buf );
|
|
}
|
|
}
|
|
|
|
void
|
|
dict_writeToStream( const DictionaryCtxt* XP_UNUSED(dict),
|
|
XWStreamCtxt* XP_UNUSED(stream) )
|
|
{
|
|
XP_ASSERT(0);
|
|
/* XP_U16 maxCount = 0; */
|
|
/* XP_U16 maxValue = 0; */
|
|
/* XP_U16 ii, nSpecials; */
|
|
/* XP_U16 maxCountBits, maxValueBits; */
|
|
/* XP_UCHAR buf[64]; */
|
|
/* XP_U16 nBytes; */
|
|
|
|
/* stream_putBits( stream, 6, dict->nFaces ); */
|
|
|
|
/* XP_ASSERT(0); /\* if this fires, need to fix for per-boardSize counts *\/ */
|
|
/* for ( ii = 0; ii < dict->nFaces*2; ii+=2 ) { */
|
|
/* XP_U16 count, value; */
|
|
|
|
/* count = dict->countsAndValues[ii]; */
|
|
/* if ( maxCount < count ) { */
|
|
/* maxCount = count; */
|
|
/* } */
|
|
|
|
/* value = dict->countsAndValues[ii+1]; */
|
|
/* if ( maxValue < value ) { */
|
|
/* maxValue = value; */
|
|
/* } */
|
|
/* } */
|
|
|
|
/* maxCountBits = bitsForMax( maxCount ); */
|
|
/* maxValueBits = bitsForMax( maxValue ); */
|
|
|
|
/* stream_putBits( stream, 3, maxCountBits ); /\* won't be bigger than 5 *\/ */
|
|
/* stream_putBits( stream, 3, maxValueBits ); */
|
|
|
|
/* for ( ii = 0; ii < dict->nFaces; ++ii ) { */
|
|
/* stream_putBits( stream, maxCountBits, counts[ii] ); */
|
|
/* stream_putBits( stream, maxValueBits, dict->values[ii] ); */
|
|
/* } */
|
|
|
|
/* /\* Stream format of the faces is unchanged: chars run together, which */
|
|
/* * happens to equal utf-8 for ascii. But now there may be more than one */
|
|
/* * byte per face. Old code assumes that, but compatibility is ensured by */
|
|
/* * the caller which will not accept an incoming message if the version's */
|
|
/* * too new. And utf-8 dicts are flagged as newer by the sender. */
|
|
/* *\/ */
|
|
|
|
/* nBytes = sizeof(buf); */
|
|
/* ucharsToNarrow( dict, buf, &nBytes ); */
|
|
/* stream_putU8( stream, nBytes ); */
|
|
/* stream_putBytes( stream, buf, nBytes ); */
|
|
|
|
/* for ( nSpecials = ii = 0; ii < dict->nFaces; ++ii ) { */
|
|
/* const XP_UCHAR* facep = dict_getTileStringRaw( dict, (Tile)ii ); */
|
|
/* if ( IS_SPECIAL( *facep ) ) { */
|
|
/* stringToStream( stream, dict->chars[nSpecials++] ); */
|
|
/* } */
|
|
/* } */
|
|
} /* dict_writeToStream */
|
|
|
|
static void
|
|
freeSpecials( DictionaryCtxt* dict )
|
|
{
|
|
Tile tt;
|
|
XP_U16 nSpecials;
|
|
|
|
for ( nSpecials = tt = 0; tt < dict->nFaces; ++tt ) {
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tt );
|
|
if ( IS_SPECIAL( *facep ) ) {
|
|
|
|
XP_ASSERT( !!dict->chars[nSpecials] );
|
|
XP_FREE( dict->mpool, dict->chars[nSpecials] );
|
|
|
|
XP_FREEP( dict->mpool, &dict->bitmaps[nSpecials].largeBM );
|
|
XP_FREEP( dict->mpool, &dict->bitmaps[nSpecials].smallBM );
|
|
|
|
++nSpecials;
|
|
}
|
|
}
|
|
if ( nSpecials > 0 ) {
|
|
XP_FREE( dict->mpool, dict->chars );
|
|
XP_FREE( dict->mpool, dict->bitmaps );
|
|
}
|
|
} /* freeSpecials */
|
|
|
|
static void
|
|
common_destructor( DictionaryCtxt* dict, XWEnv XP_UNUSED(xwe) )
|
|
{
|
|
freeSpecials( dict );
|
|
|
|
XP_FREE( dict->mpool, dict->values );
|
|
for ( int ii = 0; ii < VSIZE(dict->counts); ++ii ) {
|
|
XP_FREEP( dict->mpool, &dict->counts[ii] );
|
|
}
|
|
XP_FREE( dict->mpool, dict->otherCounts );
|
|
XP_FREE( dict->mpool, dict->faces );
|
|
XP_FREE( dict->mpool, dict->facePtrs );
|
|
|
|
XP_FREE( dict->mpool, dict );
|
|
} /* common_destructor */
|
|
|
|
void
|
|
dict_loadFromStream( DictionaryCtxt* dict, XWEnv xwe, XWStreamCtxt* stream )
|
|
{
|
|
XP_ASSERT(0); /* if this fires, need to fix for per-boardSize counts */
|
|
XP_U8 nFaces, nFaceBytes;
|
|
XP_U16 maxCountBits, maxValueBits;
|
|
XP_U16 ii, nSpecials;
|
|
XP_UCHAR* localTexts[32];
|
|
XP_U8 utf8[MAX_UNIQUE_TILES];
|
|
|
|
if ( !!dict->destructor ) {
|
|
XP_LOGF( "%s(): replacing destructor!!", __func__ );
|
|
}
|
|
dict->destructor = common_destructor;
|
|
dict->func_dict_getShortName = dict_getName; /* default */
|
|
|
|
nFaces = (XP_U8)stream_getBits( stream, 6 );
|
|
maxCountBits = (XP_U16)stream_getBits( stream, 3 );
|
|
maxValueBits = (XP_U16)stream_getBits( stream, 3 );
|
|
|
|
dict->nFaces = nFaces;
|
|
|
|
dict->values = (XP_U8*)XP_MALLOC( dict->mpool,
|
|
sizeof(dict->values[0]) * nFaces );
|
|
XP_U8* counts = dict->counts[15>>1]
|
|
= (XP_U8*)XP_MALLOC( dict->mpool, sizeof(dict->values[0]) * nFaces );
|
|
|
|
for ( ii = 0; ii < dict->nFaces; ++ii ) {
|
|
counts[ii] = (XP_U8)stream_getBits( stream, maxCountBits );
|
|
dict->values[ii] = (XP_U8)stream_getBits( stream, maxValueBits );
|
|
}
|
|
|
|
nFaceBytes = (XP_U8)stream_getU8( stream );
|
|
XP_ASSERT( nFaceBytes < VSIZE(utf8) );
|
|
stream_getBytes( stream, utf8, nFaceBytes );
|
|
dict->isUTF8 = XP_TRUE; /* need to communicate this in stream */
|
|
dict_splitFaces( dict, xwe, utf8, nFaceBytes, nFaces );
|
|
|
|
for ( nSpecials = ii = 0; ii < nFaces; ++ii ) {
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, (Tile)ii );
|
|
if ( IS_SPECIAL( *facep ) ) {
|
|
XP_UCHAR* txt = stringFromStream( dict->mpool, stream );
|
|
XP_ASSERT( !!txt );
|
|
localTexts[nSpecials] = txt;
|
|
|
|
++nSpecials;
|
|
}
|
|
}
|
|
if ( nSpecials > 0 ) {
|
|
dict->bitmaps =
|
|
(SpecialBitmaps*)XP_MALLOC( dict->mpool,
|
|
nSpecials * sizeof(*dict->bitmaps) );
|
|
XP_MEMSET( dict->bitmaps, 0, nSpecials * sizeof(*dict->bitmaps) );
|
|
|
|
dict->chars = (XP_UCHAR**)XP_MALLOC( dict->mpool,
|
|
nSpecials * sizeof(*dict->chars) );
|
|
XP_MEMCPY(dict->chars, localTexts, nSpecials * sizeof(*dict->chars));
|
|
}
|
|
setBlankTile( dict );
|
|
} /* dict_loadFromStream */
|
|
|
|
#ifdef TEXT_MODEL
|
|
/* Return the strlen of the longest face, e.g. 1 for English and Italian;
|
|
2 for Spanish; 3 for Catalan */
|
|
XP_U16
|
|
dict_getMaxWidth( const DictionaryCtxt* dict )
|
|
{
|
|
XP_U16 result = 0;
|
|
Tile tile;
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
|
|
for ( tile = 0; tile < nFaces; ++tile ) {
|
|
const XP_UCHAR* face = dict_getTileString( dict, tile );
|
|
XP_U16 len = XP_STRLEN( face );
|
|
if ( len > result ) {
|
|
result = len;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
|
|
const XP_UCHAR*
|
|
dict_getName( const DictionaryCtxt* dict )
|
|
{
|
|
XP_ASSERT( !!dict );
|
|
XP_ASSERT( !!dict->name );
|
|
return dict->name;
|
|
} /* dict_getName */
|
|
|
|
XP_Bool
|
|
dict_isUTF8( const DictionaryCtxt* dict )
|
|
{
|
|
XP_ASSERT( !!dict );
|
|
return dict->isUTF8;
|
|
}
|
|
|
|
XP_Bool
|
|
dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile )
|
|
{
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
return IS_SPECIAL(*facep) && (tile != dict->blankTile);
|
|
} /* dict_faceIsBitmap */
|
|
|
|
void
|
|
dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile, XP_Bitmaps* bmps )
|
|
{
|
|
SpecialBitmaps* bitmaps;
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
|
|
XP_ASSERT( dict_faceIsBitmap( dict, tile ) );
|
|
XP_ASSERT( !!dict->bitmaps );
|
|
|
|
bitmaps = &dict->bitmaps[(XP_U16)*facep];
|
|
bmps->nBitmaps = 2;
|
|
bmps->bmps[0] = bitmaps->smallBM;
|
|
bmps->bmps[1] = bitmaps->largeBM;
|
|
} /* dict_getFaceBitmaps */
|
|
|
|
XP_U32
|
|
dict_getWordCount( const DictionaryCtxt* dict, XWEnv xwe )
|
|
{
|
|
XP_U32 nWords = dict->nWords;
|
|
#ifdef XWFEATURE_WALKDICT
|
|
if ( 0 == nWords ) {
|
|
DictIter* iter = di_makeIter( dict, xwe, NULL, NULL, 0, NULL, 0 );
|
|
nWords = di_countWords( iter, NULL );
|
|
di_freeIter( iter, xwe );
|
|
}
|
|
#endif
|
|
return nWords;
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getDesc( const DictionaryCtxt* dict )
|
|
{
|
|
return dict->desc;
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getISOCode( const DictionaryCtxt* dict )
|
|
{
|
|
return dict->isoCode;
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getMd5Sum( const DictionaryCtxt* dict )
|
|
{
|
|
return dict->md5Sum;
|
|
}
|
|
|
|
XP_Bool
|
|
dict_hasDuplicates( const DictionaryCtxt* dict )
|
|
{
|
|
return 0 != (dict->headerFlags & HEADERFLAGS_DUPS_SUPPORTED_BIT);
|
|
}
|
|
|
|
#ifdef STUBBED_DICT
|
|
|
|
#define BLANK_FACE '\0'
|
|
|
|
static XP_U8 stub_english_data[] = {
|
|
/* count value face */
|
|
9, 1, 'A',
|
|
2, 3, 'B',
|
|
2, 3, 'C',
|
|
4, 2, 'D',
|
|
12, 1, 'E',
|
|
2, 4, 'F',
|
|
3, 2, 'G',
|
|
2, 4, 'H',
|
|
9, 1, 'I',
|
|
1, 8, 'J',
|
|
1, 5, 'K',
|
|
4, 1, 'L',
|
|
2, 3, 'M',
|
|
6, 1, 'N',
|
|
8, 1, 'O',
|
|
2, 3, 'P',
|
|
1, 10, 'Q',
|
|
6, 1, 'R',
|
|
4, 1, 'S',
|
|
6, 1, 'T',
|
|
4, 1, 'U',
|
|
2, 4, 'V',
|
|
2, 4, 'W',
|
|
1, 8, 'X',
|
|
2, 4, 'Y',
|
|
1, 10, 'Z',
|
|
2, 0, BLANK_FACE, /* BLANK1 */
|
|
};
|
|
|
|
void
|
|
setStubbedSpecials( DictionaryCtxt* dict )
|
|
{
|
|
dict->chars = (XP_UCHAR**)XP_MALLOC( dict->mpool, sizeof(char*) );
|
|
dict->chars[0] = "_";
|
|
|
|
} /* setStubbedSpecials */
|
|
|
|
void
|
|
destroy_stubbed_dict( DictionaryCtxt* dict )
|
|
{
|
|
XP_FREE( dict->mpool, dict->countsAndValues );
|
|
XP_FREE( dict->mpool, dict->faces );
|
|
XP_FREE( dict->mpool, dict->chars );
|
|
XP_FREE( dict->mpool, dict->name );
|
|
XP_FREE( dict->mpool, dict->langName );
|
|
XP_FREE( dict->mpool, dict->isoCode );
|
|
XP_FREE( dict->mpool, dict->bitmaps );
|
|
XP_FREE( dict->mpool, dict );
|
|
} /* destroy_stubbed_dict */
|
|
|
|
DictionaryCtxt*
|
|
make_stubbed_dict( MPFORMAL_NOCOMMA )
|
|
{
|
|
DictionaryCtxt* dict = (DictionaryCtxt*)XP_MALLOC( mpool, sizeof(*dict) );
|
|
XP_U8* data = stub_english_data;
|
|
XP_U16 datasize = sizeof(stub_english_data);
|
|
XP_U16 ii;
|
|
|
|
XP_MEMSET( dict, 0, sizeof(*dict) );
|
|
|
|
MPASSIGN( dict->mpool, mpool );
|
|
dict->name = copyString( mpool, "Stub dictionary" );
|
|
dict->nFaces = datasize/3;
|
|
|
|
dict->destructor = destroy_stubbed_dict;
|
|
|
|
dict->faces = (XP_UCHAR*)
|
|
XP_MALLOC( mpool, 2 * dict->nFaces * sizeof(dict->faces[0]) );
|
|
dict->facePtrs = (XP_UCHAR**)
|
|
XP_MALLOC( mpool, dict->nFaces * sizeof(dict->facePtrs[0]) );
|
|
|
|
XP_UCHAR* nextChar = dict->faces;
|
|
XP_UCHAR** nextPtr = dict->facePtrs;
|
|
for ( ii = 0; ii < datasize/3; ++ii ) {
|
|
*nextPtr++ = nextChar;
|
|
*nextChar++ = (XP_UCHAR)data[(ii*3)+2];
|
|
*nextChar++ = '\0';
|
|
}
|
|
|
|
dict->countsAndValues = (XP_U8*)XP_MALLOC( mpool, dict->nFaces*2 );
|
|
for ( ii = 0; ii < datasize/3; ++ii ) {
|
|
dict->countsAndValues[ii*2] = data[(ii*3)];
|
|
dict->countsAndValues[(ii*2)+1] = data[(ii*3)+1];
|
|
}
|
|
|
|
dict->bitmaps = (SpecialBitmaps*)XP_MALLOC( mpool, sizeof(SpecialBitmaps) );
|
|
dict->bitmaps->largeBM = dict->bitmaps->largeBM = NULL;
|
|
|
|
setStubbedSpecials( dict );
|
|
|
|
setBlankTile( dict );
|
|
|
|
return dict;
|
|
} /* make_stubbed_dict */
|
|
|
|
#endif /* STUBBED_DICT */
|
|
|
|
static array_edge*
|
|
dict_super_edge_for_index( const DictionaryCtxt* dict, XP_U32 index )
|
|
{
|
|
array_edge* result;
|
|
|
|
if ( index == 0 ) {
|
|
result = NULL;
|
|
} else {
|
|
XP_ASSERT( index < dict->numEdges );
|
|
/* avoid long-multiplication lib call on Palm... */
|
|
if ( dict->nodeSize == 3 ) {
|
|
index += (index << 1);
|
|
} else {
|
|
XP_ASSERT( dict->nodeSize == 4 );
|
|
index <<= 2;
|
|
}
|
|
result = &dict->base[index];
|
|
}
|
|
return result;
|
|
} /* dict_edge_for_index */
|
|
|
|
static array_edge*
|
|
dict_super_getTopEdge( const DictionaryCtxt* dict )
|
|
{
|
|
return dict->topEdge;
|
|
} /* dict_super_getTopEdge */
|
|
|
|
static XP_U32
|
|
dict_super_index_from( const DictionaryCtxt* dict, array_edge* p_edge )
|
|
{
|
|
unsigned long result;
|
|
|
|
array_edge_new* edge = (array_edge_new*)p_edge;
|
|
result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF;
|
|
|
|
if ( dict->is_4_byte ) {
|
|
result |= ((XP_U32)edge->moreBits) << 16;
|
|
} else {
|
|
XP_ASSERT( dict->nodeSize == 3 );
|
|
if ( (edge->bits & EXTRABITMASK_NEW) != 0 ) {
|
|
result |= 0x00010000; /* using | instead of + saves 4 bytes */
|
|
}
|
|
}
|
|
return result;
|
|
} /* dict_super_index_from */
|
|
|
|
static array_edge*
|
|
dict_super_follow( const DictionaryCtxt* dict, array_edge* in )
|
|
{
|
|
XP_U32 index = dict_index_from( dict, in );
|
|
array_edge* result = index > 0?
|
|
dict_edge_for_index( dict, index ): (array_edge*)NULL;
|
|
return result;
|
|
} /* dict_super_follow */
|
|
|
|
static array_edge*
|
|
dict_super_edge_with_tile( const DictionaryCtxt* dict, array_edge* from,
|
|
Tile tile )
|
|
{
|
|
for ( ; ; ) {
|
|
Tile candidate = EDGETILE(dict,from);
|
|
if ( candidate == tile ) {
|
|
break;
|
|
}
|
|
|
|
if ( IS_LAST_EDGE(dict, from ) ) {
|
|
from = NULL;
|
|
break;
|
|
}
|
|
from += dict->nodeSize;
|
|
}
|
|
|
|
return from;
|
|
} /* edge_with_tile */
|
|
|
|
void
|
|
dict_super_init( MPFORMAL DictionaryCtxt* dict )
|
|
{
|
|
MPASSIGN(dict->mpool, mpool);
|
|
|
|
/* subclass may change these later.... */
|
|
dict->func_edge_for_index = dict_super_edge_for_index;
|
|
dict->func_dict_getTopEdge = dict_super_getTopEdge;
|
|
dict->func_dict_index_from = dict_super_index_from;
|
|
dict->func_dict_follow = dict_super_follow;
|
|
dict->func_dict_edge_with_tile = dict_super_edge_with_tile;
|
|
dict->func_dict_getShortName = dict_getName;
|
|
|
|
pthread_mutex_init( &dict->mutex, NULL );
|
|
} /* dict_super_init */
|
|
|
|
void
|
|
dict_super_destroy( DictionaryCtxt* dict )
|
|
{
|
|
XP_FREEP( dict->mpool, &dict->desc );
|
|
XP_FREEP( dict->mpool, &dict->md5Sum );
|
|
XP_FREEP( dict->mpool, &dict->values );
|
|
for ( int ii = 0; ii < VSIZE(dict->counts); ++ii ) {
|
|
XP_FREEP( dict->mpool, &dict->counts[ii] );
|
|
}
|
|
XP_FREEP( dict->mpool, &dict->otherCounts );
|
|
XP_FREEP( dict->mpool, &dict->faces );
|
|
XP_FREEP( dict->mpool, &dict->facePtrs );
|
|
XP_FREEP( dict->mpool, &dict->name );
|
|
XP_FREEP( dict->mpool, &dict->isoCode );
|
|
XP_FREEP( dict->mpool, &dict->langName );
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getLangName( const DictionaryCtxt* ctxt )
|
|
{
|
|
return ctxt->langName;
|
|
}
|
|
|
|
#ifdef XWFEATURE_DICTSANITY
|
|
XP_Bool
|
|
checkSanity( DictionaryCtxt* dict, const XP_U32 numEdges )
|
|
{
|
|
XP_U32 ii;
|
|
XP_Bool passed = XP_TRUE;
|
|
|
|
array_edge* edge = dict->base;
|
|
if ( NULL != edge ) { /* not empty dict */
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
Tile prevTile = 0;
|
|
for ( ii = 0; ii < numEdges && passed; ++ii ) {
|
|
Tile tile = EDGETILE( dict, edge );
|
|
if ( tile < prevTile || tile >= nFaces ) {
|
|
XP_LOGF( "%s: node %d (out of %d) has too-large or "
|
|
"out-of-order tile", __func__, ii, numEdges );
|
|
passed = XP_FALSE;
|
|
break;
|
|
}
|
|
prevTile = tile;
|
|
|
|
XP_U32 index = dict_index_from( dict, edge );
|
|
if ( index >= numEdges ) {
|
|
XP_LOGF( "%s: node %d (out of %d) has too-high index %d",
|
|
__func__, ii, numEdges, index );
|
|
passed = XP_FALSE;
|
|
break;
|
|
}
|
|
|
|
if ( IS_LAST_EDGE( dict, edge ) ) {
|
|
prevTile = 0;
|
|
}
|
|
edge += dict->nodeSize;
|
|
}
|
|
|
|
if ( passed ) {
|
|
passed = 0 == prevTile; /* last edge seen was a LAST_EDGE */
|
|
}
|
|
}
|
|
|
|
return passed;
|
|
} /* checkSanity */
|
|
#endif
|
|
|
|
#ifdef CPLUS
|
|
}
|
|
#endif
|