00001
00002
00003
00004
00005
00006
00007
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;
00017 int cs_reads = 0;
00018 int cs_dels = 0;
00019 int cs_fails = 0;
00020 int cs_syncs = 0;
00021 int cs_dbreads = 0;
00022 int cs_dbwrites = 0;
00023 int cs_whits = 0;
00024 int cs_rhits = 0;
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
00095
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
00305
00306
00307
00308 int iZone, iPrime;
00309 for (iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing)
00310 {
00311
00312
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
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
00338
00339 for (iPrime = 0; iPrime < 16; iPrime += 2)
00340 {
00341 HashPrimes[iPrime] = TableSize-HashPrimes[iPrime];
00342 }
00343
00344
00345
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
00398
00399
00400
00401
00402
00403
00404
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
00419
00420 for (UINT32 iDir = 0; iDir < m_pHeader->m_nDirSize; iDir++)
00421 {
00422 if (m_pDirectory[iDir] < HP_DIR_DELETED)
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
00440
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;
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
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;
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
00533
00534
00535
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
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
00550
00551
00552
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
00563
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
00578
00579 if (m_pHeader->m_oFreeList == HP_NIL_OFFSET)
00580 {
00581 return false;
00582 }
00583
00584
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
00591
00592
00593
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
00604
00605 m_pHeader->m_oFreeList = HP_NIL_OFFSET;
00606 }
00607 else
00608 {
00609 return true;
00610 }
00611 }
00612 return false;
00613 }
00614
00615
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
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
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
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
00702
00703
00704
00705
00706
00707
00708
00709 HP_DIRINDEX CHashPage::FindFirstKey(UINT32 nHash, unsigned int *numchecks)
00710 {
00711 #ifdef HP_PROTECTION
00712
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
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
00743
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
00782
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
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
00800
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)
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
00830
00831
00832 bool CHashPage::HeapAlloc(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, UINT32 nHash, void *pRecord)
00833 {
00834
00835 if (m_pDirectory[iDir] < HP_DIR_DELETED)
00836 {
00837 return false;
00838 }
00839
00840
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
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
00866
00867
00868
00869 HP_HEAPLENGTH nNewBlockSize = pNode->nBlockSize - nRequired;
00870 if (nNewBlockSize >= EXPAND_TO_BOUNDARY(HP_MIN_HEAP_ALLOC+1))
00871 {
00872
00873
00874 HP_PHEAPNODE pNewNode = (HP_PHEAPNODE)(pBlockStart + nRequired);
00875 pNewNode->nBlockSize = nNewBlockSize;
00876 pNewNode->u.oNext = pNode->u.oNext;
00877
00878
00879
00880 pNode->nBlockSize = nRequired;
00881 pNode->u.s.nHash = nHash;
00882 pNode->u.s.nRecordSize = nRecord;
00883
00884
00885
00886 *poPrev += nRequired;
00887 }
00888 else
00889 {
00890
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
00911
00912
00913 void CHashPage::HeapFree(HP_DIRINDEX iDir)
00914 {
00915
00916 if (m_pDirectory[iDir] < HP_DIR_DELETED)
00917 {
00918 HP_HEAPOFFSET oBlock = m_pDirectory[iDir];
00919 HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + oBlock);
00920
00921
00922
00923
00924
00925 HP_HEAPLENGTH nBlockSize = pNode->nBlockSize;
00926 memset(pNode, 0, nBlockSize);
00927 pNode->nBlockSize = nBlockSize;
00928
00929
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)
00942 {
00943 HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]);
00944
00945
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)
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
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
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)
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
01129
01130
01131 Log.tinyprintf("CHashPage::Write - partial write." ENDLINE);
01132 }
01133 }
01134 return true;
01135 }
01136
01137
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
01169
01170 Log.tinyprintf("CHashPage::Read - partial read." ENDLINE);
01171 }
01172 continue;
01173 }
01174 SetVariablePointers();
01175 return true;
01176 }
01177
01178
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
01191
01192
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
01205
01206 int nRecords;
01207 HP_HEAPLENGTH nAllocatedSize;
01208 int nGoodDirSize;
01209 GetStats(nExtra, &nRecords, &nAllocatedSize, &nGoodDirSize);
01210
01211
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)
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
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
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)
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)
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
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
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
01435
01436 if (!InitializeDirectory(2))
01437 {
01438 return false;
01439 }
01440
01441
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
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
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
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
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
01581
01582
01583
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
01598
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
01610
01611 CloseAll();
01612 return HF_OPEN_STATUS_ERROR;
01613 }
01614
01615
01616
01617
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
01629
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
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
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
01794
01795
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
01806
01807
01808 MuxAlarm.Sleep(time_1s);
01809 } while (mudstate.dumping);
01810 }
01811 #endif // !WIN32
01812
01813
01814
01815
01816 if (m_nDirDepth == m_Cache[iCache].m_hp.GetDepth())
01817 {
01818 if (!DoubleDirectory())
01819 {
01820 return false;
01821 }
01822 }
01823
01824
01825
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
01863
01864 long oNew = oEndOfFile;
01865 oEndOfFile += HF_SIZEOF_PAGE;
01866
01867
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
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
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
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
01956
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
02101
02102
02103 m_iOldest = m_Cache[m_iOldest].m_iYounger;
02104 }
02105 else if (iEntry == m_Cache[m_iOldest].m_iOlder)
02106 {
02107
02108
02109 }
02110 else
02111 {
02112
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
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
02150
02151
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
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
02280
02281 m_nMaxScan = 0;
02282 }
02283 break;
02284 }
02285 if (errInserted == HP_INSERT_ERROR_ILLEGAL)
02286 {
02287 return false;
02288 }
02289
02290
02291
02292
02293 if (m_nDirDepth == m_hpLast->GetDepth())
02294 {
02295 if (!DoubleDirectory())
02296 {
02297 return false;
02298 }
02299 }
02300
02301
02302
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
02327
02328 m_nMaxScan = 0;
02329
02330
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
02519
02520 for ( ; m_iPage < m_nDir; m_iPage++)
02521 {
02522
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
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
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
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
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