1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00
kickc/src/test/kc/library/lru-cache-2-mem-varcall/lru-cache-mem-varcall.c
Sven Van de Velde e371d304f6 - Fixed test cases. Full retest of all test cases.
- Create the possibility of forward declaration of structs as part of the language.
- Generation of forward declarations in the .asm header files.
- Treat global variables as part of global memory, even in libraries. Ensure that globals are not overwritten when importing.
- Ensure that the various compilation models (stack, var, phi) in combination with the memory models (zp, mem) result in proper execution of code and proper memory allocation etc, etc.
- Added the lru-cache logic to properly test the compilation and memory model combinations. (A lot of bugfixes as a result!)
2024-01-16 08:06:14 +03:00

421 lines
12 KiB
C

/**
* @file lru-cache.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @brief Least Recently Used Cache using a hash table and a double linked list, searchable.
* To store fast and retrieve fast elements from an array. To search fast the last used element and delete it.
* @version 0.1
* @date 2022-09-02
*
* @copyright Copyright (c) 2022
*
*/
#pragma var_model(mem, local_mem, global_zp, parameter_zp)
#pragma asm_library
#pragma calling(__varcall)
#pragma asm_export(lru_cache_init)
#pragma asm_export(lru_cache_find_last)
#pragma asm_export(lru_cache_is_max)
#pragma asm_export(lru_cache_index)
#pragma asm_export(lru_cache_get)
#pragma asm_export(lru_cache_set)
#pragma asm_export(lru_cache_data)
#pragma asm_export(lru_cache_insert)
#pragma asm_export(lru_cache_delete)
#pragma asm_export(lru_cache_display)
#pragma calling(__phicall)
#include <conio-mem-varcall_asm.h>
#define LRU_CACHE_MAX 24
#define LRU_CACHE_SIZE 32
#include "lru-cache-mem-varcall.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
lru_cache_table_t lru_cache;
void lru_cache_init()
{
// The size of the elements can never exceed 256 bytes!!!
memset_fast(lru_cache.key, 0xFF, sizeof(lru_cache_key_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.data, 0xFF, sizeof(lru_cache_data_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.next, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.prev, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.link, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
lru_cache.first = 0xFF;
lru_cache.last = 0xFF;
lru_cache.count = 0;
lru_cache.size = LRU_CACHE_SIZE;
}
// __mem unsigned char lru_cache_seed;
lru_cache_index_t lru_cache_hash(lru_cache_key_t key)
{
lru_cache_index_t hash_result = (lru_cache_index_t)(key % LRU_CACHE_SIZE);
return hash_result;
}
// inline lru_cache_index_t lru_cache_hash(lru_cache_key_t key) {
// lru_cache_seed = key;
// asm {
// lda lru_cache_seed
// beq !doEor+
// asl
// beq !noEor+
// bcc !noEor+
// !doEor: eor #$2b
// !noEor: sta lru_cache_seed
// }
// return lru_cache_seed % LRU_CACHE_SIZE;
// }
inline bool lru_cache_is_max()
{
return lru_cache.count >= LRU_CACHE_MAX;
}
lru_cache_key_t lru_cache_find_last()
{
return lru_cache.key[lru_cache.last];
}
lru_cache_index_t lru_cache_find_empty(lru_cache_index_t index)
{
while (lru_cache.key[index] != LRU_CACHE_NOTHING) {
index++;
index %= LRU_CACHE_SIZE;
}
return index;
}
lru_cache_index_t lru_cache_find_duplicate(lru_cache_index_t index, lru_cache_index_t link)
{
// First find the last duplicate node.
while (lru_cache.link[index] != link && lru_cache.link[index] != LRU_CACHE_INDEX_NULL) {
index = lru_cache.link[index];
}
return index;
}
lru_cache_index_t lru_cache_index(lru_cache_key_t key)
{
lru_cache_index_t index = lru_cache_hash(key);
// Search till index == 0xFF, following the links.
while (index != LRU_CACHE_INDEX_NULL) {
if (lru_cache.key[index] == key) {
return index;
}
index = lru_cache.link[index];
}
return LRU_CACHE_INDEX_NULL;
}
lru_cache_data_t lru_cache_get(lru_cache_index_t index)
{
if (index != LRU_CACHE_INDEX_NULL) {
lru_cache_data_t data = lru_cache.data[index];
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
// Delete the node from the list.
lru_cache.next[prev] = next;
//lru_cache.next[next] = prev;
lru_cache.prev[next] = prev;
//lru_cache.prev[prev] = next;
// Reassign first and last node.
if (index == lru_cache.first) {
lru_cache.first = next;
}
if (index == lru_cache.last) {
lru_cache.last = prev;
}
// Now insert the node as the first node in the list.
lru_cache.next[index] = lru_cache.first;
lru_cache.prev[lru_cache.first] = index;
lru_cache.next[lru_cache.last] = index;
lru_cache.prev[index] = lru_cache.last;
// Now the first node in the list is the node referenced!
// All other nodes are moved one position down!
lru_cache.first = index;
lru_cache.last = lru_cache.prev[index];
return data;
}
return LRU_CACHE_NOTHING;
}
lru_cache_data_t lru_cache_set(lru_cache_index_t index, lru_cache_data_t data)
{
if (index != LRU_CACHE_INDEX_NULL) {
lru_cache.data[index] = data;
lru_cache_data_t data = lru_cache_get(index);
return data;
}
return LRU_CACHE_NOTHING;
}
lru_cache_data_t lru_cache_data(lru_cache_index_t index)
{
return lru_cache.data[index];
}
inline void lru_cache_move_link(lru_cache_index_t link, lru_cache_index_t index)
{
// Here we move the node at the index to the new link, and set the head link to the new link.
lru_cache_index_t l = lru_cache.link[index];
lru_cache.link[link] = l;
// This results in incorrect code!
// lru_cache.key[link] = lru_cache.key[index];
// lru_cache.data[link] = lru_cache.data[index];
lru_cache_key_t key = lru_cache.key[index];
lru_cache.key[link] = key;
lru_cache_data_t data = lru_cache.data[index];
lru_cache.data[link] = data;
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
lru_cache.next[link] = next;
lru_cache.prev[link] = prev;
lru_cache.next[prev] = link;
lru_cache.prev[next] = link;
// todo first and last
if (lru_cache.last == index) {
lru_cache.last = link;
}
if (lru_cache.first == index) {
lru_cache.first = link;
}
lru_cache.key[index] = LRU_CACHE_NOTHING;
lru_cache.data[index] = LRU_CACHE_NOTHING;
lru_cache.next[index] = LRU_CACHE_INDEX_NULL;
lru_cache.prev[index] = LRU_CACHE_INDEX_NULL;
lru_cache.link[index] = LRU_CACHE_INDEX_NULL;
}
lru_cache_index_t lru_cache_find_head(lru_cache_index_t index)
{
lru_cache_key_t key_link = lru_cache.key[index];
lru_cache_index_t head_link = lru_cache_hash(key_link);
if (head_link != index) {
return head_link;
} else {
return LRU_CACHE_INDEX_NULL;
}
}
lru_cache_index_t lru_cache_insert(lru_cache_key_t key, lru_cache_data_t data)
{
lru_cache_index_t index = lru_cache_hash(key);
// Check if there is already a link node in place in the hash table at the index.
lru_cache_index_t link_head = lru_cache_find_head(index);
lru_cache_index_t link_prev = lru_cache_find_duplicate(link_head, index);
if (lru_cache.key[index] != LRU_CACHE_NOTHING && link_head != LRU_CACHE_INDEX_NULL) {
// There is already a link node, so this node is not a head node and needs to be moved.
// Get the head node of this chain, we know this because we can get the head of the key.
// The link of the head_link must be changed once the new place of the link node has been found.
lru_cache_index_t link = lru_cache_find_empty(index);
lru_cache_move_link(link, index);
lru_cache.link[link_prev] = link;
}
// The index is at the head of a chain and is either duplicates or empty.
// We just follow the duplicate chain and find the last duplicate.
lru_cache_index_t index_prev = lru_cache_find_duplicate(index, LRU_CACHE_INDEX_NULL);
// From the last duplicate position, we search for the first free node.
index = lru_cache_find_empty(index_prev);
// We set the link of the free node to INDEX_NULL,
// and point the link of the previous node to the empty node.
// index != index_prev indicates there is a duplicate chain.
lru_cache.link[index] = LRU_CACHE_INDEX_NULL;
if (index_prev != index) {
lru_cache.link[index_prev] = index;
}
// Now assign the key and the data.
lru_cache.key[index] = key;
lru_cache.data[index] = data;
// And set the lru chain.
if (lru_cache.first == 0xff) {
lru_cache.first = index;
}
if (lru_cache.last == 0xff) {
lru_cache.last = index;
}
// Now insert the node as the first node in the list.
lru_cache.next[index] = lru_cache.first;
lru_cache.prev[lru_cache.first] = index;
lru_cache.next[lru_cache.last] = index;
lru_cache.prev[index] = lru_cache.last;
lru_cache.first = index;
lru_cache.count++;
return index;
}
lru_cache_data_t lru_cache_delete(lru_cache_key_t key)
{
lru_cache_index_t index = lru_cache_hash(key);
// move in array until an empty
lru_cache_index_t index_prev = LRU_CACHE_INDEX_NULL;
while (lru_cache.key[index] != LRU_CACHE_NOTHING) {
if (lru_cache.key[index] == key) {
lru_cache_data_t data = lru_cache.data[index];
// First remove the index node.
lru_cache.key[index] = LRU_CACHE_NOTHING;
lru_cache.data[index] = LRU_CACHE_NOTHING;
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
lru_cache.next[index] = LRU_CACHE_INDEX_NULL;
lru_cache.prev[index] = LRU_CACHE_INDEX_NULL;
if (lru_cache.next[index] == index) {
// Reset first and last node.
lru_cache.first = 0xff;
lru_cache.last = 0xff;
} else {
// Delete the node from the list.
lru_cache.next[prev] = next;
lru_cache.prev[next] = prev;
// Reassign first and last node.
if (index == lru_cache.first) {
lru_cache.first = next;
}
if (index == lru_cache.last) {
lru_cache.last = prev;
}
}
lru_cache_index_t link = lru_cache.link[index];
if (index_prev != LRU_CACHE_INDEX_NULL) {
// The node is not the first node but the middle of a list.
lru_cache.link[index_prev] = link;
}
if (link != LRU_CACHE_INDEX_NULL) {
// The head is the start of a duplicate chain.
// Simply move the next node in the duplicate chain as the new head.
lru_cache_move_link(index, link);
}
lru_cache.count--;
return data;
}
index_prev = index;
index = lru_cache.link[index];
}
return LRU_CACHE_NOTHING;
}
// Only for debugging
void lru_cache_display(char x, char y) {
unsigned char col = 0;
gotoxy(x, y);
printf("least recently used cache statistics\n");
printf("size:%02x, first:%02x, last:%02x, cnt:%2x\n\n", LRU_CACHE_SIZE, lru_cache.first, lru_cache.last, lru_cache.count);
printf(" ");
do {
printf(" %1x/%1x ", col, col + 8);
col++;
} while (col < 4);
printf("\n");
col = 0;
lru_cache_index_t index_row = 0;
do {
lru_cache_index_t index = index_row;
printf("%02x:", index);
do {
if (lru_cache.key[index] != LRU_CACHE_NOTHING) {
printf(" %04x:", lru_cache.key[index]);
printf("%02x", lru_cache.link[index]);
} else {
printf(" ----:--");
}
index++;
} while (index < index_row + 4);
printf("\n");
index = index_row;
printf(" :");
do {
printf(" %02x:", lru_cache.next[index]);
printf("%02x ", lru_cache.prev[index]);
index++;
} while (index < index_row + 4);
printf("\n");
index_row += 4;
} while (index_row < 4*4*2);
printf("\n");
printf("least recently used sequence\n");
lru_cache_index_t index = lru_cache.first;
lru_cache_index_t count = 0;
col = 0;
while (count < 6) {
if (count < lru_cache.count)
printf(" %4x", lru_cache.key[index]);
else
printf(" ");
//printf(" %4x %3uN %3uP ", lru_cache.key[cache_index], lru_cache.next[cache_index], lru_cache.prev[cache_index]);
index = lru_cache.next[index];
count++;
}
}