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             UINT32 cc = GetLastError();
01067             if (cc != ERROR_LOCK_VIOLATION)
01068             {
01069                 Log.tinyprintf("CHashPage::Write - WriteFile error %u." ENDLINE, cc);
01070             }
01071             continue;
01072         }
01073         return true;
01074     }
01075 }
01076 
01077 bool CHashPage::ReadPage(HANDLE hFile, HF_FILEOFFSET oWhere)
01078 {
01079     cs_dbreads++;
01080     SetFixedPointers();
01081     for ( ; ; MuxAlarm.Sleep(time_250ms))
01082     {
01083         if (SetFilePointer(hFile, oWhere, 0, FILE_BEGIN) == 0xFFFFFFFFUL)
01084         {
01085             Log.tinyprintf("CHashPage::Read - SetFilePointer error %u." ENDLINE, GetLastError());
01086             continue;
01087         }
01088         DWORD nRead;
01089         if (!ReadFile(hFile, m_pPage, m_nPageSize, &nRead, 0) || nRead != m_nPageSize)
01090         {
01091             UINT32 cc = GetLastError();
01092             if (cc != ERROR_LOCK_VIOLATION)
01093             {
01094                 Log.tinyprintf("CHashPage::Read - ReadFile error %u." ENDLINE, cc);
01095             }
01096             continue;
01097         }
01098         SetVariablePointers();
01099         return true;
01100     }
01101 }
01102 
01103 #else // WIN32
01104 bool CHashPage::WritePage(HANDLE hFile, HF_FILEOFFSET oWhere)
01105 {
01106     cs_dbwrites++;
01107     int cnt = 60;
01108     for ( ; cnt; MuxAlarm.Sleep(time_1s), cnt--)
01109     {
01110 #ifdef HAVE_PWRITE
01111         int cc = pwrite(hFile, m_pPage, m_nPageSize, oWhere);
01112 #else
01113         if (lseek(hFile, oWhere, SEEK_SET) == (off_t)-1)
01114         {
01115             Log.tinyprintf("CHashPage::Write - lseek error %u." ENDLINE, errno);
01116             continue;
01117         }
01118         int cc = write(hFile, m_pPage, m_nPageSize);
01119 #endif // HAVE_PWRITE
01120         if ((int)m_nPageSize != cc)
01121         {
01122             if (cc == -1)
01123             {
01124                 Log.tinyprintf("CHashPage::Write - write error %u." ENDLINE, errno);
01125             }
01126             else
01127             {
01128                 // Our write request was only partially filled. The disk is
01129                 // probably full.
01130                 //
01131                 Log.tinyprintf("CHashPage::Write - partial write." ENDLINE);
01132             }
01133         }
01134         return true;
01135     }
01136 
01137     // Don't struggle further.  You'll just make it worse.
01138     //
01139     mudstate.shutdown_flag = true;
01140     return false;
01141 }
01142 
01143 bool CHashPage::ReadPage(HANDLE hFile, HF_FILEOFFSET oWhere)
01144 {
01145     cs_dbreads++;
01146     SetFixedPointers();
01147     int cnt = 60;
01148     for ( ; cnt; MuxAlarm.Sleep(time_1s), cnt--)
01149     {
01150 #ifdef HAVE_PREAD
01151         int cc = pread(hFile, m_pPage, m_nPageSize, oWhere);
01152 #else
01153         if (lseek(hFile, oWhere, SEEK_SET) == (off_t)-1)
01154         {
01155             Log.tinyprintf("CHashPage::Read - lseek error %u." ENDLINE, errno);
01156             continue;
01157         }
01158         int cc = read(hFile, m_pPage, m_nPageSize);
01159 #endif // HAVE_PREAD
01160         if ((int)m_nPageSize != cc)
01161         {
01162             if (cc == -1)
01163             {
01164                 Log.tinyprintf("CHashPage::Read - read error %u." ENDLINE, errno);
01165             }
01166             else
01167             {
01168                 // Our read request was only partially filled. Surrender.
01169                 //
01170                 Log.tinyprintf("CHashPage::Read - partial read." ENDLINE);
01171             }
01172             continue;
01173         }
01174         SetVariablePointers();
01175         return true;
01176     }
01177 
01178     // Don't struggle further.  You'll just make it worse.
01179     //
01180     mudstate.shutdown_flag = true;
01181     return false;
01182 }
01183 #endif // WIN32
01184 
01185 HP_DIRINDEX CHashPage::GetDepth(void)
01186 {
01187     return m_pHeader->m_nDepth;
01188 }
01189 
01190 // Defrag
01191 //
01192 // Moves all the records together, and re-establishes a single-element free list at the end.
01193 //
01194 bool CHashPage::Defrag(HP_HEAPLENGTH nExtra)
01195 {
01196     CHashPage *hpNew = new CHashPage;
01197     if (!hpNew) return false;
01198     if (!hpNew->Allocate(m_nPageSize))
01199     {
01200         delete hpNew;
01201         return false;
01202     }
01203 
01204     // Figure out what a good directory size is given the actual records in this page.
01205     //
01206     int   nRecords;
01207     HP_HEAPLENGTH nAllocatedSize;
01208     int   nGoodDirSize;
01209     GetStats(nExtra, &nRecords, &nAllocatedSize, &nGoodDirSize);
01210 
01211     // Initialize that type of HashPage and copy records over.
01212     //
01213     hpNew->Empty(m_pHeader->m_nDepth, m_pHeader->m_nHashGroup, nGoodDirSize);
01214     int errInserted = HP_INSERT_SUCCESS;
01215     for (int iDir = 0; iDir < m_pHeader->m_nDirSize && IS_HP_SUCCESS(errInserted); iDir++)
01216     {
01217         if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
01218         {
01219             HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
01220             errInserted = hpNew->Insert(pNode->u.s.nRecordSize, pNode->u.s.nHash, pNode+1);
01221         }
01222     }
01223     if (IS_HP_SUCCESS(errInserted))
01224     {
01225         // Swap buffers.
01226         //
01227         unsigned char *tmp;
01228         tmp = hpNew->m_pPage;
01229         hpNew->m_pPage = m_pPage;
01230         m_pPage = tmp;
01231 
01232         SetFixedPointers();
01233         SetVariablePointers();
01234         delete hpNew;
01235         return true;
01236     }
01237     delete hpNew;
01238     return false;
01239 }
01240 
01241 void CHashPage::SetVariablePointers(void)
01242 {
01243     m_pHeapStart = (unsigned char *)(m_pDirectory + m_pHeader->m_nDirSize);
01244     m_pHeapEnd = (unsigned char *)(m_pTrailer);
01245 
01246     // If less than 14.29% of the entries are empty, then do another Defrag.
01247     //
01248     m_nDirEmptyTrigger = (m_pHeader->m_nDirSize)/7;
01249 }
01250 
01251 HP_DIRINDEX CHashPage::FindFirst(HP_PHEAPLENGTH pnRecord, void *pRecord)
01252 {
01253     for (m_iDir = 0; m_iDir < m_pHeader->m_nDirSize; m_iDir++)
01254     {
01255         if (m_pDirectory[m_iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
01256         {
01257             HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[m_iDir]);
01258             *pnRecord = pNode->u.s.nRecordSize;
01259             memcpy(pRecord, pNode+1, pNode->u.s.nRecordSize);
01260             return m_iDir;
01261         }
01262     }
01263     return HP_DIR_EMPTY;
01264 }
01265 
01266 HP_DIRINDEX CHashPage::FindNext(HP_PHEAPLENGTH pnRecord, void *pRecord)
01267 {
01268     for ( m_iDir++; m_iDir < m_pHeader->m_nDirSize; m_iDir++)
01269     {
01270         if (m_pDirectory[m_iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir))
01271         {
01272             HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[m_iDir]);
01273             *pnRecord = pNode->u.s.nRecordSize;
01274             memcpy(pRecord, pNode+1, pNode->u.s.nRecordSize);
01275             return m_iDir;
01276         }
01277     }
01278     return HP_DIR_EMPTY;
01279 }
01280 
01281 CHashFile::CHashFile(void)
01282 {
01283     SeedRandomNumberGenerator();
01284     m_Cache = NULL;
01285     m_nCache = 0;
01286     Init();
01287 }
01288 
01289 void CHashFile::Init(void)
01290 {
01291     m_hDirFile = INVALID_HANDLE_VALUE;
01292     m_hPageFile = INVALID_HANDLE_VALUE;
01293     m_nDir = 0;
01294     m_nDirDepth = 0;
01295     m_pDir = NULL;
01296     m_hpCacheLookup = NULL;
01297     iCache = 0;
01298     m_iLastFlushed = 0;
01299 }
01300 
01301 #ifdef WIN32
01302 void CHashFile::WriteDirectory(void)
01303 {
01304     if (m_hDirFile == INVALID_HANDLE_VALUE) return;
01305 
01306     SetFilePointer(m_hDirFile, 0, 0, FILE_BEGIN);
01307     DWORD nWritten;
01308     WriteFile(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, &nWritten, 0);
01309     SetEndOfFile(m_hDirFile);
01310 #ifdef DO_COMMIT
01311     if (!mudstate.bStandAlone)
01312     {
01313         FlushFileBuffers(m_hDirFile);
01314     }
01315 #endif // DO_COMMIT
01316 }
01317 #else // WIN32
01318 void CHashFile::WriteDirectory(void)
01319 {
01320     if (m_hDirFile == INVALID_HANDLE_VALUE) return;
01321 
01322 #ifdef HAVE_PWRITE
01323     pwrite(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, 0);
01324 #else
01325     lseek(m_hDirFile, 0, SEEK_SET);
01326     write(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir);
01327 #endif // HAVE_PWRITE
01328     //SetEndOfFile(m_hDirFile);
01329 #ifdef DO_COMMIT
01330     if (!mudstate.bStandAlone)
01331     {
01332         fsync(m_hDirFile);
01333     }
01334 #endif // DO_COMMIT
01335 }
01336 #endif // WIN32
01337 
01338 bool CHashFile::InitializeDirectory(unsigned int n)
01339 {
01340     if (m_pDir)
01341     {
01342         delete [] m_pDir;
01343         m_pDir = NULL;
01344     }
01345     if (m_hpCacheLookup)
01346     {
01347         delete [] m_hpCacheLookup;
01348         m_hpCacheLookup = NULL;
01349     }
01350 
01351     m_nDir = n;
01352     m_nDirDepth = 0;
01353     n >>= 1;
01354     while (n)
01355     {
01356         m_nDirDepth++;
01357         n >>= 1;
01358     }
01359 
01360     m_pDir = new HF_FILEOFFSET[m_nDir];
01361     ISOUTOFMEMORY(m_pDir);
01362     m_hpCacheLookup = new int[m_nDir];
01363     ISOUTOFMEMORY(m_hpCacheLookup);
01364 
01365     for (unsigned int i = 0; i < m_nDir; i++)
01366     {
01367         m_pDir[i] = 0xFFFFFFFFUL;
01368         m_hpCacheLookup[i] = -1;
01369     }
01370 
01371     return true;
01372 }
01373 
01374 bool CHashFile::CreateFileSet(const char *szDirFile, const char *szPageFile)
01375 {
01376     CloseAll();
01377 #ifdef WIN32
01378     m_hPageFile = CreateFile(szPageFile, GENERIC_READ | GENERIC_WRITE,
01379         FILE_SHARE_READ, 0, CREATE_ALWAYS,
01380         FILE_ATTRIBUTE_NORMAL + FILE_FLAG_RANDOM_ACCESS, NULL);
01381 #else // WIN32
01382     m_hPageFile = open(szPageFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600);
01383 #endif // WIN32
01384 
01385     if (m_hPageFile == INVALID_HANDLE_VALUE)
01386     {
01387         return false;
01388     }
01389 
01390 #ifdef WIN32
01391     m_hDirFile = CreateFile(szDirFile, GENERIC_READ | GENERIC_WRITE,
01392         FILE_SHARE_READ, 0, CREATE_ALWAYS,
01393         FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL);
01394 #else // WIN32
01395     m_hDirFile = open(szDirFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600);
01396 #endif // WIN32
01397 
01398     if (m_hPageFile == INVALID_HANDLE_VALUE)
01399     {
01400         return false;
01401     }
01402 
01403     // Create empty structures in memory and write them out.
01404     //
01405     if (!InitializeDirectory(2))
01406     {
01407         return false;
01408     }
01409 
01410     iCache = AllocateEmptyPage(0, 0);
01411     if (iCache < 0)
01412     {
01413         return false;
01414     }
01415     m_Cache[iCache].m_hp.Empty(0, 0UL, 100);
01416     m_Cache[iCache].m_o = 0UL;
01417 
01418     m_pDir[0] = m_pDir[1] = m_Cache[iCache].m_o;
01419     m_hpCacheLookup[0] = m_hpCacheLookup[1] = iCache;
01420 
01421     m_Cache[iCache].m_iState = HF_CACHE_UNPROTECTED;
01422 
01423     oEndOfFile = HF_SIZEOF_PAGE;
01424 
01425 #ifdef DO_COMMIT
01426     FlushCache(iCache);
01427 #endif // DO_COMMIT
01428     WriteDirectory();
01429     return true;
01430 }
01431 
01432 bool CHashFile::RebuildDirectory(void)
01433 {
01434     // Initialize in-memory page directory
01435     //
01436     if (!InitializeDirectory(2))
01437     {
01438         return false;
01439     }
01440 
01441     // Re-build the directory from CHashPages.
01442     //
01443     for (UINT32 oPage = 0; oPage < oEndOfFile; oPage += HF_SIZEOF_PAGE)
01444     {
01445         int iCache;
01446         if ((iCache = AllocateEmptyPage(0, NULL)) < 0)
01447         {
01448             Log.WriteString("CHashFile::RebuildDirectory.  AllocateEmptyPage failed. DB DAMAGE." ENDLINE);
01449             return false;
01450         }
01451 
01452         if (m_Cache[iCache].m_hp.ReadPage(m_hPageFile, oPage))
01453         {
01454             m_Cache[iCache].m_o = oPage;
01455             m_Cache[iCache].m_iState = HF_CACHE_CLEAN;
01456             ResetAge(iCache);
01457         }
01458         else
01459         {
01460             Log.WriteString("CHashFile::RebuildDirectory.  ReadPage failed to get the page. DB DAMAGE." ENDLINE);
01461         }
01462 
01463         UINT32 nPageDepth = m_Cache[iCache].m_hp.GetDepth();
01464         while (m_nDirDepth < nPageDepth)
01465         {
01466             if (!DoubleDirectory())
01467             {
01468                 return false;
01469             }
01470         }
01471         UINT32 nStart, nEnd;
01472         m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
01473         for ( ; nStart <= nEnd; nStart++)
01474         {
01475             if (m_pDir[nStart] != 0xFFFFFFFFUL)
01476             {
01477                 Log.WriteString("CHashFile::Open - The keyspace of pages in Page File overlap." ENDLINE);
01478                 return false;
01479             }
01480             m_pDir[nStart] = oPage;
01481             m_hpCacheLookup[nStart] = iCache;
01482         }
01483     }
01484 
01485     // Validate that the directory does not have holes.
01486     //
01487     for (UINT32 iFileDir = 0; iFileDir < m_nDir; iFileDir++)
01488     {
01489         if (m_pDir[iFileDir] == 0xFFFFFFFFUL)
01490         {
01491             Log.WriteString("CHashFile::Open - Page File is incomplete." ENDLINE);
01492             return false;
01493         }
01494     }
01495     WriteDirectory();
01496     return true;
01497 }
01498 
01499 bool CHashFile::ReadDirectory(void)
01500 {
01501 #ifdef WIN32
01502     UINT32 cc = SetFilePointer(m_hDirFile, 0, 0, FILE_END);
01503 #else // WIN32
01504     UINT32 cc = lseek(m_hDirFile, 0, SEEK_END);
01505 #endif // WIN32
01506     if (cc == 0xFFFFFFFFUL)
01507     {
01508         return false;
01509     }
01510 
01511     InitializeDirectory(cc / HF_SIZEOF_FILEOFFSET);
01512 
01513 #ifdef WIN32
01514     cc = SetFilePointer(m_hDirFile, 0, 0, FILE_BEGIN);
01515     DWORD nRead;
01516     ReadFile(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, &nRead, 0);
01517 #else // WIN32
01518 #ifdef HAVE_PREAD
01519     pread(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, 0);
01520 #else
01521     lseek(m_hDirFile, 0, SEEK_SET);
01522     read(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir);
01523 #endif // HAVE_PREAD
01524 #endif // WIN32
01525     return true;
01526 }
01527 
01528 void CHashFile::InitCache(int nCachePages)
01529 {
01530     if (m_Cache)
01531     {
01532         return;
01533     }
01534 
01535     // Allocate hash page cache.
01536     //
01537     m_nCache = nCachePages;
01538     m_Cache = new HF_CACHE[m_nCache];
01539     ISOUTOFMEMORY(m_Cache);
01540     for (int i = 0; i < m_nCache; i++)
01541     {
01542         m_Cache[i].m_hp.Allocate(HF_SIZEOF_PAGE);
01543         m_Cache[i].m_iState = HF_CACHE_EMPTY;
01544         m_Cache[i].m_o = 0UL;
01545         m_Cache[i].m_iYounger = i-1;
01546         m_Cache[i].m_iOlder   = i+1;
01547     }
01548     m_Cache[0].m_iYounger = m_nCache-1;
01549     m_Cache[m_nCache-1].m_iOlder = 0;
01550     m_iOldest = 0;
01551 }
01552 
01553 int CHashFile::Open(const char *szDirFile, const char *szPageFile, int nCachePages)
01554 {
01555     CloseAll();
01556     FinalCache();
01557     InitCache(nCachePages);
01558 
01559     // First let's try to open the page file. This is the more important file.
01560     //
01561 #ifdef WIN32
01562     m_hPageFile = CreateFile(szPageFile, GENERIC_READ | GENERIC_WRITE,
01563         FILE_SHARE_READ, 0, OPEN_EXISTING,
01564         FILE_ATTRIBUTE_NORMAL + FILE_FLAG_RANDOM_ACCESS, NULL);
01565 #else // WIN32
01566     m_hPageFile = open(szPageFile, O_RDWR|O_BINARY);
01567 #endif // WIN32
01568     if (m_hPageFile == INVALID_HANDLE_VALUE)
01569     {
01570         // The PageFile doesn't exist, so we have'ta create both of them.
01571         //
01572         if (!CreateFileSet(szDirFile, szPageFile))
01573         {
01574             CloseAll();
01575             return HF_OPEN_STATUS_ERROR;
01576         }
01577         return HF_OPEN_STATUS_NEW;
01578     }
01579 
01580     // We have a page file open. Let's check how big it is. If it's
01581     // zero-length, the page file is useless to use, and we need to go through
01582     // the standard creation process. If the size is not a multiple of
01583     // HF_SIZEOF_PAGE, we need to fail.
01584     //
01585 #ifdef WIN32
01586     oEndOfFile = SetFilePointer(m_hPageFile, 0, 0, FILE_END);
01587 #else // WIN32
01588     oEndOfFile = lseek(m_hPageFile, 0, SEEK_END);
01589 #endif // WIN32
01590     if (oEndOfFile == 0xFFFFFFFFUL)
01591     {
01592         CloseAll();
01593         return HF_OPEN_STATUS_ERROR;
01594     }
01595     else if (oEndOfFile == 0UL)
01596     {
01597         // The PageFile exists, but it's zero-length, so we have'ta create
01598         // both of them.
01599         //
01600         if (!CreateFileSet(szDirFile, szPageFile))
01601         {
01602             CloseAll();
01603             return HF_OPEN_STATUS_ERROR;
01604         }
01605         return HF_OPEN_STATUS_NEW;
01606     }
01607     else if ((oEndOfFile % HF_SIZEOF_PAGE) != 0)
01608     {
01609         // This is not a mulitple of HP_SIZEOF_PAGE. Weird unknown format.
01610         //
01611         CloseAll();
01612         return HF_OPEN_STATUS_ERROR;
01613     }
01614 
01615     // Now that the page file appears valid so far, let's see if the directory
01616     // file is there. This file is not strictly necessary, we can rebuild it.
01617     // However, having it helps us to open faster.
01618     //
01619 #ifdef WIN32
01620     m_hDirFile = CreateFile(szDirFile, GENERIC_READ | GENERIC_WRITE,
01621         FILE_SHARE_READ, 0, OPEN_EXISTING,
01622         FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL);
01623 #else // WIN32
01624     m_hDirFile = open(szDirFile, O_RDWR|O_BINARY);
01625 #endif // WIN32
01626     if (m_hDirFile == INVALID_HANDLE_VALUE)
01627     {
01628         // The Directory doesn't exist, so we create it anew, and rebuild the
01629         // index.
01630 #ifdef WIN32
01631         m_hDirFile = CreateFile(szDirFile, GENERIC_READ | GENERIC_WRITE,
01632             FILE_SHARE_READ, 0, CREATE_ALWAYS,
01633             FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL);
01634 #else // WIN32
01635         m_hDirFile = open(szDirFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600);
01636 #endif // WIN32
01637 
01638         if (m_hPageFile == INVALID_HANDLE_VALUE)
01639         {
01640             CloseAll();
01641             return HF_OPEN_STATUS_ERROR;
01642         }
01643 
01644         if (!RebuildDirectory())
01645         {
01646             CloseAll();
01647             return HF_OPEN_STATUS_ERROR;
01648         }
01649         return HF_OPEN_STATUS_OLD;
01650     }
01651 
01652     // Read in the directory.
01653     //
01654     if (!ReadDirectory())
01655     {
01656         CloseAll();
01657         return HF_OPEN_STATUS_ERROR;
01658     }
01659     return HF_OPEN_STATUS_OLD;
01660 }
01661 
01662 void CHashFile::Sync(void)
01663 {
01664     if (m_hPageFile != INVALID_HANDLE_VALUE)
01665     {
01666         cs_syncs++;
01667         bool bAllFlushed = true;
01668         for (int i = 0; i < m_nCache; i++)
01669         {
01670             if (!FlushCache(i))
01671             {
01672                 bAllFlushed = false;
01673             }
01674         }
01675         if (!bAllFlushed)
01676         {
01677             Log.WriteString("CHashFile::Sync. Could not flush all the pages. DB DAMAGE." ENDLINE);
01678         }
01679 
01680 #ifdef DO_COMMIT
01681         if (!mudstate.bStandAlone)
01682         {
01683 #ifdef WIN32
01684             FlushFileBuffers(m_hPageFile);
01685 #else // WIN32
01686             fsync(m_hPageFile);
01687 #endif // WIN32
01688         }
01689 #endif // DO_COMMIT
01690     }
01691 #ifdef DO_COMMIT
01692     if (  m_hDirFile != INVALID_HANDLE_VALUE
01693        && !mudstate.bStandAlone)
01694     {
01695 #ifdef WIN32
01696         FlushFileBuffers(m_hDirFile);
01697 #else // WIN32
01698         fsync(m_hDirFile);
01699 #endif // WIN32
01700     }
01701 #endif // DO_COMMIT
01702 }
01703 
01704 void CHashFile::CloseAll(void)
01705 {
01706     if (m_hPageFile != INVALID_HANDLE_VALUE)
01707     {
01708         Sync();
01709         if (m_pDir)
01710         {
01711             delete [] m_pDir;
01712             m_pDir = NULL;
01713         }
01714         if (m_hpCacheLookup)
01715         {
01716             delete [] m_hpCacheLookup;
01717             m_hpCacheLookup = NULL;
01718         }
01719 
01720 #ifdef WIN32
01721         CloseHandle(m_hPageFile);
01722 #else // WIN32
01723         close(m_hPageFile);
01724 #endif // WIN32
01725     }
01726     if (m_hDirFile != INVALID_HANDLE_VALUE)
01727     {
01728 #ifdef WIN32
01729         CloseHandle(m_hDirFile);
01730 #else // WIN32
01731         close(m_hDirFile);
01732 #endif // WIN32
01733     }
01734     Init();
01735 }
01736 
01737 void CHashFile::FinalCache(void)
01738 {
01739     if (m_Cache)
01740     {
01741         delete [] m_Cache;
01742         m_Cache = NULL;
01743         m_nCache = 0;
01744     }
01745 }
01746 
01747 CHashFile::~CHashFile(void)
01748 {
01749     FinalCache();
01750     CloseAll();
01751 }
01752 
01753 bool CHashFile::Insert(HP_HEAPLENGTH nRecord, UINT32 nHash, void *pRecord)
01754 {
01755     cs_writes++;
01756     for (;;)
01757     {
01758         UINT32 iFileDir = nHash >> (32-m_nDirDepth);
01759         if (iFileDir >= m_nDir)
01760         {
01761             Log.WriteString("CHashFile::Insert - iFileDir out of range." ENDLINE);
01762             return false;
01763         }
01764         iCache = ReadCache(iFileDir, &cs_whits);
01765         if (iCache < 0)
01766         {
01767             Log.WriteString("CHashFile::Insert - Page wasn't valid." ENDLINE);
01768             return false;
01769         }
01770 
01771         UINT32 nStart, nEnd;
01772         m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
01773         if (iFileDir < nStart || nEnd < iFileDir)
01774         {
01775             Log.tinyprintf("CHashFile::Insert - Directory entry (0x%08X) points to the wrong page (0x%08X-0x%08X)." ENDLINE,
01776                 iFileDir, nStart, nEnd);
01777             return false;
01778         }
01779         int errInserted = m_Cache[iCache].m_hp.Insert(nRecord, nHash, pRecord);
01780         if (IS_HP_SUCCESS(errInserted))
01781         {
01782             // The record was inserted successfully, so the page is dirty.
01783             //
01784             m_Cache[iCache].m_iState = HF_CACHE_UNPROTECTED;
01785             break;
01786         }
01787         else if (HP_INSERT_ERROR_ILLEGAL == errInserted)
01788         {
01789             return false;
01790         }
01791 
01792 #ifndef WIN32
01793         // First, if we are @dumping, then we have a @forked process
01794         // that is also reading from the file. We must pause and let
01795         // this reader process finish.
01796         //
01797         if (  !mudstate.bStandAlone
01798            && mudstate.dumping)
01799         {
01800             STARTLOG(LOG_DBSAVES, "DMP", "DUMP");
01801             log_text("Waiting on previously-forked child before page-splitting... ");
01802             ENDLOG;
01803             do
01804             {
01805                 // We have a forked dump in progress, so we will wait until the
01806                 // child exits.
01807                 //
01808                 MuxAlarm.Sleep(time_1s);
01809             } while (mudstate.dumping);
01810         }
01811 #endif // !WIN32
01812 
01813         // If the depth of this page is already as deep as the directory
01814         // depth,then we must increase depth of the directory, first.
01815         //
01816         if (m_nDirDepth == m_Cache[iCache].m_hp.GetDepth())
01817         {
01818             if (!DoubleDirectory())
01819             {
01820                 return false;
01821             }
01822         }
01823 
01824         // Split this page into two pages. We become a new one, and we
01825         // are given a pointer to the other one.
01826         //
01827         int Safe[2];
01828         int nSafe = 0;
01829         int iEmpty0, iEmpty1;
01830 
01831         Safe[nSafe++] = iCache;
01832         iEmpty0 = AllocateEmptyPage(nSafe, Safe);
01833         if (iEmpty0 < 0) return false;
01834 
01835         if (iCache == iEmpty0)
01836         {
01837             Log.WriteString("CHashFile::Split - iCache == iEmpty0" ENDLINE);
01838             return false;
01839         }
01840 
01841         Safe[nSafe++] = iEmpty0;
01842         iEmpty1 = AllocateEmptyPage(nSafe, Safe);
01843         if (iEmpty1 < 0) return false;
01844 
01845         if (iCache == iEmpty1)
01846         {
01847             Log.WriteString("CHashFile::Split - iCache == iEmpty1" ENDLINE);
01848             return false;
01849         }
01850 
01851         if (iEmpty0 == iEmpty1)
01852         {
01853             Log.WriteString("CHashFile::Split - iEmpty0 == iEmpty1" ENDLINE);
01854             return false;
01855         }
01856 
01857         if (!m_Cache[iCache].m_hp.Split(m_Cache[iEmpty0].m_hp, m_Cache[iEmpty1].m_hp))
01858         {
01859             return false;
01860         }
01861 
01862         // Tack another page onto the end of the .pag file.
01863         //
01864         long oNew = oEndOfFile;
01865         oEndOfFile += HF_SIZEOF_PAGE;
01866 
01867         // iEmpty0 => iCache. iEmpty1 => end of file
01868         //
01869         m_Cache[iCache].m_iState = HF_CACHE_EMPTY;
01870         m_Cache[iEmpty0].m_o = m_Cache[iCache].m_o;
01871         m_Cache[iEmpty1].m_o = oNew;
01872         m_Cache[iEmpty0].m_iState = HF_CACHE_UNPROTECTED;
01873         m_Cache[iEmpty1].m_iState = HF_CACHE_UNPROTECTED;
01874 
01875         // Update the directory.
01876         //
01877         m_Cache[iEmpty0].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
01878         for ( ; nStart <= nEnd; nStart++)
01879         {
01880             m_hpCacheLookup[nStart] = iEmpty0;
01881         }
01882         m_Cache[iEmpty1].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
01883         for ( ; nStart <= nEnd; nStart++)
01884         {
01885             m_pDir[nStart] = oNew;
01886             m_hpCacheLookup[nStart] = iEmpty1;
01887         }
01888 
01889         // Flush the pages out.
01890         //
01891         FlushCache(iEmpty1);
01892         FlushCache(iEmpty0);
01893 
01894 #ifdef DO_COMMIT
01895         if (!mudstate.bStandAlone)
01896         {
01897 #ifdef WIN32
01898             FlushFileBuffers(m_hPageFile);
01899 #else // WIN32
01900             fsync(m_hPageFile);
01901 #endif // WIN32
01902         }
01903 #endif // DO_COMMIT
01904 
01905         // Write Directory
01906         //
01907 #ifdef WIN32
01908         SetFilePointer(m_hDirFile, 0, 0, FILE_BEGIN);
01909         DWORD nWritten;
01910         WriteFile(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, &nWritten, 0);
01911 #else // WIN32
01912 #ifdef HAVE_PWRITE
01913         pwrite(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, 0);
01914 #else
01915         lseek(m_hDirFile, 0, SEEK_SET);
01916         write(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir);
01917 #endif // HAVE_PWRITE
01918 #endif // WIN32
01919 
01920 #ifdef DO_COMMIT
01921         if (!mudstate.bStandAlone)
01922         {
01923 #ifdef WIN32
01924             FlushFileBuffers(m_hDirFile);
01925 #else // WIN32
01926             fsync(m_hDirFile);
01927 #endif // WIN32
01928         }
01929 #endif // DO_COMMIT
01930     }
01931     return true;
01932 }
01933 
01934 bool CHashFile::DoubleDirectory(void)
01935 {
01936     unsigned int nNewDir     = 2 * m_nDir;
01937     HP_DIRINDEX nNewDirDepth = m_nDirDepth + 1;
01938 
01939     HF_PFILEOFFSET pNewDir = new HF_FILEOFFSET[nNewDir];
01940     ISOUTOFMEMORY(pNewDir);
01941 
01942     int *pNewCacheLookup = new int[nNewDir];
01943     ISOUTOFMEMORY(pNewCacheLookup);
01944 
01945     unsigned int iNewDir = 0;
01946     for (unsigned int iDir = 0; iDir < m_nDir; iDir++)
01947     {
01948         pNewDir[iNewDir]   = m_pDir[iDir];
01949         pNewDir[iNewDir+1] = m_pDir[iDir];
01950         pNewCacheLookup[iNewDir]   = m_hpCacheLookup[iDir];
01951         pNewCacheLookup[iNewDir+1] = m_hpCacheLookup[iDir];
01952         iNewDir += 2;
01953     }
01954 
01955     // Write out the new directory. It's always larger than
01956     // the previous one.
01957     //
01958     WriteDirectory();
01959 
01960     delete [] m_pDir;
01961     m_pDir = pNewDir;
01962 
01963     delete [] m_hpCacheLookup;
01964     m_hpCacheLookup = pNewCacheLookup;
01965 
01966     m_nDirDepth = nNewDirDepth;
01967     m_nDir = nNewDir;
01968     return true;
01969 }
01970 
01971 HP_DIRINDEX CHashFile::FindFirstKey(UINT32 nHash)
01972 {
01973     cs_reads++;
01974 
01975     UINT32 iFileDir = nHash >> (32-m_nDirDepth);
01976     if (iFileDir >= m_nDir)
01977     {
01978         Log.WriteString("CHashFile::Insert - iFileDir out of range." ENDLINE);
01979         cs_fails++;
01980         return HF_FIND_END;
01981     }
01982     iCache = ReadCache(iFileDir, &cs_rhits);
01983     if (iCache < 0)
01984     {
01985         cs_fails++;
01986         return HF_FIND_END;
01987     }
01988     UINT32 nStart, nEnd;
01989     m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
01990     if (iFileDir < nStart || nEnd < iFileDir)
01991     {
01992         Log.tinyprintf("CHashFile::Find - Directory entry (0x%08X) points to the wrong page (0x%08X-0x%08X)." ENDLINE,
01993             iFileDir, nStart, nEnd);
01994         return HF_FIND_END;
01995     }
01996 
01997     unsigned int numchecks;
01998     HP_DIRINDEX iDir = m_Cache[iCache].m_hp.FindFirstKey(nHash, &numchecks);
01999 
02000     if (iDir == HP_DIR_EMPTY)
02001     {
02002         cs_fails++;
02003         return HF_FIND_END;
02004     }
02005     return iDir;
02006 }
02007 
02008 HP_DIRINDEX CHashFile::FindNextKey(HP_DIRINDEX iDir, UINT32 nHash)
02009 {
02010     cs_reads++;
02011 
02012     unsigned int numchecks;
02013 
02014     iDir = m_Cache[iCache].m_hp.FindNextKey(iDir, nHash, &numchecks);
02015 
02016     if (iDir == HP_DIR_EMPTY)
02017     {
02018         cs_fails++;
02019         return HF_FIND_END;
02020     }
02021     return iDir;
02022 }
02023 
02024 void CHashFile::Copy(HP_DIRINDEX iDir, HP_PHEAPLENGTH pnRecord, void *pRecord)
02025 {
02026     m_Cache[iCache].m_hp.HeapCopy(iDir, pnRecord, pRecord);
02027 }
02028 
02029 void CHashFile::Remove(HP_DIRINDEX iDir)
02030 {
02031     cs_dels++;
02032     m_Cache[iCache].m_hp.HeapFree(iDir);
02033     m_Cache[iCache].m_iState = HF_CACHE_UNPROTECTED;
02034 }
02035 
02036 bool CHashFile::FlushCache(int iCache)
02037 {
02038     switch (m_Cache[iCache].m_iState)
02039     {
02040     case HF_CACHE_UNPROTECTED:
02041 #ifdef HP_PROTECTION
02042         m_Cache[iCache].m_hp.Protection();
02043 #endif // HP_PROTECTION
02044 
02045     case HF_CACHE_UNWRITTEN:
02046         if (m_Cache[iCache].m_hp.WritePage(m_hPageFile, m_Cache[iCache].m_o))
02047         {
02048             m_Cache[iCache].m_iState = HF_CACHE_CLEAN;
02049         }
02050         else
02051         {
02052             return false;
02053         }
02054     }
02055     return true;
02056 }
02057 
02058 int CHashFile::AllocateEmptyPage(int nSafe, int Safe[])
02059 {
02060     int cnt = m_nCache;
02061     while (cnt--)
02062     {
02063         int i = m_iOldest;
02064 
02065         bool bExclude = false;
02066         for (int j = 0; j < nSafe; j++)
02067         {
02068             if (Safe[j] == i)
02069             {
02070                 bExclude = true;
02071                 break;
02072             }
02073         }
02074 
02075         ResetAge(i);
02076 
02077         if (  !bExclude
02078            && FlushCache(i))
02079         {
02080             if (HF_CACHE_EMPTY != m_Cache[i].m_iState)
02081             {
02082                 UINT32 nStart, nEnd;
02083                 m_Cache[i].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
02084                 for ( ; nStart <= nEnd; nStart++)
02085                 {
02086                     m_hpCacheLookup[nStart] = -1;
02087                 }
02088                 m_Cache[i].m_iState = HF_CACHE_EMPTY;
02089             }
02090             return i;
02091         }
02092     }
02093     return -1;
02094 }
02095 
02096 void CHashFile::ResetAge(int iEntry)
02097 {
02098     if (iEntry == m_iOldest)
02099     {
02100         // Rotate the doubly-linked list to make the oldest entry the
02101         // youngest.
02102         //
02103         m_iOldest = m_Cache[m_iOldest].m_iYounger;
02104     }
02105     else if (iEntry == m_Cache[m_iOldest].m_iOlder)
02106     {
02107         // This is already the youngest entry.
02108         //
02109     }
02110     else
02111     {
02112         // Unlink this entry.
02113         //
02114         int iYounger = m_Cache[iEntry].m_iYounger;
02115         int iOlder   = m_Cache[iEntry].m_iOlder;
02116         m_Cache[iYounger].m_iOlder = iOlder;
02117         m_Cache[iOlder].m_iYounger = iYounger;
02118 
02119         // Re-link at the young end of the queue.
02120         //
02121         iYounger = m_iOldest;
02122         iOlder   = m_Cache[iYounger].m_iOlder;
02123         m_Cache[iEntry].m_iOlder   = iOlder;
02124         m_Cache[iEntry].m_iYounger = iYounger;
02125         m_Cache[iYounger].m_iOlder = iEntry;
02126         m_Cache[iOlder].m_iYounger = iEntry;
02127     }
02128 }
02129 
02130 void CHashFile::Tick(void)
02131 {
02132     int nCycle = mudconf.check_interval;
02133     if (mudconf.dump_interval < nCycle)
02134     {
02135         nCycle = mudconf.dump_interval;
02136     }
02137 
02138     CLinearTimeDelta ltdCycle;
02139     ltdCycle.SetSeconds(nCycle);
02140 
02141     int n = (mudconf.cache_tick_period*m_nCache)/ltdCycle;
02142     if (n < 1)
02143     {
02144         n = 1;
02145     }
02146 
02147     for (int i = 0; i < n; i++)
02148     {
02149         // Go ahead and flush a cache entry...just to keep the sync load
02150         // down a bit. This gives the cache time to age, and yet, pushes
02151         // the pages off to the disk eventually and gradually.
02152         //
02153         FlushCache(m_iLastFlushed++);
02154         if (m_iLastFlushed >= m_nCache)
02155         {
02156             m_iLastFlushed = 0;
02157         }
02158     }
02159 }
02160 
02161 int CHashFile::ReadCache(UINT32 iFileDir, int *phits)
02162 {
02163     int iCache = m_hpCacheLookup[iFileDir];
02164     HF_FILEOFFSET oPage = m_pDir[iFileDir];
02165 
02166     if (  iCache != -1
02167        && m_Cache[iCache].m_iState != HF_CACHE_EMPTY
02168        && m_Cache[iCache].m_o == oPage)
02169     {
02170         ResetAge(iCache);
02171         (*phits)++;
02172         return iCache;
02173     }
02174 
02175     if ((iCache = AllocateEmptyPage(0, NULL)) >= 0)
02176     {
02177         if (m_Cache[iCache].m_hp.ReadPage(m_hPageFile, oPage))
02178         {
02179             //if (m_Cache[i].m_hp.Validate())
02180             //{
02181                 m_Cache[iCache].m_o = oPage;
02182                 m_Cache[iCache].m_iState = HF_CACHE_CLEAN;
02183                 ResetAge(iCache);
02184 
02185                 UINT32 nStart, nEnd;
02186                 m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd);
02187                 for ( ; nStart <= nEnd; nStart++)
02188                 {
02189                     m_hpCacheLookup[nStart] = iCache;
02190                 }
02191 
02192                 return iCache;
02193             //}
02194         }
02195         else
02196         {
02197             Log.WriteString("CHashFile::ReadCache.  ReadPage failed to get the page. DB DAMAGE." ENDLINE);
02198         }
02199     }
02200     return -1;
02201 }
02202 
02203 CHashTable::CHashTable(void)
02204 {
02205     SeedRandomNumberGenerator();
02206     Init();
02207 }
02208 
02209 void CHashTable::Init(void)
02210 {
02211     m_nDir = 2;
02212     m_nDirDepth = 1;
02213     m_pDir = new pCHashPage[m_nDir];
02214     if (m_pDir)
02215     {
02216         m_pDir[1] = m_pDir[0] = new CHashPage;
02217         if (m_pDir[0])
02218         {
02219             if (m_pDir[0]->Allocate(HT_SIZEOF_PAGE))
02220             {
02221                 m_pDir[0]->Empty(0, 0UL, 100);
02222             }
02223             else
02224             {
02225                 delete m_pDir[0];
02226                 m_pDir[0] = NULL;
02227             }
02228         }
02229     }
02230 
02231     m_nPages = 1;
02232     m_nEntries = 0;
02233     m_nDeletions = 0;
02234     m_nScans = 0;
02235     m_nHits = 0;
02236     m_nChecks = 0;
02237     m_nMaxScan = 0;
02238 }
02239 
02240 void CHashTable::ResetStats(void)
02241 {
02242     m_nScans = 0;
02243     m_nHits = 0;
02244     m_nChecks = 0;
02245 }
02246 
02247 bool CHashTable::Insert(HP_HEAPLENGTH nRecord, UINT32  nHash, void *pRecord)
02248 {
02249     for (;;)
02250     {
02251         UINT32  iTableDir = nHash >> (32 - m_nDirDepth);
02252 #ifdef HP_PROTECTION
02253         if (iTableDir >= m_nDir)
02254         {
02255             Log.WriteString("CHashTable::Insert - iTableDir out of range." ENDLINE);
02256             return false;
02257         }
02258 #endif // HP_PROTECTION
02259         m_hpLast = m_pDir[iTableDir];
02260         if (!m_hpLast)
02261         {
02262             Log.WriteString("CHashTable::Insert - Page wasn't valid." ENDLINE);
02263             return false;
02264         }
02265         UINT32  nStart, nEnd;
02266 #ifdef HP_PROTECTION
02267         m_hpLast->GetRange(m_nDirDepth, nStart, nEnd);
02268         if (iTableDir < nStart || nEnd < iTableDir)
02269         {
02270             Log.WriteString("CHashTable::Insert - Directory points to the wrong page." ENDLINE);
02271             return false;
02272         }
02273 #endif // HP_PROTECTION
02274         int errInserted = m_hpLast->Insert(nRecord, nHash, pRecord);
02275         if (IS_HP_SUCCESS(errInserted))
02276         {
02277             if (errInserted == HP_INSERT_SUCCESS_DEFRAG)
02278             {
02279                 // Otherwise, this value will be over inflated.
02280                 //
02281                 m_nMaxScan = 0;
02282             }
02283             break;
02284         }
02285         if (errInserted == HP_INSERT_ERROR_ILLEGAL)
02286         {
02287             return false;
02288         }
02289 
02290         // If the depth of this page is already as deep as the directory
02291         // depth,then we must increase depth of the directory, first.
02292         //
02293         if (m_nDirDepth == m_hpLast->GetDepth())
02294         {
02295             if (!DoubleDirectory())
02296             {
02297                 return false;
02298             }
02299         }
02300 
02301         // Split this page into two pages. We become a new one, and we
02302         // are given a pointer to the other one.
02303         //
02304         CHashPage *hpEmpty0 = new CHashPage;
02305         if (!hpEmpty0) return false;
02306         if (!hpEmpty0->Allocate(HT_SIZEOF_PAGE))
02307         {
02308             delete hpEmpty0;
02309             return false;
02310         }
02311 
02312         CHashPage *hpEmpty1 = new CHashPage;
02313         if (!hpEmpty1) return false;
02314         if (!hpEmpty1->Allocate(HT_SIZEOF_PAGE))
02315         {
02316             delete hpEmpty0;
02317             delete hpEmpty1;
02318             return false;
02319         }
02320 
02321         if (!m_hpLast->Split(*hpEmpty0, *hpEmpty1))
02322         {
02323             return false;
02324         }
02325 
02326         // Otherwise, this value will be over inflated.
02327         //
02328         m_nMaxScan = 0;
02329 
02330         // Now, update the directory.
02331         //
02332         hpEmpty0->GetRange(m_nDirDepth, nStart, nEnd);
02333         for ( ; nStart <= nEnd; nStart++)
02334         {
02335             m_pDir[nStart] = hpEmpty0;
02336         }
02337 
02338         hpEmpty1->GetRange(m_nDirDepth, nStart, nEnd);
02339         for ( ; nStart <= nEnd; nStart++)
02340         {
02341             m_pDir[nStart] = hpEmpty1;
02342         }
02343         m_nPages++;
02344 
02345         delete m_hpLast;
02346         m_hpLast = 0;
02347     }
02348     m_nEntries++;
02349     return true;
02350 }
02351 
02352 bool CHashTable::DoubleDirectory(void)
02353 {
02354     unsigned int nNewDir      = 2 * m_nDir;
02355     unsigned int nNewDirDepth = m_nDirDepth + 1;
02356 
02357     pCHashPage *pNewDir = new pCHashPage[nNewDir];
02358     if (pNewDir)
02359     {
02360         unsigned int iNewDir = 0;
02361         for (unsigned int iDir = 0; iDir < m_nDir; iDir++)
02362         {
02363             pNewDir[iNewDir++] = m_pDir[iDir];
02364             pNewDir[iNewDir++] = m_pDir[iDir];
02365         }
02366 
02367         delete [] m_pDir;
02368 
02369         m_pDir = pNewDir;
02370         m_nDirDepth = nNewDirDepth;
02371         m_nDir = nNewDir;
02372         return true;
02373     }
02374     return false;
02375 }
02376 
02377 HP_DIRINDEX CHashTable::FindFirstKey(UINT32  nHash)
02378 {
02379     m_nScans++;
02380     UINT32  iTableDir = nHash >> (32-m_nDirDepth);
02381 #ifdef HP_PROTECTION
02382     if (iTableDir >= m_nDir)
02383     {
02384         Log.WriteString("CHashTable::Insert - iTableDir out of range." ENDLINE);
02385         return HF_FIND_END;
02386     }
02387 #endif // HP_PROTECTION
02388     m_hpLast = m_pDir[iTableDir];
02389     if (!m_hpLast)
02390     {
02391         Log.WriteString("CHashTable::Insert - Page wasn't valid." ENDLINE);
02392         return HF_FIND_END;
02393     }
02394 #ifdef HP_PROTECTION
02395     UINT32  nStart, nEnd;
02396     m_hpLast->GetRange(m_nDirDepth, nStart, nEnd);
02397     if (iTableDir < nStart || nEnd < iTableDir)
02398     {
02399         Log.WriteString("CHashTable::Find - Directory points to the wrong page." ENDLINE);
02400         return HF_FIND_END;
02401     }
02402 #endif // HP_PROTECTION
02403     unsigned int numchecks;
02404 
02405     HP_DIRINDEX iDir = m_hpLast->FindFirstKey(nHash, &numchecks);
02406 
02407     m_nChecks += numchecks;
02408     if (numchecks > m_nMaxScan)
02409     {
02410         m_nMaxScan = numchecks;
02411     }
02412     if (iDir == HP_DIR_EMPTY)
02413     {
02414         return HF_FIND_END;
02415     }
02416     m_nHits++;
02417     return iDir;
02418 }
02419 
02420 HP_DIRINDEX CHashTable::FindNextKey(HP_DIRINDEX iDir, UINT32  nHash)
02421 {
02422     m_nScans++;
02423     unsigned int numchecks;
02424 
02425     iDir = m_hpLast->FindNextKey(iDir, nHash, &numchecks);
02426 
02427     m_nChecks += numchecks;
02428     if (numchecks > m_nMaxScan)
02429     {
02430         m_nMaxScan = numchecks;
02431     }
02432     if (iDir == HP_DIR_EMPTY)
02433     {
02434         return HF_FIND_END;
02435     }
02436     m_nHits++;
02437     return iDir;
02438 }
02439 
02440 void CHashTable::Copy(HP_DIRINDEX iDir, HP_PHEAPLENGTH pnRecord, void *pRecord)
02441 {
02442     m_hpLast->HeapCopy(iDir, pnRecord, pRecord);
02443 }
02444 
02445 void CHashTable::Remove(HP_DIRINDEX iDir)
02446 {
02447     m_nEntries--;
02448     m_nDeletions++;
02449     m_hpLast->HeapFree(iDir);
02450 }
02451 
02452 void CHashTable::Update(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, void *pRecord)
02453 {
02454     m_hpLast->HeapUpdate(iDir, nRecord, pRecord);
02455 }
02456 
02457 CHashTable::~CHashTable(void)
02458 {
02459     Final();
02460 }
02461 
02462 void CHashTable::Final(void)
02463 {
02464     if (m_pDir)
02465     {
02466         m_hpLast = 0;
02467         for (unsigned int i = 0; i < m_nDir; i++)
02468         {
02469             CHashPage *hp = m_pDir[i];
02470 
02471             if (hp != m_hpLast && hp)
02472             {
02473                 delete hp;
02474                 m_hpLast = hp;
02475             }
02476         }
02477         delete [] m_pDir;
02478         m_pDir = NULL;
02479     }
02480 }
02481 
02482 void CHashTable::Reset(void)
02483 {
02484     Final();
02485     Init();
02486 }
02487 
02488 HP_DIRINDEX CHashTable::FindFirst(HP_PHEAPLENGTH pnRecord, void *pRecord)
02489 {
02490     m_hpLast = 0;
02491     for (m_iPage = 0; m_iPage < m_nDir; m_iPage++)
02492     {
02493         if (m_pDir[m_iPage] == m_hpLast) continue;
02494         m_hpLast = m_pDir[m_iPage];
02495         if (m_hpLast)
02496         {
02497             HP_DIRINDEX iDir = m_hpLast->FindFirst(pnRecord, pRecord);
02498             if (iDir != HP_DIR_EMPTY)
02499             {
02500                 return iDir;
02501             }
02502         }
02503     }
02504     return HF_FIND_END;
02505 }
02506 
02507 HP_DIRINDEX CHashTable::FindNext(HP_PHEAPLENGTH pnRecord, void *pRecord)
02508 {
02509     if (m_hpLast)
02510     {
02511         HP_DIRINDEX iDir = m_hpLast->FindNext(pnRecord, pRecord);
02512         if (iDir != HP_DIR_EMPTY)
02513         {
02514             return iDir;
02515         }
02516     }
02517 
02518     // Move on to the next page.
02519     //
02520     for ( ; m_iPage < m_nDir; m_iPage++)
02521     {
02522         // Move on to the next page.
02523         //
02524         if (m_pDir[m_iPage] == m_hpLast) continue;
02525         m_hpLast = m_pDir[m_iPage];
02526         if (m_hpLast)
02527         {
02528             HP_DIRINDEX iDir = m_hpLast->FindFirst(pnRecord, pRecord);
02529             if (iDir != HP_DIR_EMPTY)
02530             {
02531                 return iDir;
02532             }
02533         }
02534     }
02535     return HF_FIND_END;
02536 }
02537 
02538 INT64 CHashTable::GetEntryCount()
02539 {
02540     return m_nEntries;
02541 }
02542 
02543 void CHashTable::GetStats
02544 (
02545     unsigned int *hashsize,
02546     int *entries,
02547     INT64 *deletes,
02548     INT64 *scans,
02549     INT64 *hits,
02550     INT64 *checks,
02551     int *max_scan
02552 )
02553 {
02554     *hashsize = m_nPages;
02555     *entries = m_nEntries;
02556     *deletes = m_nDeletions;
02557     *scans = m_nScans;
02558     *hits = m_nHits;
02559     *checks = m_nChecks;
02560     *max_scan = m_nMaxScan;
02561 }
02562 
02563 CLogFile Log;
02564 void CLogFile::WriteInteger(int iNumber)
02565 {
02566     char aTempBuffer[20];
02567     size_t nTempBuffer = mux_ltoa(iNumber, aTempBuffer);
02568     WriteBuffer(nTempBuffer, aTempBuffer);
02569 }
02570 
02571 void CLogFile::WriteBuffer(size_t nString, const char *pString)
02572 {
02573     if (!bEnabled)
02574     {
02575         return;
02576     }
02577 
02578 #ifdef WIN32
02579     EnterCriticalSection(&csLog);
02580 #endif // WIN32
02581 
02582     while (nString > 0)
02583     {
02584         size_t nAvailable = SIZEOF_LOG_BUFFER - m_nBuffer;
02585         if (nAvailable == 0)
02586         {
02587             Flush();
02588             continue;
02589         }
02590 
02591         size_t nToMove = nAvailable;
02592         if (nString < nToMove)
02593         {
02594             nToMove = nString;
02595         }
02596 
02597         // Move nToMove bytes from pString to aBuffer+nBuffer
02598         //
02599         memcpy(m_aBuffer+m_nBuffer, pString, nToMove);
02600         pString += nToMove;
02601         nString -= nToMove;
02602         m_nBuffer += nToMove;
02603     }
02604     Flush();
02605 
02606 #ifdef WIN32
02607     LeaveCriticalSection(&csLog);
02608 #endif // WIN32
02609 }
02610 
02611 void CLogFile::WriteString(const char *pString)
02612 {
02613     size_t nString = strlen(pString);
02614     WriteBuffer(nString, pString);
02615 }
02616 
02617 void DCL_CDECL CLogFile::tinyprintf(char *fmt, ...)
02618 {
02619     va_list ap;
02620     va_start(ap, fmt);
02621     char aTempBuffer[SIZEOF_LOG_BUFFER];
02622     int nString = mux_vsnprintf(aTempBuffer, SIZEOF_LOG_BUFFER, fmt, ap);
02623     va_end(ap);
02624     WriteBuffer(nString, aTempBuffer);
02625 }
02626 
02627 static void MakeLogName
02628 (
02629     const char *pBasename,
02630     const char *szPrefix,
02631     CLinearTimeAbsolute lta,
02632     char *szLogName
02633 )
02634 {
02635     char szTimeStamp[18];
02636     lta.ReturnUniqueString(szTimeStamp);
02637     if (  pBasename
02638        && pBasename[0] != '\0')
02639     {
02640         sprintf(szLogName, "%s/%s-%s.log",
02641             pBasename,
02642             szPrefix,
02643             szTimeStamp);
02644     }
02645     else
02646     {
02647         sprintf(szLogName, "%s-%s.log",
02648             szPrefix,
02649             szTimeStamp);
02650     }
02651 }
02652 
02653 void CLogFile::CreateLogFile(void)
02654 {
02655     CloseLogFile();
02656 
02657     m_nSize = 0;
02658 #ifdef WIN32
02659     m_hFile = CreateFile(m_szFilename, GENERIC_READ | GENERIC_WRITE,
02660         FILE_SHARE_READ, 0, CREATE_ALWAYS,
02661         FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL);
02662 #else // WIN32
02663     m_hFile = open(m_szFilename, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600);
02664 #endif // WIN32
02665 }
02666 
02667 void CLogFile::AppendLogFile(void)
02668 {
02669     CloseLogFile();
02670 
02671 #ifdef WIN32
02672     m_hFile = CreateFile(m_szFilename, GENERIC_READ | GENERIC_WRITE,
02673         FILE_SHARE_READ, 0, OPEN_ALWAYS,
02674         FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL);
02675 #else // WIN32
02676     m_hFile = open(m_szFilename, O_RDWR|O_BINARY, 0600);
02677 #endif // WIN32
02678 
02679     if (m_hFile != INVALID_HANDLE_VALUE)
02680     {
02681 #ifdef WIN32
02682         SetFilePointer(m_hFile, 0, 0, FILE_END);
02683 #else // WIN32
02684         lseek(m_hFile, 0, SEEK_END);
02685 #endif // WIN32
02686     }
02687 }
02688 
02689 void CLogFile::CloseLogFile(void)
02690 {
02691     if (m_hFile != INVALID_HANDLE_VALUE)
02692     {
02693 #ifdef WIN32
02694         CloseHandle(m_hFile);
02695 #else // WIN32
02696         close(m_hFile);
02697 #endif // WIN32
02698         m_hFile = INVALID_HANDLE_VALUE;
02699     }
02700 }
02701 
02702 #define FILE_SIZE_TRIGGER (512*1024UL)
02703 
02704 void CLogFile::Flush(void)
02705 {
02706     if (  m_nBuffer <= 0
02707        || !bEnabled)
02708     {
02709         return;
02710     }
02711     if (bUseStderr)
02712     {
02713         fwrite(m_aBuffer, m_nBuffer, 1, stderr);
02714     }
02715     else
02716     {
02717         m_nSize += m_nBuffer;
02718 #ifdef WIN32
02719         unsigned long nWritten;
02720         WriteFile(m_hFile, m_aBuffer, (DWORD)m_nBuffer, &nWritten, NULL);
02721 #else // WIN32
02722         write(m_hFile, m_aBuffer, m_nBuffer);
02723 #endif // WIN32
02724 
02725         if (m_nSize > FILE_SIZE_TRIGGER)
02726         {
02727             CloseLogFile();
02728 
02729             m_ltaStarted.GetLocal();
02730             MakeLogName(m_pBasename, m_szPrefix, m_ltaStarted, m_szFilename);
02731 
02732             CreateLogFile();
02733         }
02734     }
02735     m_nBuffer = 0;
02736 }
02737 
02738 void CLogFile::SetPrefix(const char *szPrefix)
02739 {
02740     if (  !bUseStderr
02741        && strcmp(szPrefix, m_szPrefix) != 0)
02742     {
02743         if (bEnabled)
02744         {
02745             CloseLogFile();
02746         }
02747 
02748         char szNewName[SIZEOF_PATHNAME];
02749         MakeLogName(m_pBasename, szPrefix, m_ltaStarted, szNewName);
02750         if (bEnabled)
02751         {
02752             ReplaceFile(m_szFilename, szNewName);
02753         }
02754         strcpy(m_szPrefix, szPrefix);
02755         strcpy(m_szFilename, szNewName);
02756 
02757         if (bEnabled)
02758         {
02759             AppendLogFile();
02760         }
02761     }
02762 }
02763 
02764 void CLogFile::SetBasename(const char *pBasename)
02765 {
02766     if (m_pBasename)
02767     {
02768         MEMFREE(m_pBasename);
02769         m_pBasename = NULL;
02770     }
02771     if (  pBasename
02772        && strcmp(pBasename, "-") == 0)
02773     {
02774         bUseStderr = true;
02775     }
02776     else
02777     {
02778         bUseStderr = false;
02779         if (pBasename)
02780         {
02781             m_pBasename = StringClone(pBasename);
02782         }
02783         else
02784         {
02785             m_pBasename = StringClone("");
02786         }
02787     }
02788 }
02789 
02790 CLogFile::CLogFile(void)
02791 {
02792 #ifdef WIN32
02793     InitializeCriticalSection(&csLog);
02794 #endif // WIN32
02795 
02796     m_ltaStarted.GetLocal();
02797     m_hFile = INVALID_HANDLE_VALUE;
02798     m_nSize = 0;
02799     m_nBuffer = 0;
02800     bEnabled = false;
02801     bUseStderr = true;
02802     m_pBasename = NULL;
02803     m_szPrefix[0] = '\0';
02804     m_szFilename[0] = '\0';
02805 }
02806 
02807 void CLogFile::StartLogging()
02808 {
02809     if (!bUseStderr)
02810     {
02811         m_ltaStarted.GetLocal();
02812         MakeLogName(m_pBasename, m_szPrefix, m_ltaStarted, m_szFilename);
02813         CreateLogFile();
02814     }
02815     bEnabled = true;
02816 }
02817 
02818 void CLogFile::StopLogging(void)
02819 {
02820     Flush();
02821     bEnabled = false;
02822     if (!bUseStderr)
02823     {
02824         CloseLogFile();
02825         m_szPrefix[0] = '\0';
02826         m_szFilename[0] = '\0';
02827         SetBasename(NULL);
02828     }
02829 }
02830 
02831 CLogFile::~CLogFile(void)
02832 {
02833     StopLogging();
02834 #ifdef WIN32
02835     DeleteCriticalSection(&csLog);
02836 #endif // WIN32
02837 }
02838 
02839 #ifdef MEMORY_ACCOUNTING
02840 
02841 #pragma pack(1)
02842 typedef struct
02843 {
02844     void  *address;
02845     int    size;
02846     UINT32 fileline;
02847 } AllocDataRec;
02848 
02849 typedef struct
02850 {
02851     size_t nSize;
02852     int    line;
02853     char   filename[1];
02854 } IdentDataRec;
02855 #pragma pack()
02856 
02857 bool bMemAccountingInitialized = false;
02858 CHashFile hfAllocData;
02859 CHashFile hfIdentData;
02860 extern long DebugTotalMemory;
02861 
02862 UINT32 HashPointer(void *vp)
02863 {
02864     UINT32 nHash1 = HASH_ProcessBuffer(0, &vp, sizeof(void *));
02865     UINT32 nHash2 = CRC32_ProcessBuffer(0, &vp, sizeof(void *));
02866     mux_assert(nHash1 == nHash2);
02867     return nHash1;
02868 }
02869 
02870 unsigned long HashFileLine(const char *fn, int line)
02871 {
02872     UINT32 nHash1 = CRC32_ProcessInteger(line);
02873     nHash1 = HASH_ProcessBuffer(nHash1, fn, strlen(fn)+1);
02874     UINT32 nHash2 = CRC32_ProcessInteger(line);
02875     nHash2 = CRC32_ProcessBuffer(nHash2, fn, strlen(fn)+1);
02876     mux_assert(nHash1 == nHash2);
02877     return nHash1;
02878 }
02879 
02880 bool SubtractSpaceFromFileLine
02881 (
02882     UINT32  nHash,
02883     int     size,
02884     size_t *pnSpace,
02885     int    *pAllocLine,
02886     char   *file
02887 )
02888 {
02889     *pnSpace = 0;
02890     *pAllocLine = -1;
02891     HP_DIRINDEX iDir = hfIdentData.FindFirstKey(nHash);
02892     if (iDir != HF_FIND_END)
02893     {
02894         HP_HEAPLENGTH nIdent;
02895         char Buffer[1024];
02896         hfIdentData.Copy(iDir, &nIdent, Buffer);
02897         hfIdentData.Remove(iDir);
02898 
02899         IdentDataRec *idr = (IdentDataRec *)Buffer;
02900         *pAllocLine = idr->line;
02901         strcpy(file, idr->filename);
02902         idr->nSize -= size;
02903         *pnSpace = idr->nSize;
02904 
02905         hfIdentData.Insert(nIdent, nHash, Buffer);
02906     }
02907     return true;
02908 }
02909 
02910 unsigned long AddSpaceToFileLine
02911 (
02912     const char *file,
02913     int     line,
02914     size_t  nSpace,
02915     size_t *pnTotalSpace
02916 )
02917 {
02918     *pnTotalSpace = 0;
02919 
02920     HP_HEAPLENGTH nIdent;
02921     char Buffer[1024];
02922     IdentDataRec *idr = (IdentDataRec *)Buffer;
02923     UINT32 nHash = HashFileLine(file, line);
02924     bool bFound = false;
02925 again:
02926     HP_DIRINDEX iDir = hfIdentData.FindFirstKey(nHash);
02927     while (iDir != HF_FIND_END)
02928     {
02929         hfIdentData.Copy(iDir, &nIdent, Buffer);
02930         if (  line == idr->line
02931            && strcmp(idr->filename, file) == 0)
02932         {
02933             hfIdentData.Remove(iDir);
02934             nSpace += idr->nSize;
02935             bFound = true;
02936             goto again;
02937         }
02938         iDir = hfIdentData.FindNextKey(iDir, nHash);
02939     }
02940     if (!bFound)
02941     {
02942         idr->line = line;
02943         strcpy(idr->filename, file);
02944 
02945         char *p = idr->filename + strlen(idr->filename) + 1;
02946         nIdent = p - Buffer;
02947     }
02948     idr->nSize = nSpace;
02949     hfIdentData.Insert(nIdent, nHash, Buffer);
02950     *pnTotalSpace = nSpace;
02951     return nHash;
02952 }
02953 
02954 void AccountForAllocation(void *pointer, size_t size, const char *file, int line)
02955 {
02956     DebugTotalMemory += size;
02957 
02958     AllocDataRec adr;
02959     adr.address = pointer;
02960     adr.size = size;
02961 
02962     unsigned long nHash = HashPointer(pointer);
02963 again:
02964     HP_DIRINDEX iDir = hfAllocData.FindFirstKey(nHash);
02965 
02966     while (iDir != HF_FIND_END)
02967     {
02968         AllocDataRec adr2;
02969         HP_HEAPLENGTH nRecord;
02970         hfAllocData.Copy(iDir, &nRecord, &adr2);
02971         if (adr2.address == adr.address)
02972         {
02973             // Whoa! We've been told this new pointer, but it's not.
02974             //
02975             fprintf(stderr, "The heap is giving us unfreed pointers (0x%08X). Weird.\n", adr.address);
02976             hfAllocData.Remove(iDir);
02977             goto again;
02978         }
02979         iDir = hfAllocData.FindNextKey(iDir, nHash);
02980     }
02981 
02982     size_t TotalSpace;
02983     adr.fileline = AddSpaceToFileLine(file, line, size, &TotalSpace);
02984     hfAllocData.Insert(sizeof(adr), nHash, &adr);
02985     fprintf(stderr, "malloc %d bytes %s, %d\n  bringing total to %d bytes\n",
02986         size, file, line, TotalSpace);
02987 }
02988 
02989 void AccountForFree(void *pointer, const char *file, int line)
02990 {
02991     unsigned long nHash = HashPointer(pointer);
02992 
02993     bool bFound = false;
02994 again:
02995     HP_DIRINDEX iDir = hfAllocData.FindFirstKey(nHash);
02996     while (iDir != HF_FIND_END)
02997     {
02998         // We found it.
02999         //
03000         HP_HEAPLENGTH nRecord;
03001         AllocDataRec adr;
03002         hfAllocData.Copy(iDir, &nRecord, &adr);
03003         if (adr.address == pointer)
03004         {
03005             bFound = true;
03006             hfAllocData.Remove(iDir);
03007 
03008             DebugTotalMemory -= adr.size;
03009             size_t nSpace;
03010             int AllocLine;
03011             char filename[1024];
03012             if (SubtractSpaceFromFileLine(adr.fileline, adr.size, &nSpace, &AllocLine, filename))
03013             {
03014                 fprintf(stderr, "free %d bytes on %s, %d ...\n  allocated on %s, %d...\n  leaving total of %d bytes\n",
03015                     adr.size, file, line, filename, AllocLine, nSpace);
03016             }
03017             else
03018             {
03019                 fprintf(stderr, "free %d bytes on %s, %d ...\n  allocated on UNKNOWN\n", adr.size, file, line);
03020             }
03021             goto again;
03022         }
03023         iDir = hfAllocData.FindNextKey(iDir, nHash);
03024     }
03025 
03026     if (!bFound)
03027     {
03028         // Problems.
03029         //
03030         fprintf(stderr, "We are freeing unallocated pointers (0x%08X) on %s, %d\n", pointer, file, line);
03031     }
03032 }
03033 
03034 int iRecursion = 0;
03035 
03036 void *MemAllocate(size_t size, const char *file, int line)
03037 {
03038     void *vp = malloc(size);
03039     if (vp)
03040     {
03041         if (bMemAccountingInitialized)
03042         {
03043             if (iRecursion == 0)
03044             {
03045                 iRecursion++;
03046                 AccountForAllocation(vp, size, file, line);
03047                 iRecursion--;
03048             }
03049         }
03050         else
03051         {
03052             fprintf(stderr, "malloc not intitalized on %s, %d.\n", file, line);
03053         }
03054     }
03055     else
03056     {
03057         fprintf(stderr, "malloc ran out of memory on %s, %d.\n", file, line);
03058     }
03059     return vp;
03060 }
03061 
03062 void MemFree(void *pointer, const char *file, int line)
03063 {
03064     if (pointer)
03065     {
03066         if (bMemAccountingInitialized)
03067         {
03068             if (iRecursion == 0)
03069             {
03070                 iRecursion++;
03071                 AccountForFree(pointer, file, line);
03072                 iRecursion--;
03073             }
03074         }
03075         else
03076         {
03077             fprintf(stderr, "free called on %s, %d before initialized with 0x%08X\n", file, line, pointer);
03078         }
03079         free(pointer);
03080     }
03081     else
03082     {
03083         fprintf(stderr, "We tried to free(0) on %s, %d\n", file, line);
03084     }
03085 }
03086 
03087 void *MemRealloc(void *pointer, size_t size, const char *file, int line)
03088 {
03089     if (pointer)
03090     {
03091         AccountForFree(pointer, file, line);
03092     }
03093     void * vp = realloc(pointer, size);
03094     if (vp)
03095     {
03096         AccountForAllocation(vp, size, file, line);
03097     }
03098     return vp;
03099 }
03100 
03101 #endif // MEMORY_ACCOUNTING

Generated on Mon May 28 04:40:11 2007 for MUX by  doxygen 1.4.7