1 24 25 package com.mckoi.util; 26 27 38 39 public class Cache { 40 41 45 private int max_cache_size; 46 47 50 private int current_cache_size; 51 52 56 private int wipe_to; 57 58 61 private final ListNode[] node_hash; 62 63 66 private ListNode list_start; 67 68 71 private ListNode list_end; 72 73 77 public Cache(int hash_size, int max_size, int clean_percentage) { 78 if (clean_percentage >= 85) { 79 throw new RuntimeException ( 80 "Can't set to wipe more than 85% of the cache during clean."); 81 } 82 max_cache_size = max_size; 83 current_cache_size = 0; 84 wipe_to = max_size - ((clean_percentage * max_size) / 100); 85 86 node_hash = new ListNode[hash_size]; 87 88 list_start = null; 89 list_end = null; 90 } 91 92 public Cache(int max_size, int clean_percentage) { 93 this((max_size * 2) + 1, max_size, 20); 94 } 95 96 public Cache(int max_size) { 97 this(max_size, 20); 98 } 99 100 public Cache() { 101 this(50); 102 } 103 104 109 protected final int getHashSize() { 110 return (int) (max_cache_size * 2) + 1; 111 } 112 113 114 119 protected void checkClean() { 120 if (current_cache_size >= max_cache_size) { 123 clean(); 124 } 125 } 126 127 131 protected boolean shouldWipeMoreNodes() { 132 return (current_cache_size >= wipe_to); 133 } 134 135 139 protected void notifyWipingNode(Object ob) { 140 } 141 142 150 protected void notifyGetWalks(long total_walks, long total_get_ops) { 151 } 152 153 155 158 private long total_gets = 0; 159 private long get_total = 0; 160 161 165 private ListNode getFromHash(Object key) { 166 int hash = key.hashCode(); 167 int index = (hash & 0x7FFFFFFF) % node_hash.length; 168 int get_count = 1; 169 170 for (ListNode e = node_hash[index]; e != null; e = e.next_hash_entry) { 171 if (key.equals(e.key)) { 172 ++total_gets; 173 get_total += get_count; 174 175 if ((total_gets & 0x01FFF) == 0) { 178 try { 179 notifyGetWalks(get_total, total_gets); 180 if (get_total > (65536 * 65536)) { 182 get_total = 0; 183 total_gets = 0; 184 } 185 } 186 catch (Throwable except) { } 187 } 188 189 if (get_count > 1) { 191 bringToHead(e); 192 } 193 return e; 194 } 195 ++get_count; 196 } 197 return null; 198 } 199 200 203 private ListNode putIntoHash(ListNode node) { 204 int hash = node.key.hashCode(); 206 int index = (hash & 0x7FFFFFFF) % node_hash.length; 207 Object key = node.key; 208 for (ListNode e = node_hash[index] ; e != null ; e = e.next_hash_entry) { 209 if (key.equals(e.key)) { 210 throw new Error ( 211 "ListNode with same key already in the hash - remove first."); 212 } 213 } 214 215 node.next_hash_entry = node_hash[index]; 217 node_hash[index] = node; 218 219 return node; 220 } 221 222 226 private ListNode removeFromHash(Object key) { 227 int hash = key.hashCode(); 229 int index = (hash & 0x7FFFFFFF) % node_hash.length; 230 ListNode prev = null; 231 for (ListNode e = node_hash[index] ; e != null ; e = e.next_hash_entry) { 232 if (key.equals(e.key)) { 233 if (prev == null) { 235 node_hash[index] = e.next_hash_entry; 236 } 237 else { 238 prev.next_hash_entry = e.next_hash_entry; 239 } 240 return e; 241 } 242 prev = e; 243 } 244 245 return null; 247 } 248 249 252 private void clearHash() { 253 for (int i = node_hash.length - 1; i >= 0; --i) { 254 node_hash[i] = null; 255 } 256 } 257 258 259 261 265 public final int nodeCount() { 266 return current_cache_size; 267 } 268 269 272 public final void put(Object key, Object ob) { 273 274 checkClean(); 276 277 279 ListNode node = getFromHash(key); 280 if (node == null) { 281 282 node = createListNode(); 283 node.key = key; 284 node.contents = ob; 285 286 node.next = list_start; 288 node.previous = null; 289 list_start = node; 290 if (node.next == null) { 291 list_end = node; 292 } 293 else { 294 node.next.previous = node; 295 } 296 297 ++current_cache_size; 298 299 putIntoHash(node); 301 302 } 303 else { 304 305 308 node.contents = ob; 309 bringToHead(node); 310 311 } 312 313 } 314 315 319 public final Object get(Object key) { 320 ListNode node = getFromHash(key); 321 322 if (node != null) { 323 326 return node.contents; 327 } 328 329 return null; 330 } 331 332 336 public final Object remove(Object key) { 337 ListNode node = removeFromHash(key); 338 339 if (node != null) { 340 if (list_start == node) { 342 list_start = node.next; 343 if (list_start != null) { 344 list_start.previous = null; 345 } 346 else { 347 list_end = null; 348 } 349 } 350 else if (list_end == node) { 352 list_end = node.previous; 353 if (list_end != null) { 354 list_end.next = null; 355 } 356 else { 357 list_start = null; 358 } 359 } 360 else { 361 node.previous.next = node.next; 362 node.next.previous = node.previous; 363 } 364 365 --current_cache_size; 366 367 Object contents = node.contents; 368 369 node.contents = null; 371 node.key = null; 372 373 return contents; 374 } 375 376 return null; 377 } 378 379 382 public void removeAll() { 383 if (current_cache_size != 0) { 384 current_cache_size = 0; 385 clearHash(); 386 } 387 list_start = null; 388 list_end = null; 389 } 390 391 public void clear() { 392 removeAll(); 393 } 394 395 396 401 private final ListNode createListNode() { 402 return new ListNode(); 403 } 404 405 411 protected final int clean() { 412 413 ListNode node = list_end; 414 if (node == null) { 415 return 0; 416 } 417 418 int actual_count = 0; 419 while (node != null && shouldWipeMoreNodes()) { 420 notifyWipingNode(node.contents); 421 422 removeFromHash(node.key); 423 node.contents = null; 425 node.key = null; 426 ListNode old_node = node; 427 node = node.previous; 429 430 old_node.next = null; 432 old_node.previous = null; 433 434 --current_cache_size; 435 ++actual_count; 436 } 437 438 if (node != null) { 439 node.next = null; 440 list_end = node; 441 } 442 else { 443 list_start = null; 444 list_end = null; 445 } 446 447 return actual_count; 448 } 449 450 454 private final void bringToHead(ListNode node) { 455 if (list_start != node) { 456 457 ListNode next_node = node.next; 458 ListNode previous_node = node.previous; 459 460 node.next = list_start; 461 node.previous = null; 462 list_start = node; 463 node.next.previous = node; 464 465 if (next_node != null) { 466 next_node.previous = previous_node; 467 } 468 else { 469 list_end = previous_node; 470 } 471 previous_node.next = next_node; 472 473 } 474 } 475 476 477 479 482 static final class ListNode { 483 484 487 ListNode next; 488 ListNode previous; 489 490 494 ListNode next_hash_entry; 495 496 497 500 Object key; 501 502 505 Object contents; 506 507 } 508 509 } 510 | Popular Tags |