Source code of Windows XP (NT5)
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.

448 lines
8.5 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. ) throw ()
  228. : HashTableBase(hashFunction, initialSize),
  229. flink(listAsEntry()), blink(listAsEntry()),
  230. ttl(timeToLive * 10000i64),
  231. maxEntries(maxCapacity)
  232. {
  233. }
  234. CacheBase::~CacheBase() throw ()
  235. {
  236. clear();
  237. buckets = 0;
  238. }
  239. void CacheBase::clear() throw ()
  240. {
  241. lock();
  242. // Release all the entries.
  243. for (iterator i = begin(); i != end(); (i++)->Release()) { }
  244. // Reset the LRU list.
  245. flink = blink = listAsEntry();
  246. // Reset the hash table.
  247. memset(table, 0, sizeof(Bucket) * buckets);
  248. // There's nothing left.
  249. entries = 0;
  250. unlock();
  251. }
  252. ULONG CacheBase::evict() throw ()
  253. {
  254. lock();
  255. ULONG retval = entries;
  256. unsafe_evict();
  257. retval = entries - retval;
  258. unlock();
  259. return retval;
  260. }
  261. bool CacheBase::erase(const void* key) throw ()
  262. {
  263. CacheEntry* entry = remove(key);
  264. return entry ? entry->Release(), true : false;
  265. }
  266. CacheEntry* CacheBase::find(const void* key) throw ()
  267. {
  268. lock();
  269. unsafe_evict();
  270. // Look it up in the hash table.
  271. HashTableEntry* he = HashTableBase::find(key);
  272. CacheEntry* entry;
  273. if (he)
  274. {
  275. // Whenever someone reads an entry, we reset the TTL.
  276. entry = static_cast<CacheEntry*>(he);
  277. entry->setExpiry(ttl);
  278. entry->removeFromList();
  279. push_front(entry);
  280. }
  281. else
  282. {
  283. entry = NULL;
  284. }
  285. unlock();
  286. return entry;
  287. }
  288. bool CacheBase::insert(
  289. CacheEntry& entry,
  290. bool checkForDuplicates
  291. ) throw ()
  292. {
  293. lock();
  294. unsafe_evict();
  295. bool added = HashTableBase::insert(entry, checkForDuplicates);
  296. if (added)
  297. {
  298. entry.setExpiry(ttl);
  299. push_front(&entry);
  300. }
  301. unlock();
  302. return added;
  303. }
  304. CacheEntry* CacheBase::remove(const void* key) throw ()
  305. {
  306. lock();
  307. CacheEntry* entry = unsafe_remove(key);
  308. unlock();
  309. return entry;
  310. }
  311. void CacheBase::unsafe_evict() throw ()
  312. {
  313. while (entries > maxEntries )
  314. {
  315. unsafe_remove(blink->getKey())->Release();
  316. }
  317. while (entries && blink->isExpired())
  318. {
  319. unsafe_remove(blink->getKey())->Release();
  320. }
  321. }
  322. CacheEntry* CacheBase::unsafe_remove(const void* key) throw ()
  323. {
  324. CacheEntry* entry = static_cast<CacheEntry*>(HashTableBase::remove(key));
  325. if (entry)
  326. {
  327. entry->removeFromList();
  328. }
  329. return entry;
  330. }
  331. void CacheBase::push_front(CacheEntry* entry) throw ()
  332. {
  333. CacheEntry* listHead = listAsEntry();
  334. CacheEntry* oldFlink = listHead->flink;
  335. entry->flink = oldFlink;
  336. entry->blink = listHead;
  337. oldFlink->blink = entry;
  338. listHead->flink = entry;
  339. }