sqlite的分词器模块需要对输入的字符串映射为系统中的标示符,其对关键字映射使用了hash算法,其对hash冲突的解决十分巧妙。

1:最常规的解决办法:写一堆判断对每个输入字符串判断是否匹配,如果匹配就映射为系统中的关键字。由于sqlite的关键字有100多个,如果每个字符串进行比较判断,无疑效率很低

2:使用hash算法:

     首先构造一个散列函数,该函数计算字符串得到一个hash映射表的入口地址。

     在入口地址中存放对应的记录.

     由于对不同的字符串进行散列后可能得到一个相同的入口地址,这就要求我们要对冲突进行解决.

2.1以前遇到类似的问题,基本使用了链地址法来解决冲突的问题。例如:

typedef struct map_s map_t;

struct map_s

{

    unsigned char *pStr;

    unsigned char token;

}

unsigned char getTken(unsigned char *pStr, int len)

{

    const unsigned char map[M][N] = {
                                    {{"token11",TK_TOKEN11},...,{"token1n",TK_TOKEN1N}},

                                    ....

                                    {{"tokenm1",TK_TOKENM1},...,{NULL,TK_ERR}},

                                    };

    hashCode = getHashCode(pStr);

    for(int i=0;i<N;i++)

    {

            if(map[hashCode][i].pStr equals pStr)

                 return map[hashCode][i].token

    }

    return TK_ERR;
}

 

该方法简单容易理解,如果散列函数较好,基本可以寻址复杂度为O(1),但该方法对于没有被映射的位置存在一些空间的浪费。

2.2   sqlite使用了一个字符串缓冲区来包含所有的可能关键字,该缓冲区是被压缩过的。

static const char zText[544] =
     "ABORTABLEFTEMPORARYADDATABASELECTHEND"
      ....
     "UNIQUEUSINGVACUUMVALUESVIEWHEREVIRTUAL";

关键字"abort"和"table"公用“ABORTABLE”缓冲区间, 对于每个关键字都存在一个该缓冲区的入口位置,以该入口位置进行字符串比较,判断是否冲突。

static const unsigned short int aOffset[117] = {
     0,   4,   7,  10,  10,  14,  19,  21,  26,  27,  32,  34,  36,
     ....
     476, 481, 486, 494, 496, 500, 505, 511, 516, 522, 528
    };

hash映射表保存了每个计算的hash值的可能入口位置,如果该入口位置的缓冲区字符串和待转换字符串等同,表明没有冲突,直接获取对应的aCode映射值。

static const unsigned char aHash[127] = {
    92,  80, 107,  91,   0,   4,   0,   0, 114,   0,  83,   0,   0,
    ....
    15,   0, 116,  51,  56,   0,   2,  55,   0, 111,
    };    static const unsigned char aCode[117] = {
    TK_ABORT,      TK_TABLE,      TK_JOIN_KW,    TK_TEMP,        
    ....
    TK_WHERE,      TK_VIRTUAL,    
    };

如果出现冲突,使用了“再散列法”来解决,对冲突的地址在aNext重新分配一个没有使用过的入口位置,该入口位置的缓冲区字符串和待转换字符串等同。

static const unsigned char aNext[117] = {
    0,   0,   0,   0,   0,   3,   0,   0,   0,   0,   0,   0,   0,
    ....
    0,  14,  27,  78,   0,  57,  89,   0,  35,   0,  62,   0,
    };

该hash算法设计的比较巧妙,使用了一个压缩的字符串缓冲区结合“再散列法”完美的解决了hash冲突问题。在空间和查找效率上都很优秀。