// svdhash.cpp -- CHashPage, CHashFile, CHashTable modules // // $Id: svdhash.cpp,v 1.13 2000/09/20 19:28:51 sdennis Exp $ // // MUX 2.0 // Copyright (C) 1998 through 2000 Solid Vertical Domains, Ltd. All // rights not explicitly given are reserved. Permission is given to // use this code for building and hosting text-based game servers. // Permission is given to use this code for other non-commercial // purposes. To use this code for commercial purposes other than // building/hosting text-based game servers, contact the author at // Stephen Dennis <sdennis@svdltd.com> for another license. // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #ifndef STANDALONE #define DO_COMMIT #endif #define HF_SIZEOF_PAGE 24576 #define HT_SIZEOF_PAGE 8192 int cs_writes = 0; // total writes int cs_reads = 0; // total reads int cs_dels = 0; // total deletes int cs_fails = 0; // attempts to grab nonexistent int cs_syncs = 0; // total cache syncs int cs_dbreads = 0; // total read-throughs int cs_dbwrites = 0; // total write-throughs int cs_whits = 0; // writes into cached pages int cs_rhits = 0; // read from cached pages static unsigned long CRC32_Table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; #define CRC32_INITIAL 0xFFFFFFFFUL #if 1 #ifdef WIN32 // Proper CRC-32 routines. // // Visual C++ generates an inner loop that is 38 instructions per 16 data // bytes or 2.375 instructions per byte. Or, 13.62ns per byte on my // Pentium II 400. That's 73.41 MB/second. // #define CRC32_VALID_RESULT 0x2144DF1CUL unsigned long CRC32_ProcessBuffer ( unsigned long ulCrc, const void *arg_pBuffer, unsigned int nBuffer ) { ulCrc = ~ulCrc; unsigned char *pBuffer = (unsigned char *)arg_pBuffer; JustAfew: if (nBuffer <= 8) { pBuffer -= 8 - nBuffer; switch (nBuffer) { case 8: ulCrc ^= *(unsigned long *)(pBuffer + 0); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc ^= *(unsigned long *)(pBuffer + 4); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); return ~ulCrc; case 7: ulCrc = CRC32_Table[pBuffer[1] ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 6: ulCrc = CRC32_Table[pBuffer[2] ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 5: ulCrc = CRC32_Table[pBuffer[3] ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 4: ulCrc ^= *(unsigned long *)(pBuffer + 4); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); return ~ulCrc; case 3: ulCrc = CRC32_Table[pBuffer[5] ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 2: ulCrc = CRC32_Table[pBuffer[6] ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 1: ulCrc = CRC32_Table[pBuffer[7] ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 0: return ~ulCrc; } } // We may need to do some alignment work up front, and at the end, so that // the main loop is aligned and only has to worry about 8 byte at a time. // // The low-order two bits of pBuffer and nBuffer in total control the // upfront work. // unsigned int nFront = (int)(pBuffer) & 3; nBuffer -= nFront; switch (nFront) { case 3: ulCrc = CRC32_Table[*pBuffer++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 2: ulCrc = CRC32_Table[*pBuffer++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); case 1: ulCrc = CRC32_Table[*pBuffer++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); } int nMain = nBuffer >> 3; while (nMain) { nMain--; ulCrc ^= *(unsigned long *)pBuffer; ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc ^= *(unsigned long *)(pBuffer + 4); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); pBuffer += 8; } nBuffer &= 7; goto JustAfew; } #else // WIN32 // Portable CRC-32 routines. These slower routines are less compiler and // platform dependent and still get the job done. // unsigned long CRC32_ProcessBuffer ( unsigned long ulCrc, const void *arg_pBuffer, unsigned int nBuffer ) { unsigned char *pBuffer = (unsigned char *)arg_pBuffer; ulCrc = ~ulCrc; while (nBuffer) { nBuffer--; ulCrc = CRC32_Table[((unsigned char)*pBuffer++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); } return ~ulCrc; } #endif // WIN32 unsigned long CRC32_ProcessString(const void *szString) { unsigned char *pBuffer = (unsigned char *)szString; unsigned char ch; unsigned long ulCrc = CRC32_INITIAL; ch = *pBuffer++; while (ch) { ulCrc = CRC32_Table[ch ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); ch = *pBuffer++; } return ~ulCrc; } #else #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); unsigned long CRC32_ProcessBuffer ( unsigned long ulHash, const void *arg_pBuffer, unsigned int nBuffer ) { char *pBuffer = (char *)arg_pBuffer; ulHash = ~ulHash; if (nBuffer <= 16) { pBuffer -= 16 - nBuffer; switch (nBuffer) { case 16: ulHash = CRC32_Table[pBuffer[0] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 15: ulHash = CRC32_Table[pBuffer[1] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 14: ulHash = CRC32_Table[pBuffer[2] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 13: ulHash = CRC32_Table[pBuffer[3] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 12: ulHash = CRC32_Table[pBuffer[4] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 11: ulHash = CRC32_Table[pBuffer[5] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 10: ulHash = CRC32_Table[pBuffer[6] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 9: ulHash = CRC32_Table[pBuffer[7] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 8: ulHash ^= *(unsigned long *)(pBuffer + 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash ^= *(unsigned long *)(pBuffer + 12); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); return ~ulHash; case 7: ulHash = CRC32_Table[pBuffer[9] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 6: ulHash = CRC32_Table[pBuffer[10] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 5: ulHash = CRC32_Table[pBuffer[11] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 4: ulHash ^= *(unsigned long *)(pBuffer + 12); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); return ~ulHash; case 3: ulHash = CRC32_Table[pBuffer[13] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 2: ulHash = CRC32_Table[pBuffer[14] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 1: ulHash = CRC32_Table[pBuffer[15] ^ (unsigned char)ulHash] ^ (ulHash >> 8); case 0: return ~ulHash; } } int nSmall = nBuffer & 15; int nMedium = (nBuffer >> 4) & 255; int nLarge = nBuffer >> 12; unsigned long s1 = ulHash & 0xFFFF; unsigned long s2 = (ulHash >> 16) & 0xFFFF; while (nLarge) { int k = 256; while (k) { DO16(pBuffer); pBuffer += 16; k--; } nLarge--; ulHash = ~s1; ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash ^= s2; ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = ~ulHash; s1 = ulHash & 0xFFFF; s2 = (ulHash >> 16) & 0xFFFF; } while (nMedium) { DO16(pBuffer); pBuffer += 16; nMedium--; } pBuffer -= 15 - nSmall; switch (nSmall) { case 15: s1 += pBuffer[0]; s2 += s1; case 14: s1 += pBuffer[1]; s2 += s1; case 13: s1 += pBuffer[2]; s2 += s1; case 12: s1 += pBuffer[3]; s2 += s1; case 11: s1 += pBuffer[4]; s2 += s1; case 10: s1 += pBuffer[5]; s2 += s1; case 9: s1 += pBuffer[6]; s2 += s1; case 8: s1 += pBuffer[7]; s2 += s1; case 7: s1 += pBuffer[8]; s2 += s1; case 6: s1 += pBuffer[9]; s2 += s1; case 5: s1 += pBuffer[10]; s2 += s1; case 4: s1 += pBuffer[11]; s2 += s1; case 3: s1 += pBuffer[12]; s2 += s1; case 2: s1 += pBuffer[13]; s2 += s1; case 1: s1 += pBuffer[14]; s2 += s1; } ulHash = ~s1; ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash ^= s2; ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); ulHash = CRC32_Table[(unsigned char)ulHash] ^ (ulHash >> 8); return ~ulHash; } unsigned long CRC32_ProcessString(const void *szString) { int nBuffer = strlen((char *)szString); return CRC32_ProcessBuffer(0, szString, nBuffer); } #endif unsigned long CRC32_ProcessInteger(unsigned int nInteger) { unsigned long ulCrc = CRC32_INITIAL; ulCrc ^= nInteger; ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); return ~ulCrc; } unsigned long CRC32_ProcessInteger2(unsigned int nInteger1, unsigned int nInteger2) { unsigned long ulCrc = CRC32_INITIAL; ulCrc ^= nInteger1; ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc ^= nInteger2; ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); ulCrc = CRC32_Table[(unsigned char)ulCrc] ^ (ulCrc >> 8); return ~ulCrc; } #define NUMBER_OF_PRIMES 177 int Primes[NUMBER_OF_PRIMES] = { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 0 }; void ChoosePrimes(int TableSize, HP_HEAPOFFSET HashPrimes[16]) { int LargestPrime = TableSize/2; if (LargestPrime > Primes[NUMBER_OF_PRIMES-2]) { LargestPrime = Primes[NUMBER_OF_PRIMES-2]; } int Spacing = LargestPrime/16; // Pick a set primes that are evenly spaced from (0 to LargestPrime) // We divide this interval into 16 equal sized zones. We want to find // one prime number that best represents that zone. // int iZone, iPrime; for (iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing) { // Search for a prime number that is less than the target zone // number given by iZone. // int Lower = Primes[0]; for (int jPrime = 0; Primes[jPrime] != 0; jPrime++) { if (jPrime != 0 && TableSize % Primes[jPrime] == 0) continue; int Upper = Primes[jPrime]; if (Lower <= iZone && iZone <= Upper) { // Choose the closest lower prime number. // if (iZone - Lower <= Upper - iZone) { HashPrimes[iPrime++] = Lower; } else { HashPrimes[iPrime++] = Upper; } break; } Lower = Upper; } } // Alternate negative and positive numbers // for (iPrime = 0; iPrime < 16; iPrime += 2) { HashPrimes[iPrime] = TableSize-HashPrimes[iPrime]; } // Shuffle the set of primes to reduce correlation with bits in // hash key. // for (iPrime = 0; iPrime < 16-1; iPrime++) { int Pick = (int)RandomLong(0, 15-iPrime); HP_HEAPOFFSET Temp = HashPrimes[Pick]; HashPrimes[Pick] = HashPrimes[15-iPrime]; HashPrimes[15-iPrime] = Temp; } } static unsigned long anGroupMask[33] = { 0x00000000UL, 0x80000000UL, 0xC0000000UL, 0xE0000000UL, 0xF0000000UL, 0xF8000000UL, 0xFC000000UL, 0xFE000000UL, 0xFF000000UL, 0xFF800000UL, 0xFFC00000UL, 0xFFE00000UL, 0xFFF00000UL, 0xFFF80000UL, 0xFFFC0000UL, 0xFFFE0000UL, 0xFFFF0000UL, 0xFFFF8000UL, 0xFFFFC000UL, 0xFFFFE000UL, 0xFFFFF000UL, 0xFFFFF800UL, 0xFFFFFC00UL, 0xFFFFFE00UL, 0xFFFFFF00UL, 0xFFFFFF80UL, 0xFFFFFFC0UL, 0xFFFFFFE0UL, 0xFFFFFFF0UL, 0xFFFFFFF8UL, 0xFFFFFFFCUL, 0xFFFFFFFEUL, 0xFFFFFFFFUL }; BOOL CHashPage::Allocate(unsigned int nPageSize) { if (m_nPageSize) return FALSE; m_nPageSize = nPageSize; m_pPage = new unsigned char[nPageSize]; if (m_pPage) { return TRUE; } return FALSE; } CHashPage::CHashPage(void) { m_nPageSize = 0; m_pPage = 0; } CHashPage::~CHashPage(void) { if (m_pPage) { delete m_pPage; m_pPage = 0; } } // GetStats // // This functions returns the number of records in this hash page and the // number of bytes that these records would take up in a fresh page. // // It also tries to leave room for a record of size nExtra. // // This function is useful for reallocating the page // void CHashPage::GetStats(HP_HEAPLENGTH nExtra, int *pnRecords, HP_HEAPLENGTH *pnAllocatedSize, int *pnGoodDirSize) { unsigned nSize = 0; unsigned nCount = 0; unsigned nGoodDirSize = 100; // Count and measure all the records in this page. // for (unsigned long iDir = 0; iDir < m_pHeader->m_nDirSize; iDir++) { if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { nCount++; HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]); HP_HEAPLENGTH nRequired = EXPAND_TO_BOUNDARY( HP_SIZEOF_HEAPNODE + pNode->u.s.nRecordSize); if (nRequired < HP_MIN_HEAP_ALLOC) { nRequired = HP_MIN_HEAP_ALLOC; } nSize += nRequired; } } *pnRecords = nCount; *pnAllocatedSize = nSize; // If we have records to talk about, or even if we are trying to reserve // space, then do the math. // if (nExtra != 0 || nCount != 0) { unsigned long nSpace = ((unsigned char *)m_pTrailer) - ((unsigned char *)m_pDirectory); unsigned long nMinDirSize = nCount; unsigned long nMaxDirSize = (nSpace - nSize)/sizeof(HP_HEAPOFFSET); if (nExtra) { nExtra += HP_SIZEOF_HEAPNODE; if (nExtra < HP_MIN_HEAP_ALLOC) { nExtra = HP_MIN_HEAP_ALLOC; } nExtra = EXPAND_TO_BOUNDARY(nExtra); nCount++; nSize += nExtra; } #define FILL_FACTOR 1 unsigned long nAverageSize = (nSize + nCount/2)/nCount; unsigned long nHeapGoal = (nSpace * nAverageSize)/(nAverageSize + sizeof(HP_HEAPOFFSET) + FILL_FACTOR); nGoodDirSize = (unsigned long)((nSpace - nHeapGoal + sizeof(HP_HEAPOFFSET)/2)/sizeof(HP_HEAPOFFSET)); if (nGoodDirSize < nMinDirSize) { nGoodDirSize = nMinDirSize; } else if (nGoodDirSize > nMaxDirSize) { nGoodDirSize = nMaxDirSize; } } *pnGoodDirSize = nGoodDirSize; } void CHashPage::SetFixedPointers(void) { m_pHeader = (HP_PHEADER)m_pPage; m_pDirectory = (HP_PHEAPOFFSET)(m_pHeader+1); m_pTrailer = (HP_PTRAILER)(m_pPage + m_nPageSize - sizeof(HP_TRAILER)); } void CHashPage::Empty(HP_DIRINDEX arg_nDepth, unsigned long arg_nHashGroup, HP_DIRINDEX arg_nDirSize) { memset(m_pPage, 0, m_nPageSize); SetFixedPointers(); m_pHeader->m_nDepth = arg_nDepth; m_pHeader->m_nDirSize = arg_nDirSize; m_pHeader->m_nHashGroup = arg_nHashGroup; m_pHeader->m_nTotalInsert = 0; m_pHeader->m_nDirEmptyLeft = arg_nDirSize; // Number of entries marked HP_DIR_EMPTY. if (arg_nDirSize > 0) { ChoosePrimes(arg_nDirSize, m_pHeader->m_Primes); for (unsigned long iDir = 0; iDir < arg_nDirSize; iDir++) { m_pDirectory[iDir] = HP_DIR_EMPTY; } } SetVariablePointers(); // Setup initial free list. // HP_PHEAPNODE pNode = (HP_PHEAPNODE)m_pHeapStart; pNode->nBlockSize = m_pHeapEnd - m_pHeapStart; pNode->u.oNext = HP_NIL_OFFSET; m_pHeader->m_oFreeList = 0; // This is intentionally zero (i.e., m_pHeapStart - m_pHeapStart). } #ifdef HP_PROTECTION void CHashPage::Protect(void) { unsigned long ul = CRC32_ProcessBuffer(0, m_pPage, m_nPageSize-sizeof(HP_TRAILER)); m_pTrailer->m_crc32 = ul; } BOOL CHashPage::Validate(void) { unsigned long ul = CRC32_ProcessBuffer(0, m_pPage, m_nPageSize); if (ul != CRC32_VALID_RESULT) { return FALSE; } return TRUE; } // ValidateBlock. // // This function validates a block associated with a particular // Dir entry and blows that entry away if it's suspect. // BOOL CHashPage::ValidateAllocatedBlock(unsigned long iDir) { if (iDir >= m_pHeader->m_nDirSize) return FALSE; if (m_pDirectory[iDir] >= HP_DIR_DELETED) return FALSE; // Use directory entry to go find heap node. The record itself follows. // unsigned char *pBlockStart = m_pHeapStart + m_pDirectory[iDir]; unsigned char *pBlockEnd = pBlockStart + HP_MIN_HEAP_ALLOC; if (pBlockStart < m_pHeapStart || m_pHeapEnd <= pBlockEnd) { // Wow. We have a problem here. There is no good way of // finding this record anymore, so just mark it as // deleted. A sweep of the heap will reclaim any lost // free space. // m_pDirectory[iDir] = HP_DIR_DELETED; } else { HP_PHEAPNODE pNode = (HP_PHEAPNODE)pBlockStart; pBlockEnd = pBlockStart + pNode->nBlockSize; if (m_pHeapEnd < pBlockEnd || pNode->u.s.nRecordSize > pNode->nBlockSize) { // Wow. Record hangs off the end of the heap space, or the record // is larger than the block that holds it. // m_pDirectory[iDir] = HP_DIR_DELETED; } else { return TRUE; } } return FALSE; } BOOL CHashPage::ValidateFreeBlock(HP_HEAPOFFSET oBlock) { // If the free list is empty, then this can't be a valid free block. // if (m_pHeader->m_oFreeList == HP_NIL_OFFSET) { return FALSE; } // Go find heap node. The record itself follows. // unsigned char *pBlockStart = m_pHeapStart + oBlock; unsigned char *pBlockEnd = pBlockStart + HP_MIN_HEAP_ALLOC; if (pBlockStart < m_pHeapStart || m_pHeapEnd < pBlockEnd) { // Wow. We have a problem here. There is no good way of // finding this record anymore, so just empty the free list // and hope to either rehash the page into a new page, or // sweep the heap and re-establish the free list. // m_pHeader->m_oFreeList = HP_NIL_OFFSET; } else { HP_PHEAPNODE pNode = (HP_PHEAPNODE)pBlockStart; pBlockEnd = pBlockStart + pNode->nBlockSize; if (m_pHeapEnd < pBlockEnd) { // Wow. Record hangs off the end of the heap space. // m_pHeader->m_oFreeList = HP_NIL_OFFSET; } else { return TRUE; } } return FALSE; } // ValidateFreeList - Checks the validity of the free list // BOOL CHashPage::ValidateFreeList(void) { HP_HEAPOFFSET oCurrent = m_pHeader->m_oFreeList; while (oCurrent != HP_NIL_OFFSET) { if (ValidateFreeBlock(oCurrent)) { HP_PHEAPNODE pCurrent = (HP_PHEAPNODE)(m_pHeapStart + oCurrent); if (oCurrent >= pCurrent->u.oNext) { Log.WriteString("CHashPage::ValidateFreeList - Free list is corrupt.\n"); m_pHeader->m_oFreeList = HP_NIL_OFFSET; return FALSE; } oCurrent = pCurrent->u.oNext; } else { Log.WriteString("CHashPage::ValidateFreeList - Free list is corrupt.\n"); m_pHeader->m_oFreeList = HP_NIL_OFFSET; return FALSE; } } return TRUE; } #endif // Insert - Inserts a new record if there is room. // int CHashPage::Insert(HP_HEAPLENGTH nRecord, unsigned long nHash, void *pRecord) { m_pHeader->m_nTotalInsert++; for (int nTries = 0; nTries < 2; nTries++) { #ifdef HP_PROTECTION // First, is this page dealing with keys like this at all? // HP_DIRINDEX nDepth = m_pHeader->m_nDepth; if ((nHash & anGroupMask[nDepth]) != m_pHeader->m_nHashGroup) { Log.WriteString("CHashPage::Insert - Inserting into the wrong page.\n"); return HP_INSERT_ERROR_ILLEGAL; } #endif // Where do we begin our first probe? // int di = m_pHeader->m_Primes[nHash & 15]; int iDir = (nHash >> 4) % (m_pHeader->m_nDirSize); m_nProbesLeft = m_pHeader->m_nDirSize; while (m_nProbesLeft-- && (m_pDirectory[iDir] < HP_DIR_DELETED)) { iDir += di; if (iDir >= (m_pHeader->m_nDirSize)) { iDir -= (m_pHeader->m_nDirSize); } } if (m_nProbesLeft >= 0) { if (m_pHeader->m_nDirEmptyLeft < m_nDirEmptyTrigger) { if (!Defrag(nRecord)) return HP_INSERT_ERROR_FULL; continue; } if (HeapAlloc(iDir, nRecord, nHash, pRecord)) return HP_INSERT_SUCCESS; } if (!Defrag(nRecord)) return HP_INSERT_ERROR_FULL; } return HP_INSERT_ERROR_FULL; } // Find - Finds the first record with the given hash key and returns its // directory index or HP_DIR_EMPTY if no hash keys are found. // // Call iDir = FindFirstKey(hash) the first time, and then call // iDir = FindNextKey(iDir, hash) every time after than until iDir == HP_DIR_EMPTY // to interate through all the records with the desired hash key. // HP_DIRINDEX CHashPage::FindFirstKey(unsigned long nHash, unsigned int *numchecks) { #ifdef HP_PROTECTION // First, is this page dealing with keys like this at all? // HP_DIRINDEX nDepth = m_pHeader->m_nDepth; if ((nHash & anGroupMask[nDepth]) != m_pHeader->m_nHashGroup) return HP_DIR_EMPTY; #endif int nDirSize = m_pHeader->m_nDirSize; // Where do we begin our first probe? // int di = m_pHeader->m_Primes[nHash & 15]; int iDir = (nHash >> 4) % nDirSize; m_nProbesLeft = nDirSize; int sOffset = m_pDirectory[iDir]; while (sOffset != HP_DIR_EMPTY) { m_nProbesLeft--; if (sOffset != HP_DIR_DELETED) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + sOffset); if (pNode->u.s.nHash == nHash) { *numchecks = nDirSize - m_nProbesLeft; return iDir; } } if (!m_nProbesLeft) break; iDir += di; if (iDir >= nDirSize) { iDir -= nDirSize; } sOffset = m_pDirectory[iDir]; } *numchecks = nDirSize - m_nProbesLeft; return HP_DIR_EMPTY; } // Find - Finds the next record with the given hash key and returns its // directory index or HP_DIR_EMPTY if no hash keys are found. // // HP_DIRINDEX CHashPage::FindNextKey(HP_DIRINDEX iDir, unsigned long nHash, unsigned int *numchecks) { *numchecks = 0; #ifdef HP_PROTECTION // First, is this page dealing with keys like this at all? // HP_DIRINDEX nDepth = m_pHeader->m_nDepth; if ((nHash & anGroupMask[nDepth]) != m_pHeader->m_nHashGroup) return HP_DIR_EMPTY; #endif int nDirSize = m_pHeader->m_nDirSize; // Where do we begin our first probe? If this is the first call, i will be HP_DIR_EMPTY. // On calls after that, it will be what we returned on the previous call. // int di = m_pHeader->m_Primes[nHash & 15]; iDir += di; if (iDir >= nDirSize) { iDir -= nDirSize; } while (m_nProbesLeft && (m_pDirectory[iDir] != HP_DIR_EMPTY)) { m_nProbesLeft--; (*numchecks)++; if (m_pDirectory[iDir] != HP_DIR_DELETED) { if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]); if (pNode->u.s.nHash == nHash) return iDir; } } iDir += di; if (iDir >= nDirSize) { iDir -= nDirSize; } } return HP_DIR_EMPTY; } // HeapAlloc - Return true if there was enough room to copy the record into the heap, otherwise, // it returns false. // BOOL CHashPage::HeapAlloc(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, unsigned long nHash, void *pRecord) { //ValidateFreeList(); if (m_pDirectory[iDir] < HP_DIR_DELETED) { return FALSE; } // How much space do we need? // HP_HEAPLENGTH nRequired = EXPAND_TO_BOUNDARY(HP_SIZEOF_HEAPNODE + nRecord); if (nRequired < HP_MIN_HEAP_ALLOC) { nRequired = HP_MIN_HEAP_ALLOC; } // Search through the free list for something of the right size. // HP_HEAPOFFSET oNext = m_pHeader->m_oFreeList; HP_PHEAPOFFSET poPrev = &(m_pHeader->m_oFreeList); while (oNext != HP_NIL_OFFSET) { #if 0 if (!ValidateFreeBlock(oPrevious)) { ValidateFreeList(); return FALSE; } #endif unsigned char *pBlockStart = m_pHeapStart + oNext; HP_PHEAPNODE pNode = (HP_PHEAPNODE)pBlockStart; if (pNode->nBlockSize >= nRequired) { // We found something of the correct size. // // Do we cut it into two blocks or take the whole thing? // HP_HEAPLENGTH nNewBlockSize = pNode->nBlockSize - nRequired; if (nNewBlockSize >= EXPAND_TO_BOUNDARY(HP_MIN_HEAP_ALLOC+1)) { // There is enough for leftovers, split it. // HP_PHEAPNODE pNewNode = (HP_PHEAPNODE)(pBlockStart + nRequired); pNewNode->nBlockSize = nNewBlockSize; pNewNode->u.oNext = pNode->u.oNext; // Update current node. // pNode->nBlockSize = nRequired; pNode->u.s.nHash = nHash; pNode->u.s.nRecordSize = nRecord; // Update Free list pointer. // *poPrev += nRequired; } else { // Take the whole thing. // *poPrev = pNode->u.oNext; pNode->u.s.nHash = nHash; pNode->u.s.nRecordSize = nRecord; } memcpy(pNode+1, pRecord, nRecord); if (m_pDirectory[iDir] == HP_DIR_EMPTY) { m_pHeader->m_nDirEmptyLeft--; } m_pDirectory[iDir] = (HP_HEAPOFFSET)(pBlockStart - m_pHeapStart); return TRUE; } poPrev = &(pNode->u.oNext); oNext = pNode->u.oNext; } return FALSE; } // HeapFree - Returns to the heap the space for the record associated with iDir. It // always succeeds even if there wasn't a record there to delete. // void CHashPage::HeapFree(HP_DIRINDEX iDir) { //ValidateFreeList(); if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_HEAPOFFSET oBlock = m_pDirectory[iDir]; HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + oBlock); // Clear it. The reason for clearing is that it makes debugging easier, // and also, if the file is compressed by the file system, a string // of zeros will yield a smaller result. // HP_HEAPLENGTH nBlockSize = pNode->nBlockSize; memset(pNode, 0, nBlockSize); pNode->nBlockSize = nBlockSize; // Push it onto the free list. // pNode->u.oNext = m_pHeader->m_oFreeList; m_pHeader->m_oFreeList = oBlock; m_pDirectory[iDir] = HP_DIR_DELETED; } } void CHashPage::HeapCopy(HP_DIRINDEX iDir, HP_PHEAPLENGTH pnRecord, void *pRecord) { if (pnRecord == 0 || pRecord == 0) return; if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]); // Copy the record. // *pnRecord = pNode->u.s.nRecordSize; memcpy(pRecord, pNode+1, pNode->u.s.nRecordSize); } } void CHashPage::HeapUpdate(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, void *pRecord) { if (nRecord == 0 || pRecord == 0) return; if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]); if (pNode->u.s.nRecordSize != nRecord) return; memcpy(pNode+1, pRecord, nRecord); } } BOOL CHashPage::Split(CHashPage &hp0, CHashPage &hp1) { // Figure out what a good directory size is given the actual records in this page. // int nRecords; HP_HEAPLENGTH nAllocatedSize; int nGoodDirSize; GetStats(0, &nRecords, &nAllocatedSize, &nGoodDirSize); if (nRecords == 0) { Log.WriteString("Why are we splitting a page with no records in it?\n"); return FALSE; } // Initialize that type of HashPage and copy records over. // int nNewDepth = m_pHeader->m_nDepth + 1; unsigned long nBitMask = 1 << (32-nNewDepth); unsigned long nHashGroup0 = m_pHeader->m_nHashGroup & (~nBitMask); unsigned long nHashGroup1 = nHashGroup0 | nBitMask; hp0.Empty(nNewDepth, nHashGroup0, nGoodDirSize); hp1.Empty(nNewDepth, nHashGroup1, nGoodDirSize); for (int iDir = 0; iDir < m_pHeader->m_nDirSize; iDir++) { if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]); unsigned long nHash = pNode->u.s.nHash; if ((nHash & anGroupMask[nNewDepth]) == (nHashGroup0 & anGroupMask[nNewDepth])) { if (HP_INSERT_SUCCESS != hp0.Insert(pNode->u.s.nRecordSize, nHash, pNode+1)) { Log.WriteString("CHashPage::Split - Ran out of room.\n"); return FALSE; } } else if ((nHash & anGroupMask[nNewDepth]) == (nHashGroup1 & anGroupMask[nNewDepth])) { if (HP_INSERT_SUCCESS != hp1.Insert(pNode->u.s.nRecordSize, nHash, pNode+1)) { Log.WriteString("CHashPage::Split - Ran out of room.\n"); return FALSE; } } else { Log.WriteString("CHashPage::Split - This record fits in neither page...lost.\n"); return FALSE; } } } #if 0 unsigned long nRecords0, nRecords1; unsigned long nAllocatedSize0, nAllocatedSize1; unsigned long temp; hp0.GetStats(0, &nRecords0, &nAllocatedSize0, &temp); hp1.GetStats(0, &nRecords1, &nAllocatedSize1, &temp); Log.printf("Split (%d %d) page into (%d %d) and (%d %d)\n", nRecords, nAllocatedSize, nRecords0, nAllocatedSize0, nRecords1, nAllocatedSize1); if (nRecords0 + nRecords1 != nRecords) { Log.WriteString("Lost something\n"); return FALSE; } #endif return TRUE; } void CHashPage::GetRange ( unsigned long arg_nDirDepth, unsigned long &nStart, unsigned long &nEnd ) { unsigned long nBase = 0; int nShift = 32 - arg_nDirDepth; if (arg_nDirDepth > 0) { nBase = m_pHeader->m_nHashGroup >> nShift; } unsigned long ulMask = anGroupMask[nShift + m_pHeader->m_nDepth]; nStart = nBase & ulMask; nEnd = nBase | ~ulMask; } #ifdef WIN32 BOOL CHashPage::WritePage(HANDLE hFile, HF_FILEOFFSET oWhere) { cs_dbwrites++; for ( ; ; Sleep(250)) { if (SetFilePointer(hFile, oWhere, 0, FILE_BEGIN) == 0xFFFFFFFFUL) { Log.printf("CHashPage::Write - SetFilePointer error %u.\n", GetLastError()); continue; } DWORD nWritten; if (!WriteFile(hFile, m_pPage, m_nPageSize, &nWritten, 0) || nWritten != m_nPageSize) { unsigned long cc = GetLastError(); if (cc != ERROR_LOCK_VIOLATION) { Log.printf("CHashPage::Write - WriteFile error %u.\n", cc); } continue; } return TRUE; } #ifndef STANDALONE // Don't struggle further. // You'll just make it worse. // mudstate.shutdown_flag = 1; #endif // STANDALONE return FALSE; } BOOL CHashPage::ReadPage(HANDLE hFile, HF_FILEOFFSET oWhere) { cs_dbreads++; SetFixedPointers(); for ( ; ; Sleep(250)) { if (SetFilePointer(hFile, oWhere, 0, FILE_BEGIN) == 0xFFFFFFFFUL) { Log.printf("CHashPage::Read - SetFilePointer error %u.\n", GetLastError()); continue; } DWORD nRead; if (!ReadFile(hFile, m_pPage, m_nPageSize, &nRead, 0) || nRead != m_nPageSize) { unsigned long cc = GetLastError(); if (cc != ERROR_LOCK_VIOLATION) { Log.printf("CHashPage::Read - ReadFile error %u.\n", cc); } continue; } SetVariablePointers(); return TRUE; } #ifndef STANDALONE // Don't struggle further. // You'll just make it worse. // mudstate.shutdown_flag = 1; #endif // STANDALONE return FALSE; } #else // WIN32 BOOL CHashPage::WritePage(HANDLE hFile, HF_FILEOFFSET oWhere) { cs_dbwrites++; int cnt = 60; for ( ; cnt; sleep(1), cnt--) { if (lseek(hFile, oWhere, SEEK_SET) == (off_t)-1) { Log.printf("CHashPage::Write - lseek error %u.\n", errno); continue; } int cc = write(hFile, m_pPage, m_nPageSize); if ((int)m_nPageSize != cc) { if (cc == -1) { Log.printf("CHashPage::Write - write error %u.\n", errno); } else { // Our write request was only partially filled. The disk is // probably full. // Log.printf("CHashPage::Write - partial write.\n"); } } return TRUE; } #ifndef STANDALONE // Don't struggle further. // You'll just make it worse. // mudstate.shutdown_flag = 1; #endif // STANDALONE return FALSE; } BOOL CHashPage::ReadPage(HANDLE hFile, HF_FILEOFFSET oWhere) { cs_dbreads++; SetFixedPointers(); int cnt = 60; for ( ; cnt; sleep(1), cnt--) { if (lseek(hFile, oWhere, SEEK_SET) == (off_t)-1) { Log.printf("CHashPage::Read - lseek error %u.\n", errno); continue; } int cc = read(hFile, m_pPage, m_nPageSize); if ((int)m_nPageSize != cc) { if (cc == -1) { Log.printf("CHashPage::Read - read error %u.\n", errno); } else { // Our read request was only partially filled. Surrender. // Log.printf("CHashPage::Read - partial read.\n"); } continue; } SetVariablePointers(); return TRUE; } #ifndef STANDALONE // Don't struggle further. // You'll just make it worse. // mudstate.shutdown_flag = 1; #endif // STANDALONE return FALSE; } #endif // WIN32 HP_DIRINDEX CHashPage::GetDepth(void) { return m_pHeader->m_nDepth; } // Defrag // // Moves all the records together, and re-establishes a single-element free list at the end. // BOOL CHashPage::Defrag(HP_HEAPLENGTH nExtra) { CHashPage *hpNew = new CHashPage; if (!hpNew) return FALSE; if (!hpNew->Allocate(m_nPageSize)) { delete hpNew; return FALSE; } // Figure out what a good directory size is given the actual records in this page. // int nRecords; HP_HEAPLENGTH nAllocatedSize; int nGoodDirSize; GetStats(nExtra, &nRecords, &nAllocatedSize, &nGoodDirSize); // Initialize that type of HashPage and copy records over. // hpNew->Empty(m_pHeader->m_nDepth, m_pHeader->m_nHashGroup, nGoodDirSize); BOOL errInserted = HP_INSERT_SUCCESS; for (int iDir = 0; iDir < m_pHeader->m_nDirSize && errInserted == HP_INSERT_SUCCESS; iDir++) { if (m_pDirectory[iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[iDir]); errInserted = hpNew->Insert(pNode->u.s.nRecordSize, pNode->u.s.nHash, pNode+1); } } if (errInserted == HP_INSERT_SUCCESS) { // Swap buffers. // unsigned char *tmp; tmp = hpNew->m_pPage; hpNew->m_pPage = m_pPage; m_pPage = tmp; SetFixedPointers(); SetVariablePointers(); delete hpNew; return TRUE; } delete hpNew; return FALSE; } void CHashPage::SetVariablePointers(void) { m_pHeapStart = (unsigned char *)(m_pDirectory + m_pHeader->m_nDirSize); m_pHeapEnd = (unsigned char *)(m_pTrailer); // If less than 14.29% of the entries are empty, then do another Defrag. // m_nDirEmptyTrigger = (m_pHeader->m_nDirSize)/7; } HP_DIRINDEX CHashPage::FindFirst(HP_PHEAPLENGTH pnRecord, void *pRecord) { for (m_iDir = 0; m_iDir < m_pHeader->m_nDirSize; m_iDir++) { if (m_pDirectory[m_iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[m_iDir]); *pnRecord = pNode->u.s.nRecordSize; memcpy(pRecord, pNode+1, pNode->u.s.nRecordSize); return m_iDir; } } return HP_DIR_EMPTY; } HP_DIRINDEX CHashPage::FindNext(HP_PHEAPLENGTH pnRecord, void *pRecord) { for ( m_iDir++; m_iDir < m_pHeader->m_nDirSize; m_iDir++) { if (m_pDirectory[m_iDir] < HP_DIR_DELETED) // ValidateAllocatedBlock(iDir)) { HP_PHEAPNODE pNode = (HP_PHEAPNODE)(m_pHeapStart + m_pDirectory[m_iDir]); *pnRecord = pNode->u.s.nRecordSize; memcpy(pRecord, pNode+1, pNode->u.s.nRecordSize); return m_iDir; } } return HP_DIR_EMPTY; } CHashFile::CHashFile(void) { SeedRandomNumberGenerator(); for (int i = 0; i < HF_PAGES; i++) { m_Cache[i].m_hp.Allocate(HF_SIZEOF_PAGE); } Init(); } void CHashFile::Init(void) { m_hDirFile = INVALID_HANDLE_VALUE; m_hPageFile = INVALID_HANDLE_VALUE; m_nDir = 0; m_nDirDepth = 0; m_pDir = NULL; m_iAgeNext = 0; iCache = 0; m_iLastEmpty = 0; m_iLastFlushed = 0; for (int i = 0; i < HF_PAGES; i++) { m_Cache[i].m_iState = HF_CACHE_EMPTY; m_Cache[i].m_o = 0L; m_Cache[i].m_Age = 0; } memset(m_hpCacheLookup, 0, sizeof(m_hpCacheLookup)); } #ifdef WIN32 void CHashFile::WriteDirectory(void) { if (m_hDirFile == INVALID_HANDLE_VALUE) return; SetFilePointer(m_hDirFile, 0, 0, FILE_BEGIN); DWORD nWritten; WriteFile(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, &nWritten, 0); SetEndOfFile(m_hDirFile); #ifdef DO_COMMIT FlushFileBuffers(m_hDirFile); #endif } #else // WIN32 void CHashFile::WriteDirectory(void) { if (m_hDirFile == INVALID_HANDLE_VALUE) return; lseek(m_hDirFile, 0, SEEK_SET); write(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir); //SetEndOfFile(m_hDirFile); #ifdef DO_COMMIT fsync(m_hDirFile); #endif } #endif // WIN32 BOOL CHashFile::EmptyDirectory(void) { if (m_pDir) delete m_pDir; m_nDir = 2; m_nDirDepth = 1; m_pDir = (HF_FILEOFFSET *)MEMALLOC(sizeof(HF_FILEOFFSET)*m_nDir); if (ISOUTOFMEMORY(m_pDir)) { return FALSE; } m_pDir[0] = m_pDir[1] = 0xFFFFFFFFUL; return TRUE; } BOOL CHashFile::CreateFileSet(const char *szDirFile, const char *szPageFile) { CloseAll(); #ifdef WIN32 m_hPageFile = CreateFile(szPageFile, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_RANDOM_ACCESS, NULL); #else // WIN32 m_hPageFile = open(szPageFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600); #endif // WIN32 if (m_hPageFile == INVALID_HANDLE_VALUE) { return FALSE; } #ifdef WIN32 m_hDirFile = CreateFile(szDirFile, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL); #else // WIN32 m_hDirFile = open(szDirFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600); #endif // WIN32 if (m_hPageFile == INVALID_HANDLE_VALUE) { return FALSE; } // Create empty structures in memory and write them out. // if (!EmptyDirectory()) { return FALSE; } iCache = AllocateEmptyPage(0, 0); if (iCache < 0) { return FALSE; } m_Cache[iCache].m_hp.Empty(0, 0UL, 100); m_pDir[0] = m_pDir[1] = m_Cache[iCache].m_o = 0UL; m_Cache[iCache].m_iState = HF_CACHE_UNPROTECTED; oEndOfFile = HF_SIZEOF_PAGE; #ifdef DO_COMMIT FlushCache(iCache); #endif WriteDirectory(); return TRUE; } BOOL CHashFile::RebuildDirectory(void) { // Initialize in-memory page directory // if (!EmptyDirectory()) { return FALSE; } // Re-build the directory from CHashPages. // int Hits = 0; for (unsigned long oPage = 0; oPage < oEndOfFile; oPage += HF_SIZEOF_PAGE) { int iCache = ReadCache(oPage, &Hits); unsigned long nPageDepth = m_Cache[iCache].m_hp.GetDepth(); while (m_nDirDepth < nPageDepth) { if (!DoubleDirectory()) { return FALSE; } } unsigned long nStart, nEnd; m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd); for ( ; nStart <= nEnd; nStart++) { if (m_pDir[nStart] != 0xFFFFFFFFUL) { Log.WriteString("CHashFile::Open - The keyspace of pages in Page File overlap.\n"); return FALSE; } m_pDir[nStart] = oPage; } } // Validate that the directory does not have holes. // for (unsigned long iFileDir = 0; iFileDir < m_nDir; iFileDir++) { if (m_pDir[iFileDir] == 0xFFFFFFFFUL) { Log.WriteString("CHashFile::Open - Page File is incomplete.\n"); return FALSE; } } WriteDirectory(); return TRUE; } BOOL CHashFile::ReadDirectory(void) { #ifdef WIN32 unsigned long cc = SetFilePointer(m_hDirFile, 0, 0, FILE_END); #else // WIN32 unsigned long cc = lseek(m_hDirFile, 0, SEEK_END); #endif // WIN32 if (cc == 0xFFFFFFFFUL) { return FALSE; } m_nDir = (int)(cc / HF_SIZEOF_FILEOFFSET); m_nDirDepth = 0; m_pDir = (HF_FILEOFFSET *)MEMALLOC(sizeof(HF_FILEOFFSET)*m_nDir); if (ISOUTOFMEMORY(m_pDir)) { return FALSE; } int n = m_nDir; n >>= 1; while (n) { m_nDirDepth++; n >>= 1; } #ifdef WIN32 cc = SetFilePointer(m_hDirFile, 0, 0, FILE_BEGIN); DWORD nRead; ReadFile(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, &nRead, 0); #else // WIN32 lseek(m_hDirFile, 0, SEEK_SET); read(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir); #endif // WIN32 return TRUE; } int CHashFile::Open(const char *szDirFile, const char *szPageFile) { CloseAll(); // First let's try to open the page file. This is the more important file. // #ifdef WIN32 m_hPageFile = CreateFile(szPageFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_RANDOM_ACCESS, NULL); #else // WIN32 m_hPageFile = open(szPageFile, O_RDWR|O_BINARY); #endif // WIN32 if (m_hPageFile == INVALID_HANDLE_VALUE) { // The PageFile doesn't exist, so we have'ta create both of them. // if (!CreateFileSet(szDirFile, szPageFile)) { CloseAll(); return HF_OPEN_STATUS_ERROR; } return HF_OPEN_STATUS_NEW; } // We have a page file open. Let's check how big it is. If it's // zero-length, the page file is useless to use, and we need to go through // the standard creation process. If the size is not a multiple of // HF_SIZEOF_PAGE, we need to fail. // #ifdef WIN32 oEndOfFile = SetFilePointer(m_hPageFile, 0, 0, FILE_END); #else // WIN32 oEndOfFile = lseek(m_hPageFile, 0, SEEK_END); #endif // WIN32 if (oEndOfFile == 0xFFFFFFFFUL) { CloseAll(); return HF_OPEN_STATUS_ERROR; } else if (oEndOfFile == 0UL) { // The PageFile exists, but it's zero-length, so we have'ta create // both of them. // if (!CreateFileSet(szDirFile, szPageFile)) { CloseAll(); return HF_OPEN_STATUS_ERROR; } return HF_OPEN_STATUS_NEW; } else if ((oEndOfFile % HF_SIZEOF_PAGE) != 0) { // This is not a mulitple of HP_SIZEOF_PAGE. Weird unknown format. // CloseAll(); return HF_OPEN_STATUS_ERROR; } // Now that the page file appears valid so far, let's see if the directory // file is there. This file is not strictly necessary, we can rebuild it. // However, having it helps us to open faster. // #ifdef WIN32 m_hDirFile = CreateFile(szDirFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL); #else // WIN32 m_hDirFile = open(szDirFile, O_RDWR|O_BINARY); #endif // WIN32 if (m_hDirFile == INVALID_HANDLE_VALUE) { // The Directory doesn't exist, so we create it anew, and rebuild the // index. #ifdef WIN32 m_hDirFile = CreateFile(szDirFile, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL); #else // WIN32 m_hDirFile = open(szDirFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600); #endif // WIN32 if (m_hPageFile == INVALID_HANDLE_VALUE) { CloseAll(); return HF_OPEN_STATUS_ERROR; } if (!RebuildDirectory()) { CloseAll(); return HF_OPEN_STATUS_ERROR; } return HF_OPEN_STATUS_OLD; } // Read in the directory. // if (!ReadDirectory()) { CloseAll(); return HF_OPEN_STATUS_ERROR; } return HF_OPEN_STATUS_OLD; } void CHashFile::Sync(void) { if (m_hPageFile != INVALID_HANDLE_VALUE) { cs_syncs++; BOOL bAllFlushed = TRUE; for (int i = 0; i < HF_PAGES; i++) { bAllFlushed &= FlushCache(i); } if (!bAllFlushed) { Log.WriteString("CHashFile::Sync. Could not flush all the pages. DB DAMAGE.\n"); } #ifdef DO_COMMIT #ifdef WIN32 FlushFileBuffers(m_hPageFile); #else // WIN32 fsync(m_hPageFile); #endif // WIN32 #endif } #ifdef DO_COMMIT if (m_hDirFile != INVALID_HANDLE_VALUE) { #ifdef WIN32 FlushFileBuffers(m_hDirFile); #else // WIN32 fsync(m_hDirFile); #endif // WIN32 } #endif } void CHashFile::CloseAll(void) { if (m_hPageFile != INVALID_HANDLE_VALUE) { Sync(); if (m_pDir) { MEMFREE(m_pDir); m_pDir = 0; } #ifdef WIN32 CloseHandle(m_hPageFile); #else // WIN32 close(m_hPageFile); #endif // WIN32 } if (m_hDirFile != INVALID_HANDLE_VALUE) { #ifdef WIN32 CloseHandle(m_hDirFile); #else // WIN32 close(m_hDirFile); #endif // WIN32 } Init(); } CHashFile::~CHashFile(void) { CloseAll(); } BOOL CHashFile::Insert(HP_HEAPLENGTH nRecord, unsigned long nHash, void *pRecord) { cs_writes++; for (;;) { unsigned long iFileDir = nHash >> (32-m_nDirDepth); if (iFileDir >= m_nDir) { Log.WriteString("CHashFile::Insert - iFileDir out of range..\n"); return FALSE; } HF_FILEOFFSET oPage = m_pDir[iFileDir]; iCache = ReadCache(oPage, &cs_whits); if (iCache < 0) { Log.WriteString("CHashFile::Insert - Page wasn't valid.\n"); return FALSE; } unsigned long nStart, nEnd; m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd); if (iFileDir < nStart || nEnd < iFileDir) { Log.WriteString("CHashFile::Insert - Directory points to the wrong page.\n"); return FALSE; } int errInserted = m_Cache[iCache].m_hp.Insert(nRecord, nHash, pRecord); if (errInserted == HP_INSERT_SUCCESS) break; if (errInserted == HP_INSERT_ERROR_ILLEGAL) { return FALSE; } #if !defined(STANDALONE) && !defined(VMS) && !defined(WIN32) // First, if we are @dumping, then we have a @forked process // that is also reading from the file. We must pause and let // this reader process finish. // STARTLOG(LOG_DBSAVES, "DMP", "DUMP"); log_text("Waiting on previously-forked child before page-splitting... "); ENDLOG; while (mudstate.dumping) { // We have a forked dump in progress, so we will wait until the // child exits. // sleep(1); } #endif // If the depth of this page is already as deep as the directory // depth,then we must increase depth of the directory, first. // if (m_nDirDepth == m_Cache[iCache].m_hp.GetDepth()) { if (!DoubleDirectory()) { return FALSE; } } // Split this page into two pages. We become a new one, and we // are given a pointer to the other one. // int Safe[2]; int nSafe = 0; int iEmpty0, iEmpty1; Safe[nSafe++] = iCache; iEmpty0 = AllocateEmptyPage(nSafe, Safe); if (iEmpty0 < 0) return FALSE; if (iCache == iEmpty0) { Log.WriteString("CHashFile::Split - iCache == iEmpty0\n"); return FALSE; } Safe[nSafe++] = iEmpty0; iEmpty1 = AllocateEmptyPage(nSafe, Safe); if (iEmpty1 < 0) return FALSE; if (iCache == iEmpty1) { Log.WriteString("CHashFile::Split - iCache == iEmpty1\n"); return FALSE; } if (iEmpty0 == iEmpty1) { Log.WriteString("CHashFile::Split - iEmpty0 == iEmpty1\n"); return FALSE; } if (!m_Cache[iCache].m_hp.Split(m_Cache[iEmpty0].m_hp, m_Cache[iEmpty1].m_hp)) { return FALSE; } // Tack another page onto the end of the .pag file. // long oNew = oEndOfFile; oEndOfFile += HF_SIZEOF_PAGE; // iEmpty0 => iCache. iEmpty1 => end of file // m_Cache[iCache].m_iState = HF_CACHE_EMPTY; m_Cache[iEmpty0].m_o = m_Cache[iCache].m_o; m_Cache[iEmpty1].m_o = oNew; m_Cache[iEmpty0].m_iState = HF_CACHE_UNPROTECTED; m_Cache[iEmpty1].m_iState = HF_CACHE_UNPROTECTED; // Now Flush these pages out. // FlushCache(iEmpty1); FlushCache(iEmpty0); #ifdef DO_COMMIT #ifdef WIN32 FlushFileBuffers(m_hPageFile); #else // WIN32 fsync(m_hPageFile); #endif // WIN32 #endif // Now, update the directory. // m_Cache[iEmpty1].m_hp.GetRange(m_nDirDepth, nStart, nEnd); for ( ; nStart <= nEnd; nStart++) { m_pDir[nStart] = oNew; } // Write Directory // #ifdef WIN32 SetFilePointer(m_hDirFile, 0, 0, FILE_BEGIN); DWORD nWritten; WriteFile(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir, &nWritten, 0); #else // WIN32 lseek(m_hDirFile, 0, SEEK_SET); write(m_hDirFile, m_pDir, sizeof(HF_FILEOFFSET)*m_nDir); #endif // WIN32 #ifdef DO_COMMIT #ifdef WIN32 FlushFileBuffers(m_hDirFile); #else // WIN32 fsync(m_hDirFile); #endif // WIN32 #endif } m_Cache[iCache].m_iState = HF_CACHE_UNPROTECTED; return TRUE; } BOOL CHashFile::DoubleDirectory(void) { unsigned int nNewDir = 2 * m_nDir; HP_DIRINDEX nNewDirDepth = m_nDirDepth + 1; HF_PFILEOFFSET pNewDir = (HF_PFILEOFFSET)MEMALLOC(sizeof(HF_FILEOFFSET)*nNewDir); if (ISOUTOFMEMORY(pNewDir)) { return FALSE; } unsigned int iNewDir = 0; for (unsigned int iDir = 0; iDir < m_nDir; iDir++) { pNewDir[iNewDir++] = m_pDir[iDir]; pNewDir[iNewDir++] = m_pDir[iDir]; } // Write out the new directory. It's always larger than // the previous one. // WriteDirectory(); MEMFREE(m_pDir); m_pDir = pNewDir; m_nDirDepth = nNewDirDepth; m_nDir = nNewDir; return TRUE; } HP_DIRINDEX CHashFile::FindFirstKey(unsigned long nHash) { cs_reads++; unsigned long iFileDir = nHash >> (32-m_nDirDepth); if (iFileDir >= m_nDir) { Log.WriteString("CHashFile::Insert - iFileDir out of range.\n"); cs_fails++; return HF_FIND_END; } HF_FILEOFFSET oPage = m_pDir[iFileDir]; iCache = ReadCache(oPage, &cs_rhits); if (iCache < 0) { cs_fails++; return HF_FIND_END; } unsigned long nStart, nEnd; m_Cache[iCache].m_hp.GetRange(m_nDirDepth, nStart, nEnd); if (iFileDir < nStart || nEnd < iFileDir) { Log.WriteString("CHashFile::Find - Directory points to the wrong page.\n"); return HF_FIND_END; } unsigned int numchecks; HP_DIRINDEX iDir = m_Cache[iCache].m_hp.FindFirstKey(nHash, &numchecks); if (iDir == HP_DIR_EMPTY) { cs_fails++; return HF_FIND_END; } return iDir; } HP_DIRINDEX CHashFile::FindNextKey(HP_DIRINDEX iDir, unsigned long nHash) { cs_reads++; unsigned int numchecks; iDir = m_Cache[iCache].m_hp.FindNextKey(iDir, nHash, &numchecks); if (iDir == HP_DIR_EMPTY) { cs_fails++; return HF_FIND_END; } return iDir; } void CHashFile::Copy(HP_DIRINDEX iDir, HP_PHEAPLENGTH pnRecord, void *pRecord) { m_Cache[iCache].m_hp.HeapCopy(iDir, pnRecord, pRecord); } void CHashFile::Remove(HP_DIRINDEX iDir) { cs_dels++; m_Cache[iCache].m_hp.HeapFree(iDir); m_Cache[iCache].m_iState = HF_CACHE_UNPROTECTED; } BOOL CHashFile::FlushCache(int iCache) { switch (m_Cache[iCache].m_iState) { case HF_CACHE_UNPROTECTED: //m_Cache[iCache].m_hp.Protect(); case HF_CACHE_UNWRITTEN: if (m_Cache[iCache].m_hp.WritePage(m_hPageFile, m_Cache[iCache].m_o)) { m_Cache[iCache].m_iState = HF_CACHE_CLEAN; } else { return FALSE; } } return TRUE; } int CHashFile::AllocateEmptyPage(int nSafe, int Safe[]) { again: BOOL bFoundOldest = FALSE; int iOldest = 0; int iOldestAge = 0; for (int cnt = HF_PAGES, i = m_iLastEmpty+1; cnt--; i++) { // Modulus // if (i >= HF_PAGES) { i -= HF_PAGES; } BOOL bExclude = FALSE; for (int j = 0; j < nSafe; j++) { if (Safe[j] == i) { bExclude = TRUE; break; } } if (!bExclude) { if (m_Cache[i].m_iState == HF_CACHE_EMPTY) { m_iLastEmpty = i; return i; } if (bFoundOldest) { int iDiff = iOldestAge - m_Cache[i].m_Age; if (iDiff > 0) { iOldestAge = m_Cache[i].m_Age; iOldest = i; } } else { iOldestAge = m_Cache[i].m_Age; iOldest = i; bFoundOldest = TRUE; } } } if (bFoundOldest) { if (FlushCache(iOldest)) { m_Cache[iOldest].m_iState = HF_CACHE_EMPTY; return iOldest; } else { m_Cache[iOldest].m_Age = m_iAgeNext++; goto again; } } return -1; } void CHashFile::Tick(void) { for (int i = 0; i < (HF_PAGES+119)/120; i++) { // Go ahead and flush a cache entry...just to keep the sync load down a bit. This gives // the cache time to age, and yet, pushes the pages off to the disk eventually and gradually. // FlushCache(m_iLastFlushed++); if (m_iLastFlushed >= HF_PAGES) { m_iLastFlushed = 0; } } } typedef struct tagCacheLookup { long oPage; unsigned long iCache; } CACHELOOKUP, *PCACHELOOKUP; int CHashFile::ReadCache(HF_FILEOFFSET oPage, int *phits) { unsigned long nHash = CRC32_ProcessInteger(oPage); nHash = nHash % (2*HF_PAGES); int i = m_hpCacheLookup[nHash]; if (m_Cache[i].m_iState != HF_CACHE_EMPTY && m_Cache[i].m_o == oPage) { m_Cache[i].m_Age = m_iAgeNext++; (*phits)++; return i; } for (i = 0; i < HF_PAGES; i++) { if (m_Cache[i].m_iState != HF_CACHE_EMPTY && m_Cache[i].m_o == oPage) { m_hpCacheLookup[nHash] = i; m_Cache[i].m_Age = m_iAgeNext++; (*phits)++; return i; } } if ((i = AllocateEmptyPage(0, 0)) >= 0) { m_hpCacheLookup[nHash] = i; if (m_Cache[i].m_hp.ReadPage(m_hPageFile, oPage)) { //if (m_Cache[i].m_hp.Validate()) //{ m_Cache[i].m_o = oPage; m_Cache[i].m_iState = HF_CACHE_CLEAN; m_Cache[i].m_Age = m_iAgeNext++; return i; //} } else { Log.WriteString("CHashFile::ReadCache. ReadPage failed to get the page. DB DAMAGE.\n"); } } return -1; } CHashTable::CHashTable(void) { SeedRandomNumberGenerator(); Init(); } void CHashTable::Init(void) { m_nDir = 2; m_nDirDepth = 1; m_pDir = new pCHashPage[m_nDir]; if (m_pDir) { m_pDir[1] = m_pDir[0] = new CHashPage; if (m_pDir[0]) { if (m_pDir[0]->Allocate(HT_SIZEOF_PAGE)) { m_pDir[0]->Empty(0, 0UL, 100); } else { delete m_pDir[0]; m_pDir[0] = 0; } } } m_nPages = 1; m_nEntries = 0; m_nDeletions = 0; m_nScans = 0; m_nHits = 0; m_nChecks = 0; m_nMaxScan = 0; } void CHashTable::ResetStats(void) { m_nScans = 0; m_nHits = 0; m_nChecks = 0; } BOOL CHashTable::Insert(HP_HEAPLENGTH nRecord, unsigned long nHash, void *pRecord) { for (;;) { unsigned long iTableDir = nHash >> (32 - m_nDirDepth); #ifdef HP_PROTECTION if (iTableDir >= m_nDir) { Log.WriteString("CHashTable::Insert - iTableDir out of range..\n"); return FALSE; } #endif m_hpLast = m_pDir[iTableDir]; if (!m_hpLast) { Log.WriteString("CHashTable::Insert - Page wasn't valid.\n"); return FALSE; } unsigned long nStart, nEnd; #ifdef HP_PROTECTION m_hpLast->GetRange(m_nDirDepth, nStart, nEnd); if (iTableDir < nStart || nEnd < iTableDir) { Log.WriteString("CHashTable::Insert - Directory points to the wrong page.\n"); return FALSE; } #endif int errInserted = m_hpLast->Insert(nRecord, nHash, pRecord); if (errInserted == HP_INSERT_SUCCESS) break; if (errInserted == HP_INSERT_ERROR_ILLEGAL) { return FALSE; } // If the depth of this page is already as deep as the directory // depth,then we must increase depth of the directory, first. // if (m_nDirDepth == m_hpLast->GetDepth()) { if (!DoubleDirectory()) { return FALSE; } } // Split this page into two pages. We become a new one, and we // are given a pointer to the other one. // CHashPage *hpEmpty0 = new CHashPage; if (!hpEmpty0) return FALSE; if (!hpEmpty0->Allocate(HT_SIZEOF_PAGE)) { delete hpEmpty0; return FALSE; } CHashPage *hpEmpty1 = new CHashPage; if (!hpEmpty1) return FALSE; if (!hpEmpty1->Allocate(HT_SIZEOF_PAGE)) { delete hpEmpty0; delete hpEmpty1; return FALSE; } if (!m_hpLast->Split(*hpEmpty0, *hpEmpty1)) { return FALSE; } // Otherwise, this value will be over inflated. // m_nMaxScan = 0; // Now, update the directory. // hpEmpty0->GetRange(m_nDirDepth, nStart, nEnd); for ( ; nStart <= nEnd; nStart++) { m_pDir[nStart] = hpEmpty0; } hpEmpty1->GetRange(m_nDirDepth, nStart, nEnd); for ( ; nStart <= nEnd; nStart++) { m_pDir[nStart] = hpEmpty1; } m_nPages++; delete m_hpLast; m_hpLast = 0; } m_nEntries++; return TRUE; } BOOL CHashTable::DoubleDirectory(void) { unsigned int nNewDir = 2 * m_nDir; unsigned int nNewDirDepth = m_nDirDepth + 1; pCHashPage *pNewDir = new pCHashPage[nNewDir]; if (pNewDir) { unsigned int iNewDir = 0; for (unsigned int iDir = 0; iDir < m_nDir; iDir++) { pNewDir[iNewDir++] = m_pDir[iDir]; pNewDir[iNewDir++] = m_pDir[iDir]; } delete m_pDir; m_pDir = pNewDir; m_nDirDepth = nNewDirDepth; m_nDir = nNewDir; return TRUE; } return FALSE; } HP_DIRINDEX CHashTable::FindFirstKey(unsigned long nHash) { m_nScans++; unsigned long iTableDir = nHash >> (32-m_nDirDepth); #ifdef HP_PROTECTION if (iTableDir >= m_nDir) { Log.WriteString("CHashTable::Insert - iTableDir out of range.\n"); return HF_FIND_END; } #endif m_hpLast = m_pDir[iTableDir]; if (!m_hpLast) { Log.WriteString("CHashTable::Insert - Page wasn't valid.\n"); return HF_FIND_END; } #ifdef HP_PROTECTION unsigned long nStart, nEnd; m_hpLast->GetRange(m_nDirDepth, nStart, nEnd); if (iTableDir < nStart || nEnd < iTableDir) { Log.WriteString("CHashTable::Find - Directory points to the wrong page.\n"); return HF_FIND_END; } #endif unsigned int numchecks; HP_DIRINDEX iDir = m_hpLast->FindFirstKey(nHash, &numchecks); m_nChecks += numchecks; if (numchecks > m_nMaxScan) { m_nMaxScan = numchecks; } if (iDir == HP_DIR_EMPTY) { return HF_FIND_END; } m_nHits++; return iDir; } HP_DIRINDEX CHashTable::FindNextKey(HP_DIRINDEX iDir, unsigned long nHash) { m_nScans++; unsigned int numchecks; iDir = m_hpLast->FindNextKey(iDir, nHash, &numchecks); m_nChecks += numchecks; if (numchecks > m_nMaxScan) { m_nMaxScan = numchecks; } if (iDir == HP_DIR_EMPTY) { return HF_FIND_END; } m_nHits++; return iDir; } void CHashTable::Copy(HP_DIRINDEX iDir, HP_PHEAPLENGTH pnRecord, void *pRecord) { m_hpLast->HeapCopy(iDir, pnRecord, pRecord); } void CHashTable::Remove(HP_DIRINDEX iDir) { m_nEntries--; m_nDeletions++; m_hpLast->HeapFree(iDir); } void CHashTable::Update(HP_DIRINDEX iDir, HP_HEAPLENGTH nRecord, void *pRecord) { m_hpLast->HeapUpdate(iDir, nRecord, pRecord); } CHashTable::~CHashTable(void) { Final(); } void CHashTable::Final(void) { if (m_pDir) { m_hpLast = 0; for (unsigned int i = 0; i < m_nDir; i++) { CHashPage *hp = m_pDir[i]; if (hp != m_hpLast && hp) { delete hp; m_hpLast = hp; } } delete m_pDir; } } void CHashTable::Reset(void) { Final(); Init(); } HP_DIRINDEX CHashTable::FindFirst(HP_PHEAPLENGTH pnRecord, void *pRecord) { m_hpLast = 0; for (m_iPage = 0; m_iPage < m_nDir; m_iPage++) { if (m_pDir[m_iPage] == m_hpLast) continue; m_hpLast = m_pDir[m_iPage]; if (m_hpLast) { HP_DIRINDEX iDir = m_hpLast->FindFirst(pnRecord, pRecord); if (iDir != HP_DIR_EMPTY) { return iDir; } } } return HF_FIND_END; } HP_DIRINDEX CHashTable::FindNext(HP_PHEAPLENGTH pnRecord, void *pRecord) { if (m_hpLast) { HP_DIRINDEX iDir = m_hpLast->FindNext(pnRecord, pRecord); if (iDir != HP_DIR_EMPTY) { return iDir; } } // Move on to the next page. // for ( ; m_iPage < m_nDir; m_iPage++) { // Move on to the next page. // if (m_pDir[m_iPage] == m_hpLast) continue; m_hpLast = m_pDir[m_iPage]; if (m_hpLast) { HP_DIRINDEX iDir = m_hpLast->FindFirst(pnRecord, pRecord); if (iDir != HP_DIR_EMPTY) { return iDir; } } } return HF_FIND_END; } void CHashTable::GetStats(unsigned int *hashsize, int *entries, int *deletes, int *scans, int *hits, int *checks, int *max_scan) { *hashsize = m_nPages; *entries = m_nEntries; *deletes = m_nDeletions; *scans = m_nScans; *hits = m_nHits; *checks = m_nChecks; *max_scan = m_nMaxScan; } CLogFile Log; void CLogFile::WriteInteger(int iNumber) { char aTempBuffer[20]; int nTempBuffer = Tiny_ltoa(iNumber, aTempBuffer); WriteBuffer(nTempBuffer, aTempBuffer); } void CLogFile::WriteBuffer(int nString, const char *pString) { #if !defined(STANDALONE) && defined(WIN32) EnterCriticalSection(&csLog); #endif while (nString > 0) { int nAvailable = SIZEOF_LOG_BUFFER - m_nBuffer; if (nAvailable == 0) { Flush(); continue; } int nToMove = nAvailable; if (nString < nToMove) { nToMove = nString; } // Move nToMove bytes from pString to aBuffer+nBuffer // memcpy(m_aBuffer+m_nBuffer, pString, nToMove); pString += nToMove; nString -= nToMove; m_nBuffer += nToMove; } Flush(); #if !defined(STANDALONE) && defined(WIN32) LeaveCriticalSection(&csLog); #endif } void CLogFile::WriteString(const char *pString) { int nString = strlen(pString); WriteBuffer(nString, pString); } void DCL_CDECL CLogFile::printf(char *fmt, ...) { va_list ap; va_start(ap, fmt); char aTempBuffer[SIZEOF_LOG_BUFFER]; int nString = Tiny_vsnprintf(aTempBuffer, SIZEOF_LOG_BUFFER, fmt, ap); va_end(ap); WriteBuffer(nString, aTempBuffer); } CLogFile::~CLogFile(void) { Flush(); #if !defined(STANDALONE) && defined(WIN32) CloseLogFile(); DeleteCriticalSection(&csLog); #endif } #if !defined(STANDALONE) && defined(WIN32) void MakeLogName(char *szPrefix, CLinearTimeAbsolute lta, char *szLogName) { strcpy(szLogName, szPrefix); strcat(szLogName, "-"); lta.ReturnUniqueString(szLogName+strlen(szLogName)); strcat(szLogName, ".log"); } void CLogFile::CreateLogFile(void) { CloseLogFile(); m_nSize = 0; m_hFile = CreateFile(m_szFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (m_hFile == INVALID_HANDLE_VALUE) { printf("CLogFile: Cannot create tinymux.log\n"); } } void CLogFile::AppendLogFile(void) { CloseLogFile(); #ifdef WIN32 m_hFile = CreateFile(m_szFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL); #else // WIN32 m_hFile = open(m_szFilename, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, 0600); #endif // WIN32 if (m_hFile == INVALID_HANDLE_VALUE) { printf("CLogFile: Cannot create tinymux.log\n"); } else { #ifdef WIN32 if (SetFilePointer(m_hFile, 0, 0, FILE_END) == 0xFFFFFFFFUL) #else // WIN32 if (lseek(m_hFile, 0, SEEK_SET) == 0xFFFFFFFFUL) #endif // WIN32 { printf("CLogFile: seek error %u.\n", GetLastError()); } } } void CLogFile::CloseLogFile(void) { if (m_hFile != INVALID_HANDLE_VALUE) { CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; } } void CLogFile::ChangePrefix(char *szPrefix) { if (strcmp(szPrefix, m_szPrefix) != 0) { CloseLogFile(); char szNewName[SIZEOF_PATHNAME]; MakeLogName(szPrefix, m_ltaStarted, szNewName); ReplaceFile(m_szFilename, szNewName); strcpy(m_szPrefix, szPrefix); strcpy(m_szFilename, szNewName); AppendLogFile(); } } #endif CLogFile::CLogFile(void) { m_nBuffer = 0; #if !defined(STANDALONE) && defined(WIN32) InitializeCriticalSection(&csLog); m_szPrefix[0] = '\0'; m_ltaStarted.GetLocal(); MakeLogName(m_szPrefix, m_ltaStarted, m_szFilename); CreateLogFile(); #endif } #define FILE_SIZE_TRIGGER (512*1024UL) void CLogFile::Flush(void) { if (m_nBuffer <= 0) return; #if defined(STANDALONE) || !defined(WIN32) fwrite(m_aBuffer, m_nBuffer, 1, stderr); #else m_nSize += m_nBuffer; unsigned long nWritten; WriteFile(m_hFile, m_aBuffer, m_nBuffer, &nWritten, NULL); if (m_nSize > FILE_SIZE_TRIGGER) { CloseLogFile(); m_ltaStarted.GetLocal(); MakeLogName(m_szPrefix, m_ltaStarted, m_szFilename); CreateLogFile(); } #endif m_nBuffer = 0; }