lru_cache.odin 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package container_lru
  2. import "core:runtime"
  3. import "core:intrinsics"
  4. _ :: runtime
  5. _ :: intrinsics
  6. Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
  7. prev, next: ^Node(Key, Value),
  8. key: Key,
  9. value: Value,
  10. }
  11. // Cache is an LRU cache. It automatically removes entries as new entries are
  12. // added if the capacity is reached. Entries are removed based on how recently
  13. // they were used where the oldest entries are removed first.
  14. Cache :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
  15. head: ^Node(Key, Value),
  16. tail: ^Node(Key, Value),
  17. entries: map[Key]^Node(Key, Value),
  18. count: int,
  19. capacity: int,
  20. node_allocator: runtime.Allocator,
  21. on_remove: proc(key: Key, value: Value, user_data: rawptr),
  22. on_remove_user_data: rawptr,
  23. }
  24. // init initializes a Cache
  25. init :: proc(c: ^$C/Cache($Key, $Value), capacity: int, entries_allocator := context.allocator, node_allocator := context.allocator) {
  26. c.entries.allocator = entries_allocator
  27. c.node_allocator = node_allocator
  28. c.capacity = capacity
  29. }
  30. // destroy deinitializes a Cachem
  31. destroy :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
  32. clear(c, call_on_remove)
  33. delete(c.entries)
  34. }
  35. // clear the contents of a Cache
  36. clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
  37. for _, node in c.entries {
  38. if call_on_remove {
  39. _call_on_remove(c, node)
  40. }
  41. free(node, c.node_allocator)
  42. }
  43. runtime.clear(&c.entries)
  44. c.head = nil
  45. c.tail = nil
  46. c.count = 0
  47. }
  48. // set the given key value pair. This operation updates the recent usage of the item.
  49. set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
  50. if e, ok := c.entries[key]; ok {
  51. e.value = value
  52. _pop_node(c, e)
  53. _push_front_node(c, e)
  54. return nil
  55. }
  56. e : ^Node(Key, Value) = nil
  57. assert(c.count <= c.capacity)
  58. if c.count == c.capacity {
  59. e = c.tail
  60. _remove_node(c, e)
  61. }
  62. else {
  63. c.count += 1
  64. e = new(Node(Key, Value), c.node_allocator) or_return
  65. }
  66. e.key = key
  67. e.value = value
  68. _push_front_node(c, e)
  69. c.entries[key] = e
  70. return nil
  71. }
  72. // get a value from the cache from a given key. This operation updates the usage of the item.
  73. get :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
  74. e: ^Node(Key, Value)
  75. e, ok = c.entries[key]
  76. if !ok {
  77. return
  78. }
  79. _pop_node(c, e)
  80. _push_front_node(c, e)
  81. return e.value, true
  82. }
  83. // get_ptr gets the pointer to a value the cache from a given key. This operation updates the usage of the item.
  84. get_ptr :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: ^Value, ok: bool) #optional_ok {
  85. e: ^Node(Key, Value)
  86. e, ok = c.entries[key]
  87. if !ok {
  88. return
  89. }
  90. _pop_node(c, e)
  91. _push_front_node(c, e)
  92. return &e.value, true
  93. }
  94. // peek gets the value from the cache from a given key without updating the recent usage.
  95. peek :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
  96. e: ^Node(Key, Value)
  97. e, ok = c.entries[key]
  98. if !ok {
  99. return
  100. }
  101. return e.value, true
  102. }
  103. // exists checks for the existence of a value from a given key without updating the recent usage.
  104. exists :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
  105. return key in c.entries
  106. }
  107. // remove removes an item from the cache.
  108. remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
  109. e, ok := c.entries[key]
  110. if !ok {
  111. return false
  112. }
  113. _remove_node(c, e)
  114. free(node, c.node_allocator)
  115. c.count -= 1
  116. return true
  117. }
  118. @(private)
  119. _remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
  120. if c.head == node {
  121. c.head = node.next
  122. }
  123. if c.tail == node {
  124. c.tail = node.prev
  125. }
  126. if node.prev != nil {
  127. node.prev.next = node.next
  128. }
  129. if node.next != nil {
  130. node.next.prev = node.prev
  131. }
  132. node.prev = nil
  133. node.next = nil
  134. delete_key(&c.entries, node.key)
  135. _call_on_remove(c, node)
  136. }
  137. @(private)
  138. _call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
  139. if c.on_remove != nil {
  140. c.on_remove(node.key, node.value, c.on_remove_user_data)
  141. }
  142. }
  143. @(private)
  144. _push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
  145. if c.head != nil {
  146. e.next = c.head
  147. e.next.prev = e
  148. }
  149. c.head = e
  150. if c.tail == nil {
  151. c.tail = e
  152. }
  153. e.prev = nil
  154. }
  155. @(private)
  156. _pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
  157. if e == nil {
  158. return
  159. }
  160. if c.head == e {
  161. c.head = e.next
  162. }
  163. if c.tail == e {
  164. c.tail = e.prev
  165. }
  166. if e.prev != nil {
  167. e.prev.next = e.next
  168. }
  169. if e.next != nil {
  170. e.next.prev = e.prev
  171. }
  172. e.prev = nil
  173. e.next = nil
  174. }