1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
|
/**************************** omfhash.cpp **********************************
* Author: Agner Fog
* Date created: 2007-02-14
* Last modified: 2007-02-14
* Project: objconv
* Module: omfhash.cpp
* Description:
* This module contains code for searching and making hash tables for OMF
* libraries.
*
* Copyright 2007-2008 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"
void COMFHashTable::Init(SOMFHashBlock * blocks, uint32_t NumBlocks) {
// Initialize
this->blocks = blocks; // Pointer to blocks
this->NumBlocks = NumBlocks; // Number of blocks
String = 0;
StringLength = 0;
}
// Rotate right 16-bit word
uint16_t RotR(uint16_t x, uint16_t bits) {
return (x >> bits) | (x << (16 - bits));
}
// Rotate left 16-bit word
uint16_t RotL(uint16_t x, uint16_t bits) {
return (x << bits) | (x >> (16 - bits));
}
void COMFHashTable::MakeHash(char * name) {
// Compute hash according to the official algorithm
char * pb; // Pointer for forward scan through string
char * pe; // Pointer for backwards scan through string
uint16_t c; // Current character converted to lower case
uint16_t BlockX; // Calculate block hash
uint16_t BucketX; // Calculate block hash
String = name; // Type cast string to unsigned char *
StringLength = (uint32_t)strlen(name);
if (StringLength > 255 || StringLength == 0) {
// String too long
err.submit(1204, name); // Warning: truncating
StringLength = 255;
String[StringLength] = 0; // Truncation modifies string source!
}
String = name; // Type cast to unsigned characters
pb = String; // Initialize pointer for forward scan
pe = String + StringLength; // Initialize pointer for backward scan
BlockX = BucketD = StringLength | 0x20; // Initialize left-to-right scan
BucketX = BlockD = 0; // Initialize right-to-left scan
// Scan loop
while (1) {
c = *(--pe) | 0x20; // Read character for backward scan, make lower case
BucketX = RotR(BucketX, 2) ^ c; // Rotate, XOR
BlockD = RotL(BlockD, 2) ^ c; // Rotate, XOR
if (pe == String) break; // Stop loop when backward scan finished
c = *(pb++) | 0x20; // Read character for forward scan, make lower case
BlockX = RotL(BlockX, 2) ^ c; // Rotate, XOR
BucketD = RotR(BucketD, 2) ^ c; // Rotate, XOR
}
// Make values modulo number of blocks / buckets
BlockX = BlockX % NumBlocks;
BlockD = BlockD % NumBlocks;
if (BlockD == 0) BlockD = 1;
BucketX = BucketX % OMFNumBuckets;
BucketD = BucketD % OMFNumBuckets;
if (BucketD == 0) BucketD = 1;
StartBlock = BlockX;
StartBucket = BucketX;
}
int COMFHashTable::FindString(uint32_t & ModulePage, uint32_t & Conflicts) {
// Search for String.
// Returns number of occurrences of String
// Module receives the module page for the first occurrence
// Conflicts receives the number of conflicting entries encountered before the match
uint32_t Num = 0; // Number of occurrences of string found
uint16_t Block; // Block number
uint16_t Bucket; // Bucket number
uint32_t StringIndex; // Index to string
Conflicts = 0; // Start counting Conflicts
Block = StartBlock;
Bucket = StartBucket;
// Loop through blocks
do {
// Loop through buckets
do {
// String index of current bucket
StringIndex = blocks[Block].b.Buckets[Bucket];
if (StringIndex == 0) {
if (blocks[Block].b.FreeSpace < 0xff) {
// Empty bucket found. End of search
return Num;
}
else {
// Block is full. Search next block
// Note: It would be logical to set StartBucket = Bucket
// here in order to allow all buckets in the next block
// to be tried, but the official algorithm doesn't seem
// to do so!?
// StartBucket = Bucket;
break;
}
}
// Bucket contains a string. Is it the same string?
if (blocks[Block].Strings[StringIndex*2] == StringLength
&& strncmp((char*)&blocks[Block].Strings[StringIndex*2+1], (char*)String, StringLength) == 0) {
// Matching string found
Num++;
if (Num == 1) {
// First occurrence. Save module number
ModulePage = *(uint16_t*)&blocks[Block].Strings[StringIndex*2+1+StringLength];
}
}
else {
// Conflicting string found
Conflicts++;
}
// Next bucket
Bucket = (Bucket + BucketD) % OMFNumBuckets;
} while (Bucket != StartBucket);
// Next block
Block = (Block + BlockD) % NumBlocks;
} while (Block != StartBlock);
// Finished searching all blocks and buckets
return Num;
}
int COMFHashTable::InsertString(uint16_t & ModulePage) {
// Insert string in hash table.
// Parameter:
// ModulePage = module address / page size
// Return value:
// 0 if success,
// 1 if identical string allready exists in the table. New string will not be entered.
// ModulePage will receive the module page of the existing string in this case.
// 2 if table is full,
uint16_t Block; // Block number
uint16_t Bucket; // Bucket number
uint32_t StringIndex; // Index to string space
uint32_t StringOffset; // Offset to string from begin of block
uint32_t SpaceRequired; // Space required to store string
SpaceRequired = StringLength + 3; // Space for string + stringlength + module index
SpaceRequired = (SpaceRequired + 1) & uint32_t(-2);// Round up to nearest even
Block = StartBlock;
Bucket = StartBucket;
// Loop through blocks
do {
// Loop through buckets
do {
// String index of current bucket
StringIndex = blocks[Block].b.Buckets[Bucket];
if (StringIndex == 0) {
// Found empty bucket. Check if block has enough free space
if (uint32_t(OMFBlockSize) - blocks[Block].b.FreeSpace * 2 < SpaceRequired) {
// Not enough space in block.
// Continue with same bucket in next block.
// Note: It would be logical to set StartBucket = Bucket
// here in order to allow all buckets in the next block
// to be tried, but the official algorithm doesn't seem
// to do so!?
// StartBucket = Bucket;
break;
}
// Enough space found. Enter string in bucket
StringIndex = blocks[Block].b.FreeSpace;
blocks[Block].b.Buckets[Bucket] = StringIndex;
// Address to store string
StringOffset = StringIndex * 2;
// Store string length
blocks[Block].Strings[StringOffset] = (uint8_t)StringLength;
// Copy string
memcpy(blocks[Block].Strings + StringOffset + 1, String, StringLength);
// Insert module page number
*(uint16_t*)(blocks[Block].Strings + StringOffset + 1 + StringLength) = ModulePage;
// Update free space
blocks[Block].b.FreeSpace += (uint8_t)(SpaceRequired / 2);
// Check if overflow
if (blocks[Block].b.FreeSpace == 0) blocks[Block].b.FreeSpace = 0xFF;
// Indicate success
return 0;
}
else {
// Bucket contains a string. Check if it is the same string
if (blocks[Block].Strings[StringIndex*2] == StringLength
&& strncmp((char*)(blocks[Block].Strings+StringIndex*2+1), (char*)String, StringLength) == 0) {
// Identical string found. Return module index for existing string entry
ModulePage = *(uint16_t*)(blocks[Block].Strings+StringIndex*2+1+StringLength);
// Indicate failure
return 1;
}
}
// Bucket was full. Go to next bucket
Bucket = (Bucket + BucketD) % OMFNumBuckets;
} while (Bucket != StartBucket);
// If we got here, we have found no empty bucket in the block or
// there was not enough string space in the block.
// We need to mark the block as full to tell the linker to
// continue in next block when searching for this string
// Whether the block has any empty buckets or not
blocks[Block].b.FreeSpace = 0xFF;
// Go to next block
Block = (Block + BlockD) % NumBlocks;
} while (Block != StartBlock);
// Finished searching all blocks and buckets
// No empty space found. Indicate failure:
return 2;
}
// Table of prime numbers
// You may add more prime numbers if very big library files are needed
static const uint32_t PrimeNumbers[] = {
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, 1049, 1051, 1061, 1063,
1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163,
1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259,
1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361,
1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453,
1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549,
1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621,
1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847,
1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949,
1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039,
2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137,
2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251,
2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347,
2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437,
2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551,
2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671,
2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741,
2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843,
2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957,
2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067,
3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191,
3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307,
3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391,
3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517,
3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607,
3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701,
3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821,
3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919,
3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021,
4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133,
4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243,
4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357,
4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481,
4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591,
4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691,
4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801,
4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021
};
// Length of table
static const uint32_t PrimeNumbersLen = sizeof(PrimeNumbers)/sizeof(PrimeNumbers[0]);
void COMFHashTable::MakeHashTable(CSList<SStringEntry> & StringEntries,
CMemoryBuffer & StringBuffer, CMemoryBuffer & OutFile, CLibrary * Library) {
// Make hash table. Parameters:
// StringEntries[].String = name of each public symbol as offset into StringBuffer
// StringEntries[].Member = page address of member = offset / page size
// StringBuffer = contains all strings
// OutFile will receive the output hash table
CSList<SOMFHashBlock> HashTable; // Hash table
COMFHashTable TableHandler; // Hash table handler
uint32_t NumBlocksI; // Number of blocks as index into prime number table
uint32_t BlockI; // Block index
uint32_t SymI; // Symbol index
char * String; // Symbol name
uint16_t Module1, Module2; // Module page = offset / page size
int Result; // 0 = success
// Estimate required number of blocks
NumBlocks = (StringEntries.GetNumEntries() * 8 + StringBuffer.GetDataSize()) / 256;
// Find nearest prime number >= NumBlocks, but stay within the range from 2 to 251.
// The minimum NumBlocks is 1, but some systems use 2 as the minimum.
// The maximum is 251, but some linkers may allow a higher number
for (NumBlocksI = 1; NumBlocksI < 55; NumBlocksI++) {
if (PrimeNumbers[NumBlocksI] >= NumBlocks) break;
}
// Try if this number of blocks is sufficient
while (NumBlocksI < PrimeNumbersLen) {
// Get number of blocks from prime numbers table
NumBlocks = PrimeNumbers[NumBlocksI];
// Check if <= 251
if (NumBlocks > 255) err.submit(1215); // Number of blocks exceeds official limit. May still work with some linkers
// Allocate space for hash table
HashTable.SetNum(NumBlocks);
memset(&HashTable[0], 0, NumBlocks * OMFBlockSize);
// Initialize hash table handler
TableHandler.Init(&HashTable[0], NumBlocks);
// Set free space pointers
for (BlockI = 0; BlockI < NumBlocks; BlockI++) {
TableHandler.blocks[BlockI].b.FreeSpace = 19;
}
Result = 0;
// Insert symbols
// Loop through symbols
for (SymI = 0; SymI < StringEntries.GetNumEntries(); SymI++) {
// Symbol name
String = (char*)StringBuffer.Buf() + StringEntries[SymI].String;
// Module page
Module1 = Module2 = StringEntries[SymI].Member;
// Insert name in table
TableHandler.MakeHash(String);
Result = TableHandler.InsertString(Module2);
if (Result == 1) {
// String already exists
// Compose error string "Modulename1 and Modulename2"
char ErrorModuleNames[64];
strcpy(ErrorModuleNames, Library->GetModuleName(Module1));
strcpy(ErrorModuleNames + strlen(ErrorModuleNames), " and ");
strcpy(ErrorModuleNames + strlen(ErrorModuleNames), Library->GetModuleName(Module2));
// submit error message
err.submit(1214, String, ErrorModuleNames);
}
if (Result == 2) {
// Table is full. Stop and repeat with a higher NumBlocks
break;
}
} // End of loop through symbols
if (Result < 2) {
// Finished with success
// Store hash table
OutFile.Push(&HashTable[0], HashTable.GetNumEntries() * OMFBlockSize);
return;
}
// Table is full. Try again with a higher number of blocks
NumBlocksI++;
}
// End of loop through PrimeNumbers table
err.submit(2605); // Failed to make table
}
|