Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
8.9 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // iascache.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines classes for creating hash tables and caches.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 02/07/2000 Original version.
  16. // 04/25/2000 Decrement entries when item is removed.
  17. //
  18. ///////////////////////////////////////////////////////////////////////////////
  19. #include <proxypch.h>
  20. #include <iascache.h>
  21. ///////////////////////////////////////////////////////////////////////////////
  22. //
  23. // HashTableEntry
  24. //
  25. ///////////////////////////////////////////////////////////////////////////////
  26. HashTableEntry::~HashTableEntry() throw ()
  27. { }
  28. ///////////////////////////////////////////////////////////////////////////////
  29. //
  30. // HashTableBase
  31. //
  32. ///////////////////////////////////////////////////////////////////////////////
  33. HashTableBase::HashTableBase(
  34. HashKey hashFunction,
  35. ULONG initialSize
  36. ) throw ()
  37. : hash(hashFunction),
  38. table(NULL),
  39. buckets(initialSize ? initialSize : 1),
  40. entries(0)
  41. {
  42. table = new Bucket[buckets];
  43. memset(table, 0, buckets * sizeof(Bucket));
  44. }
  45. HashTableBase::~HashTableBase() throw ()
  46. {
  47. clear();
  48. delete[] table;
  49. }
  50. void HashTableBase::clear() throw ()
  51. {
  52. lock();
  53. Bucket* end = table + buckets;
  54. // Iterate through the buckets.
  55. for (Bucket* b = table; b != end; ++b)
  56. {
  57. // Iterate through the entries in the bucket.
  58. for (HashTableEntry* entry = *b; entry; )
  59. {
  60. HashTableEntry* next = entry->next;
  61. entry->Release();
  62. entry = next;
  63. }
  64. }
  65. // Zero out the table.
  66. memset(table, 0, buckets * sizeof(Bucket));
  67. entries = 0;
  68. unlock();
  69. }
  70. bool HashTableBase::erase(const void* key) throw ()
  71. {
  72. HashTableEntry* entry = remove(key);
  73. return entry ? entry->Release(), true : false;
  74. }
  75. HashTableEntry* HashTableBase::find(const void* key) throw ()
  76. {
  77. HashTableEntry* match = NULL;
  78. ULONG hashval = hash(key);
  79. lock();
  80. for (match = table[hashval % buckets]; match; match = match->next)
  81. {
  82. if (match->matches(key))
  83. {
  84. match->AddRef();
  85. break;
  86. }
  87. }
  88. unlock();
  89. return match;
  90. }
  91. bool HashTableBase::insert(
  92. HashTableEntry& entry,
  93. bool checkForDuplicates
  94. ) throw ()
  95. {
  96. HashTableEntry* match = NULL;
  97. // Do what we can before acquiring the lock.
  98. const void* key = entry.getKey();
  99. ULONG hashval = hash(key);
  100. lock();
  101. // Resize the table to make room.
  102. if (entries > buckets) { resize(buckets * 2); }
  103. // Find the bucket.
  104. Bucket* bucket = table + (hashval % buckets);
  105. // Do we already have an entry with this key?
  106. if (checkForDuplicates)
  107. {
  108. for (match = *bucket; match; match = match->next)
  109. {
  110. if (match->matches(key))
  111. {
  112. break;
  113. }
  114. }
  115. }
  116. if (!match)
  117. {
  118. // No, so stick it in the bucket.
  119. entry.next = *bucket;
  120. *bucket = &entry;
  121. entry.AddRef();
  122. ++entries;
  123. }
  124. unlock();
  125. return match ? false : true;
  126. }
  127. HashTableEntry* HashTableBase::remove(const void* key) throw ()
  128. {
  129. HashTableEntry* match = NULL;
  130. ULONG hashval = hash(key);
  131. lock();
  132. for (HashTableEntry** entry = table + (hashval % buckets);
  133. *entry != 0;
  134. entry = &((*entry)->next))
  135. {
  136. if ((*entry)->matches(key))
  137. {
  138. match = *entry;
  139. *entry = match->next;
  140. --entries;
  141. break;
  142. }
  143. }
  144. unlock();
  145. return match;
  146. }
  147. bool HashTableBase::resize(ULONG newSize) throw ()
  148. {
  149. if (!newSize) { newSize = 1; }
  150. // Allocate memory for the new table.
  151. Bucket* newTable = new (std::nothrow) Bucket[newSize];
  152. // If the allocation failed, there's nothing else we can do.
  153. if (!newTable) { return false; }
  154. // Null out the buckets.
  155. memset(newTable, 0, newSize * sizeof(Bucket));
  156. lock();
  157. // Save the old table.
  158. Bucket* begin = table;
  159. Bucket* end = table + buckets;
  160. // Swap in the new table.
  161. table = newTable;
  162. buckets = newSize;
  163. // Iterate through the old buckets.
  164. for (Bucket* oldBucket = begin; oldBucket != end; ++oldBucket)
  165. {
  166. // Iterate through the entries in the bucket.
  167. for (HashTableEntry* entry = *oldBucket; entry; )
  168. {
  169. // Save the next entry.
  170. HashTableEntry* next = entry->next;
  171. // Get the appropriate bucket.
  172. Bucket* newBucket = table + (hash(entry->getKey()) % buckets);
  173. // Add the node to the head of the new bucket.
  174. entry->next = *newBucket;
  175. *newBucket = entry;
  176. // Move on.
  177. entry = next;
  178. }
  179. }
  180. unlock();
  181. // Delete the old table.
  182. delete[] begin;
  183. return true;
  184. }
  185. ///////////////////////////////////////////////////////////////////////////////
  186. //
  187. // CacheEntry
  188. //
  189. ///////////////////////////////////////////////////////////////////////////////
  190. inline CacheEntry* CacheEntry::prevInList() const throw ()
  191. {
  192. return blink;
  193. }
  194. inline CacheEntry* CacheEntry::nextInList() const throw ()
  195. {
  196. return flink;
  197. }
  198. inline void CacheEntry::removeFromList() throw ()
  199. {
  200. CacheEntry* oldFlink = flink;
  201. CacheEntry* oldBlink = blink;
  202. oldBlink->flink = oldFlink;
  203. oldFlink->blink = oldBlink;
  204. }
  205. inline bool CacheEntry::isExpired(const ULONG64& now) const throw ()
  206. {
  207. return now > expiry;
  208. }
  209. inline bool CacheEntry::isExpired() const throw ()
  210. {
  211. return isExpired(GetSystemTime64());
  212. }
  213. inline void CacheEntry::setExpiry(ULONG64 ttl) throw ()
  214. {
  215. expiry = GetSystemTime64() + ttl;
  216. }
  217. ///////////////////////////////////////////////////////////////////////////////
  218. //
  219. // HashTableBase
  220. //
  221. ///////////////////////////////////////////////////////////////////////////////
  222. CacheBase::CacheBase(
  223. HashKey hashFunction,
  224. ULONG initialSize,
  225. ULONG maxCapacity,
  226. ULONG timeToLive,
  227. bool updateExpiryOnFind
  228. ) throw ()
  229. : HashTableBase(hashFunction, initialSize),
  230. flink(listAsEntry()), blink(listAsEntry()),
  231. ttl(timeToLive * 10000i64),
  232. maxEntries(maxCapacity),
  233. autoUpdate(updateExpiryOnFind)
  234. {
  235. }
  236. CacheBase::~CacheBase() throw ()
  237. {
  238. clear();
  239. buckets = 0;
  240. }
  241. void CacheBase::clear() throw ()
  242. {
  243. lock();
  244. // Release all the entries.
  245. for (iterator i = begin(); i != end(); (i++)->Release()) { }
  246. // Reset the LRU list.
  247. flink = blink = listAsEntry();
  248. // Reset the hash table.
  249. memset(table, 0, sizeof(Bucket) * buckets);
  250. // There's nothing left.
  251. entries = 0;
  252. unlock();
  253. }
  254. ULONG CacheBase::evict() throw ()
  255. {
  256. lock();
  257. ULONG retval = entries;
  258. unsafe_evict();
  259. retval = entries - retval;
  260. unlock();
  261. return retval;
  262. }
  263. bool CacheBase::erase(const void* key) throw ()
  264. {
  265. CacheEntry* entry = remove(key);
  266. return entry ? entry->Release(), true : false;
  267. }
  268. CacheEntry* CacheBase::find(const void* key) throw ()
  269. {
  270. lock();
  271. unsafe_evict();
  272. // Look it up in the hash table.
  273. CacheEntry* entry = static_cast<CacheEntry*>(HashTableBase::find(key));
  274. if ((entry != 0) && autoUpdate)
  275. {
  276. // Whenever someone reads an entry, we reset the TTL.
  277. entry->setExpiry(ttl);
  278. entry->removeFromList();
  279. push_front(entry);
  280. }
  281. unlock();
  282. return entry;
  283. }
  284. bool CacheBase::insert(
  285. CacheEntry& entry,
  286. bool checkForDuplicates
  287. ) throw ()
  288. {
  289. lock();
  290. unsafe_evict();
  291. bool added = HashTableBase::insert(entry, checkForDuplicates);
  292. if (added)
  293. {
  294. entry.setExpiry(ttl);
  295. push_front(&entry);
  296. }
  297. unlock();
  298. return added;
  299. }
  300. CacheEntry* CacheBase::remove(const void* key) throw ()
  301. {
  302. lock();
  303. CacheEntry* entry = unsafe_remove(key);
  304. unlock();
  305. return entry;
  306. }
  307. void CacheBase::unsafe_evict() throw ()
  308. {
  309. while (entries > maxEntries )
  310. {
  311. unsafe_remove(blink->getKey())->Release();
  312. }
  313. while (entries && blink->isExpired())
  314. {
  315. unsafe_remove(blink->getKey())->Release();
  316. }
  317. }
  318. CacheEntry* CacheBase::unsafe_remove(const void* key) throw ()
  319. {
  320. CacheEntry* entry = static_cast<CacheEntry*>(HashTableBase::remove(key));
  321. if (entry)
  322. {
  323. entry->removeFromList();
  324. }
  325. return entry;
  326. }
  327. void CacheBase::push_front(CacheEntry* entry) throw ()
  328. {
  329. CacheEntry* listHead = listAsEntry();
  330. CacheEntry* oldFlink = listHead->flink;
  331. entry->flink = oldFlink;
  332. entry->blink = listHead;
  333. oldFlink->blink = entry;
  334. listHead->flink = entry;
  335. }