mux/src/svdhash.cpp

Go to the documentation of this file.
00001 // svdhash.cpp -- CHashPage, CHashFile, CHashTable modules.
00002 //
00003 // $Id: svdhash.cpp,v 1.45 2007/05/01 03:54:30 sdennis Exp $
00004 //
00005 // MUX 2.4
00006 // Copyright (C) 1998 through 2004 Solid Vertical Domains, Ltd. All
00007 // rights not explicitly given are reserved.
00008 //
00009 #include "copyright.h"
00010 #include "autoconf.h"
00011 #include "config.h"
00012 #include "externs.h"
00013 
00014 #define DO_COMMIT
00015 
00016 int cs_writes   = 0;    // total writes
00017 int cs_reads    = 0;    // total reads
00018 int cs_dels     = 0;    // total deletes
00019 int cs_fails    = 0;    // attempts to grab nonexistent
00020 int cs_syncs    = 0;    // total cache syncs
00021 int cs_dbreads  = 0;    // total read-throughs
00022 int cs_dbwrites = 0;    // total write-throughs
00023 int cs_whits    = 0;    // writes into cached pages
00024 int cs_rhits    = 0;    // read from cached pages
00025 
00026 static const UINT32 CRC32_Table[256] =
00027 {
00028     0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
00029     0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
00030     0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
00031     0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
00032     0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
00033     0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
00034     0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
00035     0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
00036     0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
00037     0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
00038     0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
00039     0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
00040     0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
00041     0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
00042     0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
00043     0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
00044     0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
00045     0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
00046     0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
00047     0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
00048     0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
00049     0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
00050     0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
00051     0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
00052     0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
00053     0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
00054     0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
00055     0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
00056     0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
00057     0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
00058     0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
00059     0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
00060     0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
00061     0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
00062     0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
00063     0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
00064     0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
00065     0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
00066     0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
00067     0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
00068     0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
00069     0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
00070     0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
00071     0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
00072     0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
00073     0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
00074     0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
00075     0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
00076     0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
00077     0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
00078     0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
00079     0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
00080     0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
00081     0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
00082     0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
00083     0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
00084     0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
00085     0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
00086     0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
00087     0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
00088     0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
00089     0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
00090     0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
00091     0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
00092 };
00093 
00094 // Portable CRC-32 routine. These slower routines are less compiler and
00095 // platform dependent and still get the job done.
00096 //
00097 UINT32 CRC32_ProcessBuffer
00098 (
00099     UINT32         ulCrc,
00100     const void    *arg_pBuffer,
00101     unsigned int   nBuffer
00102 )
00103 {
00104     UINT8 *pBuffer = (UINT8 *)arg_pBuffer;
00105 
00106     ulCrc = ~ulCrc;
00107     while (nBuffer--)
00108     {
00109         ulCrc  = CRC32_Table[((UINT8)*pBuffer++) ^ (UINT8)ulCrc] ^ (ulCrc >> 8);
00110     }
00111     return ~ulCrc;
00112 }
00113 
00114 UINT32 CRC32_ProcessInteger(UINT32 nInteger)
00115 {
00116     UINT32 ulCrc;
00117     ulCrc  = ~nInteger;
00118     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00119     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00120     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00121     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00122     return ~ulCrc;
00123 }
00124 
00125 UINT32 CRC32_ProcessInteger2(UINT32 nInteger1, UINT32 nInteger2)
00126 {
00127     UINT32 ulCrc;
00128     ulCrc  = ~nInteger1;
00129     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00130     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00131     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00132     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00133     ulCrc ^= nInteger2;
00134     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00135     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00136     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00137     ulCrc  = CRC32_Table[(UINT8)ulCrc] ^ (ulCrc >> 8);
00138     return ~ulCrc;
00139 }
00140 
00141 #define DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
00142 #define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
00143 #define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
00144 #define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
00145 #define DO16(buf)   DO8(buf,0); DO8(buf,8);
00146 
00147 UINT32 HASH_ProcessBuffer
00148 (
00149     UINT32       ulHash,
00150     const void  *arg_pBuffer,
00151     size_t       nBuffer
00152 )
00153 {
00154     UINT8 *pBuffer = (UINT8 *)arg_pBuffer;
00155     ulHash = ~ulHash;
00156 
00157     if (nBuffer <= 16)
00158     {
00159         pBuffer -= 16 - nBuffer;
00160         switch (nBuffer)
00161         {
00162         case 16: ulHash  = CRC32_Table[pBuffer[0] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00163         case 15: ulHash  = CRC32_Table[pBuffer[1] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00164         case 14: ulHash  = CRC32_Table[pBuffer[2] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00165         case 13: ulHash  = CRC32_Table[pBuffer[3] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00166         case 12: ulHash  = CRC32_Table[pBuffer[4] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00167         case 11: ulHash  = CRC32_Table[pBuffer[5] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00168         case 10: ulHash  = CRC32_Table[pBuffer[6] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00169         case 9:  ulHash  = CRC32_Table[pBuffer[7] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00170 #if defined(UNALIGNED32) && defined(WORDS_LITTLEENDIAN)
00171         case 8:  ulHash ^= *(UINT32 *)(pBuffer + 8);
00172                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00173                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00174                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00175                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00176                  ulHash ^= *(UINT32 *)(pBuffer + 12);
00177                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00178                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00179                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00180                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00181                  return ~ulHash;
00182 #else
00183         case 8:  ulHash  = CRC32_Table[pBuffer[8] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00184 #endif
00185 
00186         case 7:  ulHash  = CRC32_Table[pBuffer[9] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00187         case 6:  ulHash  = CRC32_Table[pBuffer[10] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00188         case 5:  ulHash  = CRC32_Table[pBuffer[11] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00189 #if defined(UNALIGNED32) && defined(WORDS_LITTLEENDIAN)
00190         case 4:  ulHash ^= *(UINT32 *)(pBuffer + 12);
00191                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00192                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00193                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00194                  ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00195                  return ~ulHash;
00196 #else
00197         case 4:  ulHash  = CRC32_Table[pBuffer[12] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00198 #endif
00199 
00200         case 3:  ulHash  = CRC32_Table[pBuffer[13] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00201         case 2:  ulHash  = CRC32_Table[pBuffer[14] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00202         case 1:  ulHash  = CRC32_Table[pBuffer[15] ^ (UINT8)ulHash] ^ (ulHash >> 8);
00203         case 0:  return ~ulHash;
00204         }
00205     }
00206 
00207     size_t nSmall  = nBuffer & 15;
00208     size_t nMedium = (nBuffer >> 4) & 255;
00209     size_t nLarge  = nBuffer >> 12;
00210 
00211     UINT32 s1 = ulHash & 0xFFFF;
00212     UINT32 s2 = (ulHash >> 16) & 0xFFFF;
00213 
00214     while (nLarge--)
00215     {
00216         int k = 256;
00217         while (k)
00218         {
00219             DO16(pBuffer);
00220             pBuffer += 16;
00221             k--;
00222         }
00223         ulHash  = ~s1;
00224         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00225         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00226         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00227         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00228         ulHash ^= s2;
00229         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00230         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00231         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00232         ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00233         ulHash = ~ulHash;
00234         s1 = ulHash & 0xFFFF;
00235         s2 = (ulHash >> 16) & 0xFFFF;
00236     }
00237 
00238     while (nMedium--)
00239     {
00240         DO16(pBuffer);
00241         pBuffer += 16;
00242     }
00243 
00244     pBuffer -= 15 - nSmall;
00245     switch (nSmall)
00246     {
00247     case 15: s1 += pBuffer[0];  s2 += s1;
00248     case 14: s1 += pBuffer[1];  s2 += s1;
00249     case 13: s1 += pBuffer[2];  s2 += s1;
00250     case 12: s1 += pBuffer[3];  s2 += s1;
00251     case 11: s1 += pBuffer[4];  s2 += s1;
00252     case 10: s1 += pBuffer[5];  s2 += s1;
00253     case 9:  s1 += pBuffer[6];  s2 += s1;
00254     case 8:  s1 += pBuffer[7];  s2 += s1;
00255     case 7:  s1 += pBuffer[8];  s2 += s1;
00256     case 6:  s1 += pBuffer[9];  s2 += s1;
00257     case 5:  s1 += pBuffer[10]; s2 += s1;
00258     case 4:  s1 += pBuffer[11]; s2 += s1;
00259     case 3:  s1 += pBuffer[12]; s2 += s1;
00260     case 2:  s1 += pBuffer[13]; s2 += s1;
00261     case 1:  s1 += pBuffer[14]; s2 += s1;
00262     case 0:  break;
00263     }
00264 
00265     ulHash  = ~s1;
00266     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00267     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00268     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00269     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00270     ulHash ^= s2;
00271     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00272     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00273     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00274     ulHash  = CRC32_Table[(UINT8)ulHash] ^ (ulHash >> 8);
00275     return ~ulHash;
00276 }
00277 
00278 #define NUMBER_OF_PRIMES 177
00279 const int Primes[NUMBER_OF_PRIMES] =
00280 {
00281 
00282     1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
00283     71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
00284     157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239,
00285     241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337,
00286     347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
00287     439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
00288     547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
00289     643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743,
00290     751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857,
00291     859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971,
00292     977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 0
00293 };
00294 
00295 static void ChoosePrimes(int TableSize, HP_HEAPOFFSET HashPrimes[16])
00296 {
00297     int LargestPrime = TableSize/2;
00298     if (LargestPrime > Primes[NUMBER_OF_PRIMES-2])
00299     {
00300         LargestPrime = Primes[NUMBER_OF_PRIMES-2];
00301     }
00302     int Spacing = LargestPrime/16;
00303 
00304     // Pick a set primes that are evenly spaced from (0 to LargestPrime)
00305     // We divide this interval into 16 equal sized zones. We want to find
00306     // one prime number that best represents that zone.
00307     //
00308     int iZone, iPrime;
00309     for (iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing)
00310     {
00311         // Search for a prime number that is less than the target zone
00312         // number given by iZone.
00313         //
00314         int Lower = Primes[0];
00315         for (int jPrime = 0; Primes[jPrime] != 0; jPrime++)
00316         {
00317             if (jPrime != 0 && TableSize % Primes[jPrime] == 0) continue;
00318             int Upper = Primes[jPrime];
00319             if (Lower <= iZone && iZone <= Upper)
00320             {
00321                 // Choose the closest lower prime number.
00322                 //
00323                 if (iZone - Lower <= Upper - iZone)
00324                 {
00325                     HashPrimes[iPrime++] = Lower;
00326                 }
00327                 else
00328                 {
00329                     HashPrimes[iPrime++] = Upper;
00330                 }
00331                 break;
00332             }
00333             Lower = Upper;
00334         }
00335     }
00336 
00337     // Alternate negative and positive numbers
00338     //
00339     for (iPrime = 0; iPrime < 16; iPrime += 2)
00340     {
00341         HashPrimes[iPrime] = TableSize-HashPrimes[iPrime];
00342     }
00343 
00344     // Shuffle the set of primes to reduce correlation with bits in
00345     // hash key.
00346     //
00347     for (iPrime = 0; iPrime < 16-1; iPrime++)
00348     {
00349         int Pick = (int)RandomINT32(0, 15-iPrime);
00350         HP_HEAPOFFSET Temp = HashPrimes[Pick];
00351         HashPrimes[Pick] = HashPrimes[15-iPrime];
00352         HashPrimes[15-iPrime] = Temp;
00353     }
00354 }
00355 
00356 static const UINT32 anGroupMask[33] =
00357 {
00358     0x00000000U,
00359     0x80000000U, 0xC0000000U, 0xE0000000U, 0xF0000000U,
00360     0xF8000000U, 0xFC000000U, 0xFE000000U, 0xFF000000U,
00361     0xFF800000U, 0xFFC00000U, 0xFFE00000U, 0xFFF00000U,
00362     0xFFF80000U, 0xFFFC0000U, 0xFFFE0000U, 0xFFFF0000U,
00363     0xFFFF8000U, 0xFFFFC000U, 0xFFFFE000U, 0xFFFFF000U,
00364     0xFFFFF800U, 0xFFFFFC00U, 0xFFFFFE00U, 0xFFFFFF00U,
00365     0xFFFFFF80U, 0xFFFFFFC0U, 0xFFFFFFE0U, 0xFFFFFFF0U,
00366     0xFFFFFFF8U, 0xFFFFFFFCU, 0xFFFFFFFEU, 0xFFFFFFFFU
00367 };
00368 
00369 bool CHashPage::Allocate(unsigned int nPageSize)
00370 {
00371     if (m_nPageSize) return false;
00372 
00373     m_nPageSize = nPageSize;
00374     m_pPage = new unsigned char[nPageSize];
00375     if (m_pPage)
00376     {
00377         return true;
00378     }
00379     return false;
00380 }
00381 
00382 CHashPage::CHashPage(void)
00383 {
00384     m_nPageSize = 0;
00385     m_pPage = 0;
00386 }
00387 
00388 CHashPage::~CHashPage(void)
00389 {
00390     if (m_pPage)
00391     {
00392         delete [] m_pPage;
00393         m_pPage = 0;
00394     }
00395 }
00396 
00397 // GetStats
00398 //
00399 // This functions returns the number of records in this hash page and the
00400 // number of bytes that these records would take up in a fresh page.
00401 //
00402 // It also tries to leave room for a record of size nExtra.
00403 //
00404 // This function is useful for reallocating the page
00405 //
00406 void CHashPage::GetStats
00407 (
00408     HP_HEAPLENGTH nExtra,
00409     int *pnRecords,
00410     HP_HEAPLENGTH *pnAllocatedSize,
00411     int *pnGoodDirSize
00412 )
00413 {
00414     unsigned nSize = 0;
00415     unsigned nCount = 0;
00416     unsigned nGoodDirSize = 100;
00417 
00418     // Count and measure all the records in this page.
00419     //
00420     for (UINT32 iDir = 0; iDir < m_pHeader->m_nDirSize; iDir++)
00421     {
00422         if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
00423         {
00424             nCount++;
00425             HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
00426             HP_HEAPLENGTH nRequired = EXPAND_TO_BOUNDARY(
00427                 HP_SIZEOF_HEAPNODE + pNode->u.s.nRecordSize);
00428 
00429             if (nRequired < HP_MIN_HEAP_ALLOC)
00430             {
00431                 nRequired = HP_MIN_HEAP_ALLOC;
00432             }
00433             nSize += nRequired;
00434         }
00435     }
00436     *pnRecords = nCount;
00437     *pnAllocatedSize = nSize;
00438 
00439     // If we have records to talk about, or even if we are trying to reserve
00440     // space, then do the math.
00441     //
00442     if (  nExtra != 0
00443        || nCount != 0)
00444     {
00445         UINT32 nSpace = (UINT32)( ((unsigned char *)m_pTrailer)
00446                                 - ((unsigned char *)m_pDirectory));
00447         UINT32 nMinDirSize = nCount;
00448         UINT32 nMaxDirSize = (nSpace - nSize)/sizeof(HP_HEAPOFFSET);
00449 
00450         if (nExtra)
00451         {
00452             nExtra += HP_SIZEOF_HEAPNODE;
00453             if (nExtra < HP_MIN_HEAP_ALLOC)
00454             {
00455                 nExtra = HP_MIN_HEAP_ALLOC;
00456             }
00457             nExtra = EXPAND_TO_BOUNDARY(nExtra);
00458             nCount++;
00459             nSize += nExtra;
00460         }
00461 
00462 #define FILL_FACTOR 1
00463         UINT32 nAverageSize = (nSize + nCount/2)/nCount;
00464         UINT32 nHeapGoal = (nSpace * nAverageSize)/(nAverageSize + sizeof(HP_HEAPOFFSET) + FILL_FACTOR);
00465         nGoodDirSize = (UINT32)((nSpace - nHeapGoal + sizeof(HP_HEAPOFFSET)/2)/sizeof(HP_HEAPOFFSET));
00466         if (nGoodDirSize < nMinDirSize)
00467         {
00468             nGoodDirSize = nMinDirSize;
00469         }
00470         else if (nGoodDirSize > nMaxDirSize)
00471         {
00472             nGoodDirSize = nMaxDirSize;
00473         }
00474     }
00475     *pnGoodDirSize = nGoodDirSize;
00476 }
00477 
00478 
00479 void CHashPage::SetFixedPointers(void)
00480 {
00481     m_pHeader = (HP_PHEADER)m_pPage;
00482     m_pDirectory = (HP_PHEAPOFFSET)(m_pHeader+1);
00483     m_pTrailer = (HP_PTRAILER)(m_pPage + m_nPageSize - sizeof(HP_TRAILER));
00484 }
00485 
00486 void CHashPage::Empty(HP_DIRINDEX arg_nDepth, UINT32 arg_nHashGroup, HP_DIRINDEX arg_nDirSize)
00487 {
00488     memset(m_pPage, 0, m_nPageSize);
00489 
00490     SetFixedPointers();
00491 
00492     m_pHeader->m_nDepth = arg_nDepth;
00493     m_pHeader->m_nDirSize = arg_nDirSize;
00494     m_pHeader->m_nHashGroup = arg_nHashGroup;
00495     m_pHeader->m_nTotalInsert = 0;
00496     m_pHeader->m_nDirEmptyLeft = arg_nDirSize;  // Number of entries marked HP_DIR_EMPTY.
00497     if (arg_nDirSize > 0)
00498     {
00499         ChoosePrimes(arg_nDirSize, m_pHeader->m_Primes);
00500         for (UINT32 iDir = 0; iDir < arg_nDirSize; iDir++)
00501         {
00502             m_pDirectory[iDir] = HP_DIR_EMPTY;
00503         }
00504     }
00505     SetVariablePointers();
00506 
00507     // Setup initial free list.
00508     //
00509     HP_PHEAPNODE pNode = (HP_PHEAPNODE)m_pHeapStart;
00510     pNode->nBlockSize = (HP_HEAPLENGTH)(m_pHeapEnd - m_pHeapStart);
00511     pNode->u.oNext = HP_NIL_OFFSET;
00512     m_pHeader->m_oFreeList = 0; // This is intentionally zero (i.e., m_pHeapStart - m_pHeapStart).
00513 }
00514 
00515 #ifdef HP_PROTECTION
00516 void CHashPage::Protection(void)
00517 {
00518     UINT32 ul = HASH_ProcessBuffer(0, m_pPage, m_nPageSize-sizeof(HP_TRAILER));
00519     m_pTrailer->m_checksum = ul;
00520 }
00521 
00522 bool CHashPage::Validate(void)
00523 {
00524     UINT32 ul = HASH_ProcessBuffer(0, m_pPage, m_nPageSize-sizeof(HP_TRAILER));
00525     if (ul != m_pTrailer->m_checksum)
00526     {
00527         return false;
00528     }
00529     return true;
00530 }
00531 
00532 // ValidateBlock.
00533 //
00534 // This function validates a block associated with a particular
00535 // Dir entry and blows that entry away if it's suspect.
00536 //
00537 bool CHashPage::ValidateAllocatedBlock(UINT32 iDir)
00538 {
00539     if (iDir >= m_pHeader->m_nDirSize) return false;
00540     if (m_pDirectory[iDir] >= HP_DIR_DELETED)
00541         return false;
00542 
00543     // Use directory entry to go find heap node. The record itself follows.
00544     //
00545     unsigned char *pBlockStart = m_pHeapStart + m_pDirectory[iDir];
00546     unsigned char *pBlockEnd = pBlockStart + HP_MIN_HEAP_ALLOC;
00547     if (pBlockStart < m_pHeapStart || m_pHeapEnd <= pBlockEnd)
00548     {
00549         // Wow. We have a problem here. There is no good way of
00550         // finding this record anymore, so just mark it as
00551         // deleted. A sweep of the heap will reclaim any lost
00552         // free space.
00553         //
00554         m_pDirectory[iDir] = HP_DIR_DELETED;
00555     }
00556     else
00557     {
00558         HP_PHEAPNODE pNode = (HP_PHEAPNODE)pBlockStart;
00559         pBlockEnd = pBlockStart + pNode->nBlockSize;
00560         if (m_pHeapEnd < pBlockEnd || pNode->u.s.nRecordSize > pNode->nBlockSize)
00561         {
00562             // Wow. Record hangs off the end of the heap space, or the record
00563             // is larger than the block that holds it.
00564             //
00565             m_pDirectory[iDir] = HP_DIR_DELETED;
00566         }
00567         else
00568         {
00569             return true;
00570         }
00571     }
00572     return false;
00573 }
00574 
00575 bool CHashPage::ValidateFreeBlock(HP_HEAPOFFSET oBlock)
00576 {
00577     // If the free list is empty, then this can't be a valid free block.
00578     //
00579     if (m_pHeader->m_oFreeList == HP_NIL_OFFSET)
00580     {
00581         return false;
00582     }
00583 
00584     // Go find heap node. The record itself follows.
00585     //
00586     unsigned char *pBlockStart = m_pHeapStart + oBlock;
00587     unsigned char *pBlockEnd = pBlockStart + HP_MIN_HEAP_ALLOC;
00588     if (pBlockStart < m_pHeapStart || m_pHeapEnd < pBlockEnd)
00589     {
00590         // Wow. We have a problem here. There is no good way of
00591         // finding this record anymore, so just empty the free list
00592         // and hope to either rehash the page into a new page, or
00593         // sweep the heap and re-establish the free list.
00594         //
00595         m_pHeader->m_oFreeList = HP_NIL_OFFSET;
00596     }
00597     else
00598     {
00599         HP_PHEAPNODE pNode = (HP_PHEAPNODE)pBlockStart;
00600         pBlockEnd = pBlockStart + pNode->nBlockSize;
00601         if (m_pHeapEnd < pBlockEnd)
00602         {
00603             // Wow. Record hangs off the end of the heap space.
00604             //
00605             m_pHeader->m_oFreeList = HP_NIL_OFFSET;
00606         }
00607         else
00608         {
00609             return true;
00610         }
00611     }
00612     return false;
00613 }
00614 
00615 // ValidateFreeList - Checks the validity of the free list
00616 //
00617 bool CHashPage::ValidateFreeList(void)
00618 {
00619     HP_HEAPOFFSET oCurrent = m_pHeader->m_oFreeList;
00620     while (oCurrent != HP_NIL_OFFSET)
00621     {
00622         if (ValidateFreeBlock(oCurrent))
00623         {
00624             HP_PHEAPNODE pCurrent = (HP_PHEAPNODE)(m_pHeapStart + oCurrent);
00625             if (oCurrent >= pCurrent->u.oNext)
00626             {
00627                 Log.WriteString("CHashPage::ValidateFreeList - Free list is corrupt." ENDLINE);
00628                 m_pHeader->m_oFreeList = HP_NIL_OFFSET;
00629                 return false;
00630             }
00631             oCurrent = pCurrent->u.oNext;
00632         }
00633         else
00634         {
00635             Log.WriteString("CHashPage::ValidateFreeList - Free list is corrupt." ENDLINE);
00636             m_pHeader->m_oFreeList = HP_NIL_OFFSET;
00637             return false;
00638         }
00639     }
00640     return true;
00641 }
00642 #endif // HP_PROTECTION
00643 
00644 // Insert - Inserts a new record if there is room.
00645 //
00646 int CHashPage::Insert(HP_HEAPLENGTH nRecord, UINT32 nHash, void *pRecord)
00647 {
00648     int ret = HP_INSERT_SUCCESS;
00649     m_pHeader->m_nTotalInsert++;
00650     for (int nTries = 0; nTries < 2; nTries++)
00651     {
00652 #ifdef HP_PROTECTION
00653         // First, is this page dealing with keys like this at all?
00654         //
00655         HP_DIRINDEX nDepth = m_pHeader->m_nDepth;
00656         if ((nHash & anGroupMask[nDepth]) != m_pHeader->m_nHashGroup)
00657         {
00658             Log.WriteString("CHashPage::Insert - Inserting into the wrong page." ENDLINE);
00659             return HP_INSERT_ERROR_ILLEGAL;
00660         }
00661 #endif // HP_PROTECTION
00662 
00663         // Where do we begin our first probe?
00664         //
00665         int di   = m_pHeader->m_Primes[nHash & 15];
00666         int iDir = (nHash >> 4) % (m_pHeader->m_nDirSize);
00667         m_nProbesLeft = m_pHeader->m_nDirSize;
00668         while (m_nProbesLeft-- && (m_pDirectory[iDir] < HP_DIR_DELETED))
00669         {
00670             iDir += di;
00671             if (iDir >= (m_pHeader->m_nDirSize))
00672             {
00673                 iDir -= (m_pHeader->m_nDirSize);
00674             }
00675         }
00676         if (m_nProbesLeft >= 0)
00677         {
00678             if (m_pHeader->m_nDirEmptyLeft < m_nDirEmptyTrigger)
00679             {
00680                 if (!Defrag(nRecord))
00681                 {
00682                     return HP_INSERT_ERROR_FULL;
00683                 }
00684                 ret = HP_INSERT_SUCCESS_DEFRAG;
00685                 continue;
00686             }
00687             if (HeapAlloc(iDir, nRecord, nHash, pRecord))
00688             {
00689                 return ret;
00690             }
00691         }
00692         if (!Defrag(nRecord))
00693         {
00694             return HP_INSERT_ERROR_FULL;
00695         }
00696         ret = HP_INSERT_SUCCESS_DEFRAG;
00697     }
00698     return HP_INSERT_ERROR_FULL;
00699 }
00700 
00701 // Find - Finds the first record with the given hash key and returns its
00702 //        directory index or HP_DIR_EMPTY if no hash keys are found.
00703 //
00704 //        Call iDir = FindFirstKey(hash) the first time, and then call
00705 //        iDir = FindNextKey(iDir, hash) every time after than until
00706 //        iDir == HP_DIR_EMPTY to interate through all the records with the
00707 //        desired hash key.
00708 //
00709 HP_DIRINDEX CHashPage::FindFirstKey(UINT32 nHash, unsigned int *numchecks)
00710 {
00711 #ifdef HP_PROTECTION
00712     // First, is this page dealing with keys like this at all?
00713     //
00714     HP_DIRINDEX nDepth = m_pHeader->m_nDepth;
00715     if ((nHash & anGroupMask[nDepth]) != m_pHeader->m_nHashGroup)
00716         return HP_DIR_EMPTY;
00717 #endif // HP_PROTECTION
00718 
00719     const int nDirSize = m_pHeader->m_nDirSize;
00720 
00721     // Where do we begin our first probe?
00722     //
00723     int iDir = (nHash >> 4) % nDirSize;
00724     int sOffset = m_pDirectory[iDir];
00725     if ((unsigned int)sOffset < HP_DIR_DELETED)
00726     {
00727         HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + sOffset);
00728         if (pNode->u.s.nHash == nHash)
00729         {
00730             m_nProbesLeft = nDirSize - 1;
00731             *numchecks = 1;
00732             return iDir;
00733         }
00734     }
00735     else if (HP_DIR_EMPTY == sOffset)
00736     {
00737         m_nProbesLeft = nDirSize;
00738         *numchecks = 0;
00739         return HP_DIR_EMPTY;
00740     }
00741 
00742     //    HP_DIR_DELETED == sOffset
00743     // || pNode->u.s.nHash != nHash
00744 
00745     m_nProbesLeft = nDirSize - 1;
00746     int di = m_pHeader->m_Primes[nHash & 15];
00747 
00748     iDir += di;
00749     if (iDir >= nDirSize)
00750     {
00751         iDir -= nDirSize;
00752     }
00753     sOffset = m_pDirectory[iDir];
00754 
00755     while (sOffset != HP_DIR_EMPTY)
00756     {
00757         m_nProbesLeft--;
00758         if (sOffset != HP_DIR_DELETED)
00759         {
00760             HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + sOffset);
00761             if (pNode->u.s.nHash == nHash)
00762             {
00763                 *numchecks = nDirSize - m_nProbesLeft;
00764                 return iDir;
00765             }
00766         }
00767 
00768         if (!m_nProbesLeft) break;
00769 
00770         iDir += di;
00771         if (iDir >= nDirSize)
00772         {
00773             iDir -= nDirSize;
00774         }
00775         sOffset = m_pDirectory[iDir];
00776     }
00777     *numchecks = nDirSize - m_nProbesLeft;
00778     return HP_DIR_EMPTY;
00779 }
00780 
00781 // Find - Finds the next record with the given hash key and returns its
00782 //        directory index or HP_DIR_EMPTY if no hash keys are found.
00783 //
00784 //
00785 HP_DIRINDEX CHashPage::FindNextKey(HP_DIRINDEX iDir, UINT32 nHash, unsigned int *numchecks)
00786 {
00787     *numchecks = 0;
00788 
00789 #ifdef HP_PROTECTION
00790     // First, is this page dealing with keys like this at all?
00791     //
00792     HP_DIRINDEX nDepth = m_pHeader->m_nDepth;
00793     if ((nHash & anGroupMask[nDepth]) != m_pHeader->m_nHashGroup)
00794         return HP_DIR_EMPTY;
00795 #endif // HP_PROTECTION
00796 
00797     int nDirSize = m_pHeader->m_nDirSize;
00798 
00799     // Where do we begin our first probe? If this is the first call, i will be HP_DIR_EMPTY.
00800     // On calls after that, it will be what we returned on the previous call.
00801     //
00802     int di = m_pHeader->m_Primes[nHash & 15];
00803     iDir += di;
00804     if (iDir >= nDirSize)
00805     {
00806         iDir -= nDirSize;
00807     }
00808     while (m_nProbesLeft && (m_pDirectory[iDir] != HP_DIR_EMPTY))
00809     {
00810         m_nProbesLeft--;
00811         (*numchecks)++;
00812         if (m_pDirectory[iDir] != HP_DIR_DELETED)
00813         {
00814             if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
00815             {
00816                 HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
00817                 if (pNode->u.s.nHash == nHash) return iDir;
00818             }
00819         }
00820         iDir += di;
00821         if (iDir >= nDirSize)
00822         {
00823             iDir -= nDirSize;
00824         }
00825     }
00826     return HP_DIR_EMPTY;
00827 }
00828 
00829 // HeapAlloc - Return true if there was enough room to copy the record into the heap, otherwise,
00830 //             it returns false.
00831 //
00832 bool CHashPage::HeapAlloc(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, UINT32 nHash, void *pRecord)
00833 {
00834     //ValidateFreeList();
00835     if (m_pDirectory[iDir] < HP_DIR_DELETED)
00836     {
00837         return false;
00838     }
00839 
00840     // How much space do we need?
00841     //
00842     HP_HEAPLENGTH nRequired = EXPAND_TO_BOUNDARY(HP_SIZEOF_HEAPNODE + nRecord);
00843     if (nRequired < HP_MIN_HEAP_ALLOC)
00844     {
00845         nRequired = HP_MIN_HEAP_ALLOC;
00846     }
00847 
00848     // Search through the free list for something of the right size.
00849     //
00850     HP_HEAPOFFSET oNext = m_pHeader->m_oFreeList;
00851     HP_PHEAPOFFSET poPrev = &(m_pHeader->m_oFreeList);
00852     while (oNext != HP_NIL_OFFSET)
00853     {
00854 #if 0
00855         if (!ValidateFreeBlock(oPrevious))
00856         {
00857             ValidateFreeList();
00858             return false;
00859         }
00860 #endif // 0
00861         unsigned char *pBlockStart = m_pHeapStart + oNext;
00862         HP_PHEAPNODE pNode = (HP_PHEAPNODE)pBlockStart;
00863         if (pNode->nBlockSize >= nRequired)
00864         {
00865             // We found something of the correct size.
00866             //
00867             // Do we cut it into two blocks or take the whole thing?
00868             //
00869             HP_HEAPLENGTH nNewBlockSize = pNode->nBlockSize - nRequired;
00870             if (nNewBlockSize >= EXPAND_TO_BOUNDARY(HP_MIN_HEAP_ALLOC+1))
00871             {
00872                 // There is enough for leftovers, split it.
00873                 //
00874                 HP_PHEAPNODE pNewNode = (HP_PHEAPNODE)(pBlockStart + nRequired);
00875                 pNewNode->nBlockSize = nNewBlockSize;
00876                 pNewNode->u.oNext = pNode->u.oNext;
00877 
00878                 // Update current node.
00879                 //
00880                 pNode->nBlockSize = nRequired;
00881                 pNode->u.s.nHash = nHash;
00882                 pNode->u.s.nRecordSize = nRecord;
00883 
00884                 // Update Free list pointer.
00885                 //
00886                 *poPrev += nRequired;
00887             }
00888             else
00889             {
00890                 // Take the whole thing.
00891                 //
00892                 *poPrev = pNode->u.oNext;
00893                 pNode->u.s.nHash = nHash;
00894                 pNode->u.s.nRecordSize = nRecord;
00895             }
00896             memcpy(pNode+1, pRecord, nRecord);
00897             if (m_pDirectory[iDir] == HP_DIR_EMPTY)
00898             {
00899                 m_pHeader->m_nDirEmptyLeft--;
00900             }
00901             m_pDirectory[iDir] = (HP_HEAPOFFSET)(pBlockStart - m_pHeapStart);
00902             return true;
00903         }
00904         poPrev = &(pNode->u.oNext);
00905         oNext = pNode->u.oNext;
00906     }
00907     return false;
00908 }
00909 
00910 // HeapFree - Returns to the heap the space for the record associated with iDir. It
00911 //            always succeeds even if there wasn't a record there to delete.
00912 //
00913 void CHashPage::HeapFree(HP_DIRINDEX iDir)
00914 {
00915     //ValidateFreeList();
00916     if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
00917     {
00918         HP_HEAPOFFSET oBlock = m_pDirectory[iDir];
00919         HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + oBlock);
00920 
00921         // Clear it. The reason for clearing is that it makes debugging easier,
00922         // and also, if the file is compressed by the file system, a string
00923         // of zeros will yield a smaller result.
00924         //
00925         HP_HEAPLENGTH nBlockSize = pNode->nBlockSize;
00926         memset(pNode, 0, nBlockSize);
00927         pNode->nBlockSize = nBlockSize;
00928 
00929         // Push it onto the free list.
00930         //
00931         pNode->u.oNext = m_pHeader->m_oFreeList;
00932         m_pHeader->m_oFreeList = oBlock;
00933         m_pDirectory[iDir] = HP_DIR_DELETED;
00934     }
00935 }
00936 
00937 void CHashPage::HeapCopy(HP_DIRINDEX iDir, HP_PHEAPLENGTH pnRecord, void *pRecord)
00938 {
00939     if (pnRecord == 0 || pRecord == 0) return;
00940 
00941     if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
00942     {
00943         HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
00944 
00945         // Copy the record.
00946         //
00947         *pnRecord = pNode->u.s.nRecordSize;
00948         memcpy(pRecord, pNode+1, pNode->u.s.nRecordSize);
00949     }
00950 }
00951 
00952 void CHashPage::HeapUpdate(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, void *pRecord)
00953 {
00954     if (nRecord == 0 || pRecord == 0) return;
00955 
00956     if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
00957     {
00958         HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
00959 
00960         if (pNode->u.s.nRecordSize != nRecord) return;
00961         memcpy(pNode+1, pRecord, nRecord);
00962     }
00963 }
00964 
00965 bool CHashPage::Split(CHashPage &hp0, CHashPage &hp1)
00966 {
00967     // Figure out what a good directory size is given the actual records in this page.
00968     //
00969     int   nRecords;
00970     HP_HEAPLENGTH nAllocatedSize;
00971     int   nGoodDirSize;
00972     GetStats(0, &nRecords, &nAllocatedSize, &nGoodDirSize);
00973     if (nRecords == 0)
00974     {
00975         Log.WriteString("Why are we splitting a page with no records in it?" ENDLINE);
00976         return false;
00977     }
00978 
00979     // Initialize that type of HashPage and copy records over.
00980     //
00981     int   nNewDepth = m_pHeader->m_nDepth + 1;
00982     UINT32 nBitMask = 1 << (32-nNewDepth);
00983     UINT32 nHashGroup0 = m_pHeader->m_nHashGroup & (~nBitMask);
00984     UINT32 nHashGroup1 = nHashGroup0 | nBitMask;
00985     hp0.Empty(nNewDepth, nHashGroup0, nGoodDirSize);
00986     hp1.Empty(nNewDepth, nHashGroup1, nGoodDirSize);
00987     for (int iDir = 0; iDir < m_pHeader->m_nDirSize; iDir++)
00988     {
00989         if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
00990         {
00991             HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
00992             UINT32 nHash = pNode->u.s.nHash;
00993             if ((nHash & anGroupMask[nNewDepth]) == (nHashGroup0 & anGroupMask[nNewDepth]))
00994             {
00995                 if (!IS_HP_SUCCESS(hp0.Insert(pNode->u.s.nRecordSize, nHash, pNode+1)))
00996                 {
00997                     Log.WriteString("CHashPage::Split - Ran out of room." ENDLINE);
00998                     return false;
00999                 }
01000             }
01001             else if ((nHash & anGroupMask[nNewDepth]) == (nHashGroup1 & anGroupMask[nNewDepth]))
01002             {
01003                 if (!IS_HP_SUCCESS(hp1.Insert(pNode->u.s.nRecordSize, nHash, pNode+1)))
01004                 {
01005                     Log.WriteString("CHashPage::Split - Ran out of room." ENDLINE);
01006                     return false;
01007                 }
01008             }
01009             else
01010             {
01011                 Log.WriteString("CHashPage::Split - This record fits in neither page...lost." ENDLINE);
01012                 return false;
01013             }
01014         }
01015     }
01016 #if 0
01017     int nRecords0, nRecords1;
01018     HP_HEAPLENGTH nAllocatedSize0, nAllocatedSize1;
01019     int    temp;
01020     hp0.GetStats(0, &nRecords0, &nAllocatedSize0, &temp);
01021     hp1.GetStats(0, &nRecords1, &nAllocatedSize1, &temp);
01022     Log.tinyprintf("Split (%d %d) page into (%d %d) and (%d %d)" ENDLINE,
01023         nRecords, nAllocatedSize, nRecords0, nAllocatedSize0, nRecords1,
01024         nAllocatedSize1);
01025     if (nRecords0 + nRecords1 != nRecords)
01026     {
01027         Log.WriteString("Lost something" ENDLINE);
01028         return false;
01029     }
01030 #endif // 0
01031     return true;
01032 }
01033 
01034 void CHashPage::GetRange
01035 (
01036     UINT32 arg_nDirDepth,
01037     UINT32 &nStart,
01038     UINT32 &nEnd
01039 )
01040 {
01041     UINT32 nBase = 0;
01042     int nShift = 32 - arg_nDirDepth;
01043     if (arg_nDirDepth > 0)
01044     {
01045         nBase = m_pHeader->m_nHashGroup >> nShift;
01046     }
01047     UINT32 ulMask = anGroupMask[nShift + m_pHeader->m_nDepth];
01048     nStart = nBase & ulMask;
01049     nEnd   = nBase | ~ulMask;
01050 }
01051 
01052 #ifdef WIN32
01053 bool CHashPage::WritePage(HANDLE hFile, HF_FILEOFFSET oWhere)
01054 {
01055     cs_dbwrites++;
01056     for ( ; ; MuxAlarm.Sleep(time_250ms))
01057     {
01058         if (SetFilePointer(hFile, oWhere, 0, FILE_BEGIN) == 0xFFFFFFFFUL)
01059         {
01060             Log.tinyprintf("CHashPage::Write - SetFilePointer error %u." ENDLINE, GetLastError());
01061             continue;
01062         }
01063         DWORD nWritten;
01064         if (!WriteFile(hFile, m_pPage, m_nPageSize, &nWritten, 0) || nWritten != m_nPageSize)
01065         {
01066