| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #ifndef SQLITE_DISABLE_FTS3_UNICODE |
|
|
| #include "fts3Int.h" |
| #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) |
|
|
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
|
|
| #include "fts3_tokenizer.h" |
|
|
| |
| |
| |
| |
| |
| #ifndef SQLITE_AMALGAMATION |
|
|
| static const unsigned char sqlite3Utf8Trans1[] = { |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
| 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
| 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
| 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
| 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, |
| }; |
|
|
| #define READ_UTF8(zIn, zTerm, c) \ |
| c = *(zIn++); \ |
| if( c>=0xc0 ){ \ |
| c = sqlite3Utf8Trans1[c-0xc0]; \ |
| while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ |
| c = (c<<6) + (0x3f & *(zIn++)); \ |
| } \ |
| if( c<0x80 \ |
| || (c&0xFFFFF800)==0xD800 \ |
| || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| } |
|
|
| #define WRITE_UTF8(zOut, c) { \ |
| if( c<0x00080 ){ \ |
| *zOut++ = (u8)(c&0xFF); \ |
| } \ |
| else if( c<0x00800 ){ \ |
| *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \ |
| *zOut++ = 0x80 + (u8)(c & 0x3F); \ |
| } \ |
| else if( c<0x10000 ){ \ |
| *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \ |
| *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ |
| *zOut++ = 0x80 + (u8)(c & 0x3F); \ |
| }else{ \ |
| *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \ |
| *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ |
| *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ |
| *zOut++ = 0x80 + (u8)(c & 0x3F); \ |
| } \ |
| } |
|
|
| #endif |
|
|
| typedef struct unicode_tokenizer unicode_tokenizer; |
| typedef struct unicode_cursor unicode_cursor; |
|
|
| struct unicode_tokenizer { |
| sqlite3_tokenizer base; |
| int eRemoveDiacritic; |
| int nException; |
| int *aiException; |
| }; |
|
|
| struct unicode_cursor { |
| sqlite3_tokenizer_cursor base; |
| const unsigned char *aInput; |
| int nInput; |
| int iOff; |
| int iToken; |
| char *zToken; |
| int nAlloc; |
| }; |
|
|
|
|
| |
| |
| |
| static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){ |
| if( pTokenizer ){ |
| unicode_tokenizer *p = (unicode_tokenizer *)pTokenizer; |
| sqlite3_free(p->aiException); |
| sqlite3_free(p); |
| } |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int unicodeAddExceptions( |
| unicode_tokenizer *p, |
| int bAlnum, |
| const char *zIn, |
| int nIn |
| ){ |
| const unsigned char *z = (const unsigned char *)zIn; |
| const unsigned char *zTerm = &z[nIn]; |
| unsigned int iCode; |
| int nEntry = 0; |
|
|
| assert( bAlnum==0 || bAlnum==1 ); |
|
|
| while( z<zTerm ){ |
| READ_UTF8(z, zTerm, iCode); |
| assert( (sqlite3FtsUnicodeIsalnum((int)iCode) & 0xFFFFFFFE)==0 ); |
| if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum |
| && sqlite3FtsUnicodeIsdiacritic((int)iCode)==0 |
| ){ |
| nEntry++; |
| } |
| } |
|
|
| if( nEntry ){ |
| int *aNew; |
| int nNew; |
|
|
| aNew = sqlite3_realloc64(p->aiException,(p->nException+nEntry)*sizeof(int)); |
| if( aNew==0 ) return SQLITE_NOMEM; |
| nNew = p->nException; |
|
|
| z = (const unsigned char *)zIn; |
| while( z<zTerm ){ |
| READ_UTF8(z, zTerm, iCode); |
| if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum |
| && sqlite3FtsUnicodeIsdiacritic((int)iCode)==0 |
| ){ |
| int i, j; |
| for(i=0; i<nNew && aNew[i]<(int)iCode; i++); |
| for(j=nNew; j>i; j--) aNew[j] = aNew[j-1]; |
| aNew[i] = (int)iCode; |
| nNew++; |
| } |
| } |
| p->aiException = aNew; |
| p->nException = nNew; |
| } |
|
|
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static int unicodeIsException(unicode_tokenizer *p, int iCode){ |
| if( p->nException>0 ){ |
| int *a = p->aiException; |
| int iLo = 0; |
| int iHi = p->nException-1; |
|
|
| while( iHi>=iLo ){ |
| int iTest = (iHi + iLo) / 2; |
| if( iCode==a[iTest] ){ |
| return 1; |
| }else if( iCode>a[iTest] ){ |
| iLo = iTest+1; |
| }else{ |
| iHi = iTest-1; |
| } |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| |
| |
| |
| |
| static int unicodeIsAlnum(unicode_tokenizer *p, int iCode){ |
| assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 ); |
| return sqlite3FtsUnicodeIsalnum(iCode) ^ unicodeIsException(p, iCode); |
| } |
|
|
| |
| |
| |
| static int unicodeCreate( |
| int nArg, |
| const char * const *azArg, |
| sqlite3_tokenizer **pp |
| ){ |
| unicode_tokenizer *pNew; |
| int i; |
| int rc = SQLITE_OK; |
|
|
| pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer)); |
| if( pNew==NULL ) return SQLITE_NOMEM; |
| memset(pNew, 0, sizeof(unicode_tokenizer)); |
| pNew->eRemoveDiacritic = 1; |
|
|
| for(i=0; rc==SQLITE_OK && i<nArg; i++){ |
| const char *z = azArg[i]; |
| int n = (int)strlen(z); |
|
|
| if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){ |
| pNew->eRemoveDiacritic = 1; |
| } |
| else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){ |
| pNew->eRemoveDiacritic = 0; |
| } |
| else if( n==19 && memcmp("remove_diacritics=2", z, 19)==0 ){ |
| pNew->eRemoveDiacritic = 2; |
| } |
| else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){ |
| rc = unicodeAddExceptions(pNew, 1, &z[11], n-11); |
| } |
| else if( n>=11 && memcmp("separators=", z, 11)==0 ){ |
| rc = unicodeAddExceptions(pNew, 0, &z[11], n-11); |
| } |
| else{ |
| |
| rc = SQLITE_ERROR; |
| } |
| } |
|
|
| if( rc!=SQLITE_OK ){ |
| unicodeDestroy((sqlite3_tokenizer *)pNew); |
| pNew = 0; |
| } |
| *pp = (sqlite3_tokenizer *)pNew; |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static int unicodeOpen( |
| sqlite3_tokenizer *p, |
| const char *aInput, |
| int nInput, |
| sqlite3_tokenizer_cursor **pp |
| ){ |
| unicode_cursor *pCsr; |
|
|
| pCsr = (unicode_cursor *)sqlite3_malloc(sizeof(unicode_cursor)); |
| if( pCsr==0 ){ |
| return SQLITE_NOMEM; |
| } |
| memset(pCsr, 0, sizeof(unicode_cursor)); |
|
|
| pCsr->aInput = (const unsigned char *)aInput; |
| if( aInput==0 ){ |
| pCsr->nInput = 0; |
| pCsr->aInput = (const unsigned char*)""; |
| }else if( nInput<0 ){ |
| pCsr->nInput = (int)strlen(aInput); |
| }else{ |
| pCsr->nInput = nInput; |
| } |
|
|
| *pp = &pCsr->base; |
| UNUSED_PARAMETER(p); |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| static int unicodeClose(sqlite3_tokenizer_cursor *pCursor){ |
| unicode_cursor *pCsr = (unicode_cursor *) pCursor; |
| sqlite3_free(pCsr->zToken); |
| sqlite3_free(pCsr); |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| static int unicodeNext( |
| sqlite3_tokenizer_cursor *pC, |
| const char **paToken, |
| int *pnToken, |
| int *piStart, |
| int *piEnd, |
| int *piPos |
| ){ |
| unicode_cursor *pCsr = (unicode_cursor *)pC; |
| unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer); |
| unsigned int iCode = 0; |
| char *zOut; |
| const unsigned char *z = &pCsr->aInput[pCsr->iOff]; |
| const unsigned char *zStart = z; |
| const unsigned char *zEnd; |
| const unsigned char *zTerm = &pCsr->aInput[pCsr->nInput]; |
|
|
| |
| |
| |
| while( z<zTerm ){ |
| READ_UTF8(z, zTerm, iCode); |
| if( unicodeIsAlnum(p, (int)iCode) ) break; |
| zStart = z; |
| } |
| if( zStart>=zTerm ) return SQLITE_DONE; |
|
|
| zOut = pCsr->zToken; |
| do { |
| int iOut; |
|
|
| |
| if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){ |
| char *zNew = sqlite3_realloc64(pCsr->zToken, pCsr->nAlloc+64); |
| if( !zNew ) return SQLITE_NOMEM; |
| zOut = &zNew[zOut - pCsr->zToken]; |
| pCsr->zToken = zNew; |
| pCsr->nAlloc += 64; |
| } |
|
|
| |
| zEnd = z; |
| iOut = sqlite3FtsUnicodeFold((int)iCode, p->eRemoveDiacritic); |
| if( iOut ){ |
| WRITE_UTF8(zOut, iOut); |
| } |
|
|
| |
| if( z>=zTerm ) break; |
| READ_UTF8(z, zTerm, iCode); |
| }while( unicodeIsAlnum(p, (int)iCode) |
| || sqlite3FtsUnicodeIsdiacritic((int)iCode) |
| ); |
|
|
| |
| pCsr->iOff = (int)(z - pCsr->aInput); |
| *paToken = pCsr->zToken; |
| *pnToken = (int)(zOut - pCsr->zToken); |
| *piStart = (int)(zStart - pCsr->aInput); |
| *piEnd = (int)(zEnd - pCsr->aInput); |
| *piPos = pCsr->iToken++; |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){ |
| static const sqlite3_tokenizer_module module = { |
| 0, |
| unicodeCreate, |
| unicodeDestroy, |
| unicodeOpen, |
| unicodeClose, |
| unicodeNext, |
| 0, |
| }; |
| *ppModule = &module; |
| } |
|
|
| #endif |
| #endif |
|
|