mirror of
https://github.com/libretro/blastem.git
synced 2026-11-12 18:37:45 -10:00
319 lines
6.6 KiB
C
319 lines
6.6 KiB
C
/*
|
|
Copyright 2013 Michael Pavone
|
|
This file is part of BlastEm.
|
|
BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
|
|
*/
|
|
#include "tern.h"
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include "util.h"
|
|
|
|
tern_node * tern_insert(tern_node * head, char const * key, tern_val value, uint8_t valtype)
|
|
{
|
|
tern_node ** cur = &head;
|
|
while(*key)
|
|
{
|
|
if (*cur) {
|
|
while(*cur && (*cur)->el != *key)
|
|
{
|
|
if (*key < (*cur)->el) {
|
|
cur = &(*cur)->left;
|
|
} else {
|
|
cur = &(*cur)->right;
|
|
}
|
|
}
|
|
}
|
|
if (!*cur) {
|
|
*cur = malloc(sizeof(tern_node));
|
|
(*cur)->left = NULL;
|
|
(*cur)->right = NULL;
|
|
(*cur)->straight.next = NULL;
|
|
(*cur)->el = *key;
|
|
(*cur)->valtype = TVAL_NONE;
|
|
}
|
|
cur = &((*cur)->straight.next);
|
|
key++;
|
|
}
|
|
while(*cur && (*cur)->el)
|
|
{
|
|
cur = &(*cur)->left;
|
|
}
|
|
if (!*cur) {
|
|
*cur = malloc(sizeof(tern_node));
|
|
(*cur)->left = NULL;
|
|
(*cur)->right = NULL;
|
|
(*cur)->el = 0;
|
|
(*cur)->valtype = TVAL_NONE;
|
|
}
|
|
if ((*cur)->valtype == TVAL_PTR) {
|
|
//not freeing tern nodes can also cause leaks, but handling freeing those here is problematic
|
|
//since updating a sub-tree may involve creating a new root node
|
|
free((*cur)->straight.value.ptrval);
|
|
}
|
|
(*cur)->straight.value = value;
|
|
(*cur)->valtype = valtype;
|
|
return head;
|
|
}
|
|
|
|
uint8_t tern_find(tern_node * head, char const * key, tern_val *ret)
|
|
{
|
|
tern_node * cur = head;
|
|
while (cur)
|
|
{
|
|
if (cur->el == *key) {
|
|
if (*key) {
|
|
cur = cur->straight.next;
|
|
key++;
|
|
} else {
|
|
*ret = cur->straight.value;
|
|
return cur->valtype;
|
|
}
|
|
} else if (*key < cur->el) {
|
|
cur = cur->left;
|
|
} else {
|
|
cur = cur->right;
|
|
}
|
|
}
|
|
return TVAL_NONE;
|
|
}
|
|
|
|
tern_node * tern_find_prefix(tern_node * head, char const * key)
|
|
{
|
|
tern_node * cur = head;
|
|
while (cur && *key)
|
|
{
|
|
if (cur->el == *key) {
|
|
cur = cur->straight.next;
|
|
key++;
|
|
} else if (*key < cur->el) {
|
|
cur = cur->left;
|
|
} else {
|
|
cur = cur->right;
|
|
}
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
intptr_t tern_find_int(tern_node * head, char const * key, intptr_t def)
|
|
{
|
|
tern_val ret;
|
|
uint8_t valtype = tern_find(head, key, &ret);
|
|
if (valtype == TVAL_INT) {
|
|
return ret.intval;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
tern_node * tern_insert_int(tern_node * head, char const * key, intptr_t value)
|
|
{
|
|
tern_val val;
|
|
val.intval = value;
|
|
return tern_insert(head, key, val, TVAL_INT);
|
|
}
|
|
|
|
void * tern_find_ptr_default(tern_node * head, char const * key, void * def)
|
|
{
|
|
tern_val ret;
|
|
uint8_t valtype = tern_find(head, key, &ret);
|
|
if (valtype == TVAL_PTR) {
|
|
return ret.ptrval;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
void * tern_find_ptr(tern_node * head, char const * key)
|
|
{
|
|
return tern_find_ptr_default(head, key, NULL);
|
|
}
|
|
|
|
tern_node *tern_find_node(tern_node *head, char const *key)
|
|
{
|
|
tern_val ret;
|
|
uint8_t valtype = tern_find(head, key, &ret);
|
|
if (valtype == TVAL_NODE) {
|
|
return ret.ptrval;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t tern_delete(tern_node **head, char const *key, tern_val *out)
|
|
{
|
|
tern_node *cur = *head, **last = head;
|
|
while (cur)
|
|
{
|
|
if (cur->el == *key) {
|
|
if (*key) {
|
|
last = &cur->straight.next;
|
|
cur = cur->straight.next;
|
|
key++;
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (*key < cur->el) {
|
|
last = &cur->left;
|
|
cur = cur->left;
|
|
} else {
|
|
last = &cur->right;
|
|
cur = cur->right;
|
|
}
|
|
}
|
|
if (!cur) {
|
|
return TVAL_NONE;
|
|
}
|
|
*last = cur->right;
|
|
uint8_t valtype = cur->valtype;
|
|
if (out) {
|
|
*out = cur->straight.value;
|
|
}
|
|
free(cur);
|
|
return valtype;
|
|
}
|
|
|
|
tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def, uint8_t req_valtype)
|
|
{
|
|
tern_val ret;
|
|
while (*key)
|
|
{
|
|
uint8_t valtype = tern_find(head, key, &ret);
|
|
if (!valtype) {
|
|
return def;
|
|
}
|
|
key = key + strlen(key) + 1;
|
|
if (*key) {
|
|
if (valtype != TVAL_NODE) {
|
|
return def;
|
|
}
|
|
head = ret.ptrval;
|
|
} else if (req_valtype && req_valtype != valtype) {
|
|
return def;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
tern_val tern_find_path(tern_node *head, char const *key, uint8_t valtype)
|
|
{
|
|
tern_val def;
|
|
def.ptrval = NULL;
|
|
return tern_find_path_default(head, key, def, valtype);
|
|
}
|
|
|
|
tern_node * tern_insert_ptr(tern_node * head, char const * key, void * value)
|
|
{
|
|
tern_val val;
|
|
val.ptrval = value;
|
|
return tern_insert(head, key, val, TVAL_PTR);
|
|
}
|
|
|
|
tern_node * tern_insert_node(tern_node *head, char const *key, tern_node *value)
|
|
{
|
|
tern_val val;
|
|
val.ptrval = value;
|
|
return tern_insert(head, key, val, TVAL_NODE);
|
|
}
|
|
|
|
tern_node *tern_insert_path(tern_node *head, char const *key, tern_val val, uint8_t valtype)
|
|
{
|
|
const char *next_key = key + strlen(key) + 1;
|
|
if (*next_key) {
|
|
tern_node *child = tern_find_node(head, key);
|
|
child = tern_insert_path(child, next_key, val, valtype);
|
|
return tern_insert_node(head, key, child);
|
|
} else {
|
|
return tern_insert(head, key, val, valtype);
|
|
}
|
|
}
|
|
|
|
uint8_t tern_delete_path(tern_node **head, char const *key, tern_val *out)
|
|
{
|
|
const char *next_key = key + strlen(key) + 1;
|
|
if (*next_key) {
|
|
tern_node *child = tern_find_node(*head, key);
|
|
if (!child) {
|
|
return TVAL_NONE;
|
|
}
|
|
tern_node *tmp = child;
|
|
uint8_t valtype = tern_delete_path(&tmp, next_key, out);
|
|
if (tmp != child) {
|
|
*head = tern_insert_node(*head, key, tmp);
|
|
}
|
|
return valtype;
|
|
} else {
|
|
return tern_delete(head, key, out);
|
|
}
|
|
}
|
|
|
|
uint32_t tern_count(tern_node *head)
|
|
{
|
|
uint32_t count = 0;
|
|
if (head->left) {
|
|
count += tern_count(head->left);
|
|
}
|
|
if (head->right) {
|
|
count += tern_count(head->right);
|
|
}
|
|
if (!head->el) {
|
|
count++;
|
|
} else if (head->straight.next) {
|
|
count += tern_count(head->straight.next);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
#define MAX_ITER_KEY 127
|
|
void tern_foreach_int(tern_node *head, iter_fun fun, void *data, char *keybuf, int pos)
|
|
{
|
|
if (!head->el) {
|
|
keybuf[pos] = 0;
|
|
fun(keybuf, head->straight.value, head->valtype, data);
|
|
}
|
|
if (head->left) {
|
|
tern_foreach_int(head->left, fun, data, keybuf, pos);
|
|
}
|
|
if (head->el && head->straight.next) {
|
|
if (pos == MAX_ITER_KEY) {
|
|
fatal_error("tern_foreach_int: exceeded maximum key size");
|
|
}
|
|
keybuf[pos] = head->el;
|
|
tern_foreach_int(head->straight.next, fun, data, keybuf, pos+1);
|
|
}
|
|
if (head->right) {
|
|
tern_foreach_int(head->right, fun, data, keybuf, pos);
|
|
}
|
|
}
|
|
|
|
void tern_foreach(tern_node *head, iter_fun fun, void *data)
|
|
{
|
|
//lame, but good enough for my purposes
|
|
char key[MAX_ITER_KEY+1];
|
|
tern_foreach_int(head, fun, data, key, 0);
|
|
}
|
|
|
|
char * tern_int_key(uint32_t key, char * buf)
|
|
{
|
|
char * cur = buf;
|
|
while (key)
|
|
{
|
|
*(cur++) = (key & 0x7F) + 1;
|
|
key >>= 7;
|
|
}
|
|
*cur = 0;
|
|
return buf;
|
|
}
|
|
|
|
void tern_free(tern_node *head)
|
|
{
|
|
if (head->left) {
|
|
tern_free(head->left);
|
|
}
|
|
if (head->right) {
|
|
tern_free(head->right);
|
|
}
|
|
if (head->el) {
|
|
tern_free(head->straight.next);
|
|
}
|
|
free(head);
|
|
}
|