mirror of
https://github.com/Ed94/VEFontCache-Odin.git
synced 2025-08-05 14:42:42 -07:00
244 lines
5.8 KiB
Odin
244 lines
5.8 KiB
Odin
package VEFontCache
|
|
|
|
/*
|
|
The choice was made to keep the LRU cache implementation as close to the original as possible.
|
|
*/
|
|
|
|
import "base:runtime"
|
|
|
|
PoolListIter :: i32
|
|
PoolListValue :: u64
|
|
|
|
PoolListItem :: struct {
|
|
prev : PoolListIter,
|
|
next : PoolListIter,
|
|
value : PoolListValue,
|
|
}
|
|
|
|
PoolList :: struct {
|
|
items : [dynamic]PoolListItem,
|
|
free_list : [dynamic]PoolListIter,
|
|
front : PoolListIter,
|
|
back : PoolListIter,
|
|
size : i32,
|
|
capacity : i32,
|
|
dbg_name : string,
|
|
}
|
|
|
|
pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = "" )
|
|
{
|
|
error : AllocatorError
|
|
pool.items, error = make( [dynamic]PoolListItem, int(capacity) )
|
|
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array")
|
|
resize( & pool.items, capacity )
|
|
|
|
pool.free_list, error = make( [dynamic]PoolListIter, len = 0, cap = int(capacity) )
|
|
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array")
|
|
resize( & pool.free_list, capacity )
|
|
|
|
pool.capacity = capacity
|
|
|
|
pool.dbg_name = dbg_name
|
|
using pool
|
|
|
|
for id in 0 ..< capacity {
|
|
free_list[id] = i32(id)
|
|
items[id] = {
|
|
prev = -1,
|
|
next = -1,
|
|
}
|
|
}
|
|
|
|
front = -1
|
|
back = -1
|
|
}
|
|
|
|
pool_list_free :: proc( pool : ^PoolList ) {
|
|
// TODO(Ed): Implement
|
|
}
|
|
|
|
pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) {
|
|
reload_array( & pool.items, allocator )
|
|
reload_array( & pool.free_list, allocator )
|
|
}
|
|
|
|
pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
|
|
{
|
|
using pool
|
|
if size >= capacity do return
|
|
|
|
length := len(free_list)
|
|
assert( length > 0 )
|
|
assert( length == int(capacity - size) )
|
|
|
|
id := free_list[ len(free_list) - 1 ]
|
|
if pool.dbg_name != "" {
|
|
logf("pool_list: back %v", id)
|
|
}
|
|
pop( & free_list )
|
|
items[ id ].prev = -1
|
|
items[ id ].next = front
|
|
items[ id ].value = value
|
|
if pool.dbg_name != "" {
|
|
logf("pool_list: pushed %v into id %v", value, id)
|
|
}
|
|
|
|
if front != -1 do items[ front ].prev = id
|
|
if back == -1 do back = id
|
|
front = id
|
|
size += 1
|
|
}
|
|
|
|
pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
|
|
{
|
|
using pool
|
|
if size <= 0 do return
|
|
assert( iter >= 0 && iter < i32(capacity) )
|
|
assert( len(free_list) == int(capacity - size) )
|
|
|
|
iter_node := & items[ iter ]
|
|
prev := iter_node.prev
|
|
next := iter_node.next
|
|
|
|
if iter_node.prev != -1 do items[ prev ].next = iter_node.next
|
|
if iter_node.next != -1 do items[ next ].prev = iter_node.prev
|
|
|
|
if front == iter do front = iter_node.next
|
|
if back == iter do back = iter_node.prev
|
|
|
|
iter_node.prev = -1
|
|
iter_node.next = -1
|
|
iter_node.value = 0
|
|
append( & free_list, iter )
|
|
|
|
size -= 1
|
|
if size == 0 {
|
|
back = -1
|
|
front = -1
|
|
}
|
|
}
|
|
|
|
pool_list_move_to_front :: #force_inline proc( pool : ^PoolList, iter : PoolListIter )
|
|
{
|
|
using pool
|
|
|
|
if front == iter do return
|
|
|
|
item := & items[iter]
|
|
if item.prev != -1 do items[ item.prev ].next = item.next
|
|
if item.next != -1 do items[ item.next ].prev = item.prev
|
|
if back == iter do back = item.prev
|
|
|
|
item.prev = -1
|
|
item.next = front
|
|
items[ front ].prev = iter
|
|
front = iter
|
|
}
|
|
|
|
pool_list_peek_back :: #force_inline proc ( pool : ^PoolList ) -> PoolListValue {
|
|
assert( pool.back != - 1 )
|
|
value := pool.items[ pool.back ].value
|
|
return value
|
|
}
|
|
|
|
pool_list_pop_back :: #force_inline proc( pool : ^PoolList ) -> PoolListValue {
|
|
if pool.size <= 0 do return 0
|
|
assert( pool.back != -1 )
|
|
|
|
value := pool.items[ pool.back ].value
|
|
pool_list_erase( pool, pool.back )
|
|
return value
|
|
}
|
|
|
|
LRU_Link :: struct {
|
|
pad_top : u64,
|
|
value : i32,
|
|
ptr : PoolListIter,
|
|
pad_bottom : u64,
|
|
}
|
|
|
|
LRU_Cache :: struct {
|
|
capacity : i32,
|
|
num : i32,
|
|
table : map[u64]LRU_Link,
|
|
key_queue : PoolList,
|
|
}
|
|
|
|
LRU_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) {
|
|
error : AllocatorError
|
|
cache.capacity = capacity
|
|
cache.table, error = make( map[u64]LRU_Link, uint(capacity) )
|
|
assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table")
|
|
|
|
pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name )
|
|
}
|
|
|
|
LRU_free :: proc( cache : ^LRU_Cache ) {
|
|
// TODO(Ed): Implement
|
|
}
|
|
|
|
LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) {
|
|
reload_map( & cache.table, allocator )
|
|
pool_list_reload( & cache.key_queue, allocator )
|
|
}
|
|
|
|
LRU_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) {
|
|
link, success := cache.table[key]
|
|
return link, success
|
|
}
|
|
|
|
LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 {
|
|
if link, ok := &cache.table[ key ]; ok {
|
|
pool_list_move_to_front(&cache.key_queue, link.ptr)
|
|
return link.value
|
|
}
|
|
return -1
|
|
}
|
|
|
|
LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 {
|
|
if cache.key_queue.size >= cache.capacity {
|
|
evict := pool_list_peek_back( & cache.key_queue )
|
|
return evict
|
|
}
|
|
return 0xFFFFFFFFFFFFFFFF
|
|
}
|
|
|
|
LRU_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 {
|
|
iter, success := LRU_find( cache, key, must_find )
|
|
if success == false {
|
|
return -1
|
|
}
|
|
return iter.value
|
|
}
|
|
|
|
LRU_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
|
|
{
|
|
if link, ok := & cache.table[ key ]; ok {
|
|
pool_list_move_to_front( & cache.key_queue, link.ptr )
|
|
link.value = value
|
|
return key
|
|
}
|
|
|
|
evict := key
|
|
if cache.key_queue.size >= cache.capacity {
|
|
evict = pool_list_pop_back(&cache.key_queue)
|
|
delete_key(&cache.table, evict)
|
|
cache.num -= 1
|
|
}
|
|
|
|
pool_list_push_front(&cache.key_queue, key)
|
|
cache.table[key] = LRU_Link{
|
|
value = value,
|
|
ptr = cache.key_queue.front,
|
|
}
|
|
cache.num += 1
|
|
return evict
|
|
}
|
|
|
|
LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
|
|
link, success := LRU_find( cache, key )
|
|
pool_list_erase( & cache.key_queue, link.ptr )
|
|
pool_list_push_front( & cache.key_queue, key )
|
|
link.ptr = cache.key_queue.front
|
|
}
|