title: how to improve the code (Delphi) for loading and searching in a dictionary?
body: <p>I'm a Delphi programmer.
  I have made a program who uses dictionaries with words and expressions (loaded in program as "array of string").
  It uses a search algorithm based on their "checksum" (I hope this is the correct word).
  A string is transformed in integer based on this:</p>
  
  <pre><code>var
     FHashSize: Integer; //stores the value of GetHashSize
     HashTable, HashTableNoCase: array[Byte] of Longword;
     HashTableInit: Boolean = False;
  
  const
     AnsiLowCaseLookup: array[AnsiChar] of AnsiChar = (
        #$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07,
        #$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
        #$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17,
        #$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F,
        #$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27,
        #$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
        #$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37,
        #$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
        #$40, #$61, #$62, #$63, #$64, #$65, #$66, #$67,
        #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
        #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77,
        #$78, #$79, #$7A, #$5B, #$5C, #$5D, #$5E, #$5F,
        #$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67,
        #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
        #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77,
        #$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F,
        #$80, #$81, #$82, #$83, #$84, #$85, #$86, #$87,
        #$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F,
        #$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97,
        #$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F,
        #$A0, #$A1, #$A2, #$A3, #$A4, #$A5, #$A6, #$A7,
        #$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF,
        #$B0, #$B1, #$B2, #$B3, #$B4, #$B5, #$B6, #$B7,
        #$B8, #$B9, #$BA, #$BB, #$BC, #$BD, #$BE, #$BF,
        #$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7,
        #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
        #$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7,
        #$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF,
        #$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7,
        #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
        #$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7,
        #$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF);
  
  implementation
  
  function GetHashSize(const Count: Integer): Integer;
  begin
     if Count &lt; 65 then
        Result := 256
     else
        Result := Round(IntPower(16, Ceil(Log10(Count div 4) / Log10(16))));
  end;
  
  function Hash(const Hash: LongWord; const Buf; const BufSize: Integer): LongWord;
  var P: PByte;
     I: Integer;
  begin
     P := @Buf;
     Result := Hash;
     for I := 1 to BufSize do
     begin
        Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
        Inc(P);
     end;
  end;
  
  function HashStrBuf(const StrBuf: Pointer; const StrLength: Integer; const Slots: LongWord): LongWord;
  var P: PChar;
     I, J: Integer;
  begin
     if not HashTableInit then
        InitHashTable;
     P := StrBuf;
     if StrLength &lt;= 48 then // Hash all characters for short strings
        Result := Hash($FFFFFFFF, P^, StrLength)
     else
     begin
        // Hash first 16 bytes
        Result := Hash($FFFFFFFF, P^, 16);
        // Hash last 16 bytes
        Inc(P, StrLength - 16);
        Result := Hash(Result, P^, 16);
        // Hash 16 bytes sampled from rest of string
        I := (StrLength - 48) div 16;
        P := StrBuf;
        Inc(P, 16);
        for J := 1 to 16 do
        begin
           Result := HashTable[Byte(Result) xor Byte(P^)] xor (Result shr 8);
           Inc(P, I + 1);
        end;
     end;
    // Mod into slots
     if Slots &lt;&gt; 0 then
        Result := Result mod Slots;
  end;
  
  procedure InitHashTable;
  var I, J: Byte;
     R: LongWord;
  begin
     for I := $00 to $FF do
     begin
        R := I;
        for J := 8 downto 1 do
           if R and 1 &lt;&gt; 0 then
              R := (R shr 1) xor $EDB88320
           else
              R := R shr 1;
        HashTable[I] := R;
     end;
     Move(HashTable, HashTableNoCase, Sizeof(HashTable));
     for I := Ord('A') to Ord('Z') do
        HashTableNoCase[I] := HashTableNoCase[I or 32];
     HashTableInit := True;
  end;
  </code></pre>
  
  <p>The result of the HashStrBuf is "and (FHashSize - 1)" and is used as index in an "array of array of Integer" (of FHashSize size) to store the index of the string from that "array of string".
  This way, when searches for a string, it's transformed in "checksum" and then the code searches in the "branch" with this index comparing this string with the strings from dictionary who have the same "checksum".</p>
  
  <p>Ideally each string from dictionary should have unique checksum. But in the "real world" about 2/3 share the same "checksum" with other words. Because of that the search is not that fast.
  In these dictionaries strings are composed of this characters: ['a'..'z',#224..#246,#248..#254,#154,#156..#159,#179,#186,#191,#190,#185,'0'..'9', '''']
  Is there any way to improve the "hashing" so the strings would have more unique "checksums"?
  Oh, one way is to increase the size of that "array of array of Integer"  (FHashSize) but it cannot be increased too much because it takes a lot of Ram.</p>
  
  <p>Another thing: these dictionaries are stored on HDD only as words/expressions (not the "checksums"). Their "checksum" is generated at program startup. But it takes a lot of seconds to do that...
  Is there any way to speed up the startup of the program? Maybe by improving the "hashing" function, maybe by storing the "checksums" on HDD and loading them from there...</p>
  
  <p>Any input would be appreciated...</p>
  
  <p>PS: here is the code to search:</p>
  
  <pre><code>function TDictionary.LocateKey(const Key: AnsiString): Integer;
  var i, j, l, H: Integer;
     P, Q: PChar;
  begin
     Result := -1;
     l := Length(Key);
     H := HashStrBuf(@Key[1], l, 0) and (FHashSize - 1);
     P := @Key[1];
     for i := 0 to High(FHash[H]) do  //FHash is that "array of array of integer"
     begin
        if l &lt;&gt; FKeys.ItemSize[FHash[H][i]] then //FKeys.ItemSize is an byte array with the lengths of strings from dictionary
           Continue;
        Q := FKeys.Pointer(FHash[H][i]); //pointer to string in dictionary
        for j := 0 to l - 1 do
           if (P + j)^ &lt;&gt; (Q + j)^ then
              Break;
        if j = l then
        begin
           Result := FHash[H][i];
           Exit;
        end;
     end;
  end;
  </code></pre>