d96b02edca11cda772914871cec46a0cb2fd7293cb8536eea5ff4a2da6f88d483c23026ea7bb9ea8d76111eeb783291f18283ddffa33ddcb15bd2fcee85cb1 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { Dictionary } from './types';
  2. // Simple LRU cache use doubly linked list
  3. // @module zrender/core/LRU
  4. export class Entry<T> {
  5. value: T
  6. key: string | number
  7. next: Entry<T>
  8. prev: Entry<T>
  9. constructor(val: T) {
  10. this.value = val;
  11. }
  12. }
  13. /**
  14. * Simple double linked list. Compared with array, it has O(1) remove operation.
  15. * @constructor
  16. */
  17. export class LinkedList<T> {
  18. head: Entry<T>
  19. tail: Entry<T>
  20. private _len = 0
  21. /**
  22. * Insert a new value at the tail
  23. */
  24. insert(val: T): Entry<T> {
  25. const entry = new Entry(val);
  26. this.insertEntry(entry);
  27. return entry;
  28. }
  29. /**
  30. * Insert an entry at the tail
  31. */
  32. insertEntry(entry: Entry<T>) {
  33. if (!this.head) {
  34. this.head = this.tail = entry;
  35. }
  36. else {
  37. this.tail.next = entry;
  38. entry.prev = this.tail;
  39. entry.next = null;
  40. this.tail = entry;
  41. }
  42. this._len++;
  43. }
  44. /**
  45. * Remove entry.
  46. */
  47. remove(entry: Entry<T>) {
  48. const prev = entry.prev;
  49. const next = entry.next;
  50. if (prev) {
  51. prev.next = next;
  52. }
  53. else {
  54. // Is head
  55. this.head = next;
  56. }
  57. if (next) {
  58. next.prev = prev;
  59. }
  60. else {
  61. // Is tail
  62. this.tail = prev;
  63. }
  64. entry.next = entry.prev = null;
  65. this._len--;
  66. }
  67. /**
  68. * Get length
  69. */
  70. len(): number {
  71. return this._len;
  72. }
  73. /**
  74. * Clear list
  75. */
  76. clear() {
  77. this.head = this.tail = null;
  78. this._len = 0;
  79. }
  80. }
  81. /**
  82. * LRU Cache
  83. */
  84. export default class LRU<T> {
  85. private _list = new LinkedList<T>()
  86. private _maxSize = 10
  87. private _lastRemovedEntry: Entry<T>
  88. private _map: Dictionary<Entry<T>> = {}
  89. constructor(maxSize: number) {
  90. this._maxSize = maxSize;
  91. }
  92. /**
  93. * @return Removed value
  94. */
  95. put(key: string | number, value: T): T {
  96. const list = this._list;
  97. const map = this._map;
  98. let removed = null;
  99. if (map[key] == null) {
  100. const len = list.len();
  101. // Reuse last removed entry
  102. let entry = this._lastRemovedEntry;
  103. if (len >= this._maxSize && len > 0) {
  104. // Remove the least recently used
  105. const leastUsedEntry = list.head;
  106. list.remove(leastUsedEntry);
  107. delete map[leastUsedEntry.key];
  108. removed = leastUsedEntry.value;
  109. this._lastRemovedEntry = leastUsedEntry;
  110. }
  111. if (entry) {
  112. entry.value = value;
  113. }
  114. else {
  115. entry = new Entry(value);
  116. }
  117. entry.key = key;
  118. list.insertEntry(entry);
  119. map[key] = entry;
  120. }
  121. return removed;
  122. }
  123. get(key: string | number): T {
  124. const entry = this._map[key];
  125. const list = this._list;
  126. if (entry != null) {
  127. // Put the latest used entry in the tail
  128. if (entry !== list.tail) {
  129. list.remove(entry);
  130. list.insertEntry(entry);
  131. }
  132. return entry.value;
  133. }
  134. }
  135. /**
  136. * Clear the cache
  137. */
  138. clear() {
  139. this._list.clear();
  140. this._map = {};
  141. }
  142. len() {
  143. return this._list.len();
  144. }
  145. }