lru_cache.odin 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package container_lru
  2. import "base:runtime"
  3. import "base: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. } else {
  62. c.count += 1
  63. e = new(Node(Key, Value), c.node_allocator) or_return
  64. }
  65. e.key = key
  66. e.value = value
  67. _push_front_node(c, e)
  68. c.entries[key] = e
  69. return nil
  70. }
  71. // get a value from the cache from a given key. This operation updates the usage of the item.
  72. get :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
  73. e: ^Node(Key, Value)
  74. e, ok = c.entries[key]
  75. if !ok {
  76. return
  77. }
  78. _pop_node(c, e)
  79. _push_front_node(c, e)
  80. return e.value, true
  81. }
  82. // get_ptr gets the pointer to a value the cache from a given key. This operation updates the usage of the item.
  83. get_ptr :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: ^Value, ok: bool) #optional_ok {
  84. e: ^Node(Key, Value)
  85. e, ok = c.entries[key]
  86. if !ok {
  87. return
  88. }
  89. _pop_node(c, e)
  90. _push_front_node(c, e)
  91. return &e.value, true
  92. }
  93. // peek gets the value from the cache from a given key without updating the recent usage.
  94. peek :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
  95. e: ^Node(Key, Value)
  96. e, ok = c.entries[key]
  97. if !ok {
  98. return
  99. }
  100. return e.value, true
  101. }
  102. // exists checks for the existence of a value from a given key without updating the recent usage.
  103. exists :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
  104. return key in c.entries
  105. }
  106. // remove removes an item from the cache.
  107. remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
  108. e, ok := c.entries[key]
  109. if !ok {
  110. return false
  111. }
  112. _remove_node(c, e)
  113. free(node, c.node_allocator)
  114. c.count -= 1
  115. return true
  116. }
  117. @(private)
  118. _remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
  119. if c.head == node {
  120. c.head = node.next
  121. }
  122. if c.tail == node {
  123. c.tail = node.prev
  124. }
  125. if node.prev != nil {
  126. node.prev.next = node.next
  127. }
  128. if node.next != nil {
  129. node.next.prev = node.prev
  130. }
  131. node.prev = nil
  132. node.next = nil
  133. delete_key(&c.entries, node.key)
  134. _call_on_remove(c, node)
  135. }
  136. @(private)
  137. _call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
  138. if c.on_remove != nil {
  139. c.on_remove(node.key, node.value, c.on_remove_user_data)
  140. }
  141. }
  142. @(private)
  143. _push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
  144. if c.head != nil {
  145. e.next = c.head
  146. e.next.prev = e
  147. }
  148. c.head = e
  149. if c.tail == nil {
  150. c.tail = e
  151. }
  152. e.prev = nil
  153. }
  154. @(private)
  155. _pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
  156. if e == nil {
  157. return
  158. }
  159. if c.head == e {
  160. c.head = e.next
  161. }
  162. if c.tail == e {
  163. c.tail = e.prev
  164. }
  165. if e.prev != nil {
  166. e.prev.next = e.next
  167. }
  168. if e.next != nil {
  169. e.next.prev = e.prev
  170. }
  171. e.prev = nil
  172. e.next = nil
  173. }