acme/src/tree.c

214 lines
6.6 KiB
C

// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
// Copyright (C) 1998-2020 Marco Baye
// Have a look at "acme.c" for further info
//
// tree stuff
#include "tree.h"
#include "config.h"
#include "dynabuf.h"
#include "global.h"
#include "platform.h"
// Functions
// Compute hash value by exclusive ORing the node's ID string and write
// output to struct.
// This function is not allowed to change GlobalDynaBuf!
hash_t make_hash(struct ronode *node) {
register char byte;
register const char *read;
register hash_t tmp = 0;
read = node->id_string;
while ((byte = *read++))
tmp = ((tmp << 7) | (tmp >> (8 * sizeof(hash_t) - 7))) ^ byte;
node->hash_value = tmp;
return tmp;
}
// Link a predefined data set to a tree
static void add_node_to_tree(struct ronode **tree, struct ronode *node_to_add)
{
hash_t hash;
// compute hash value
hash = make_hash(node_to_add);
// search for NULL pointer to replace
while (*tree) {
// decide which way to go
if (hash > (*tree)->hash_value)
tree = &((*tree)->greater_than);
else
tree = &((*tree)->less_than_or_equal);
}
*tree = node_to_add; // add new leaf to tree
// New nodes are always added as leaves, so there's no need to copy a second
// pointer. And because the PREDEF* macros contain NULL as init values, it is
// not necessary to clear the new node's greater_than and less_than_or_equal
// fields.
}
// Add predefined tree items to given tree. The PREDEF* macros set the hash
// to 1 in all entries but the last, and to 0 in the last entry.
static void tree_from_list(struct ronode **tree, struct ronode *table_to_add)
{
//printf("Building tree from list.\n");
// Caution when trying to optimise this. :)
while (table_to_add->hash_value)
add_node_to_tree(tree, table_to_add++);
add_node_to_tree(tree, table_to_add);
}
// Search for a given ID string in a given tree.
// Compute the hash of the given string and then use that to try to find a
// tree item that matches the given data (HashValue and DynaBuf-String).
// Store "body" component in node_body and return TRUE.
// Return FALSE if no matching item found.
int Tree_easy_scan(struct ronode *tree, void **node_body, struct dynabuf *dyna_buf)
{
struct ronode wanted; // temporary storage
const char *p1,
*p2;
char b1,
b2;
hash_t hash;
// check if tree is actually ready to use. if not, build it from list.
// (list's first item does not contain real data, so "greater_than" is
// used to hold pointer to tree root)
if (tree->greater_than == NULL)
tree_from_list(&tree->greater_than, tree + 1); // real data starts at next list item
tree = tree->greater_than; // go from list head to tree root
// ok, we're done with this setup stuff.
// from now on, "greater_than" really means "greater_than"!
wanted.id_string = dyna_buf->buffer;
hash = make_hash(&wanted);
while (tree) {
// compare HashValue
if (hash > tree->hash_value) {
// wanted hash is bigger than current, so go
// to tree branch with bigger hashes
tree = tree->greater_than;
continue;
}
if (hash == tree->hash_value) {
p1 = wanted.id_string;
p2 = tree->id_string;
do {
b1 = *p1++;
b2 = *p2++;
} while ((b1 == b2) && b1);
if (b1 == b2) {
// store body data
*node_body = tree->body;
return TRUE;
}
}
// either the wanted hash is smaller or
// it was exact but didn't match
tree = tree->less_than_or_equal;
}
return FALSE ; // indicate failure
}
// Search for a "RAM tree" item. Compute the hash of string in GlobalDynaBuf
// and then use that to try to find a tree item that matches the given data
// (HashValue, ID_Number, GlobalDynaBuf-String). Save pointer to found tree
// item in given location.
// If no matching item is found, check the "create" flag. If it is set, create
// a new tree item, link to tree, fill with data and store its pointer. If the
// "create" flag is zero, store NULL as result.
// Returns whether item was created.
int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, boolean create)
{
struct ronode wanted; // temporary storage
struct rwnode **current_node;
struct rwnode *new_leaf_node;
const char *p1,
*p2;
char b1,
b2;
hash_t byte_hash;
wanted.id_string = GLOBALDYNABUF_CURRENT;
// incorporate ID number into hash value
byte_hash = make_hash(&wanted) ^ id_number;
wanted.hash_value = byte_hash; // correct struct's hash
PLATFORM_UINT2CHAR(byte_hash); // transform into byte
current_node = &(forest[byte_hash]); // point into table
while (*current_node) {
// compare HashValue
if (wanted.hash_value > (*current_node)->hash_value) {
// wanted hash is bigger than current, so go
// to tree branch with bigger hashes
current_node = &((*current_node)->greater_than);
continue;
}
if (wanted.hash_value == (*current_node)->hash_value) {
if (id_number == (*current_node)->id_number) {
p1 = wanted.id_string;
p2 = (*current_node)->id_string;
do {
b1 = *p1++;
b2 = *p2++;
} while ((b1 == b2) && b1);
if (b1 == b2) {
// store node pointer
*result = *current_node;
// return FALSE because node
// was not created
return FALSE;
}
}
}
// either the wanted hash is smaller or
// it was exact but didn't match
current_node = &((*current_node)->less_than_or_equal);
}
// node wasn't found. Check whether to create it
if (!create) {
*result = NULL; // indicate failure
return FALSE; // return FALSE because node was not created
}
// create new node
new_leaf_node = safe_malloc(sizeof(*new_leaf_node));
new_leaf_node->greater_than = NULL;
new_leaf_node->less_than_or_equal = NULL;
new_leaf_node->hash_value = wanted.hash_value;
new_leaf_node->id_number = id_number;
new_leaf_node->id_string = DynaBuf_get_copy(GlobalDynaBuf); // make permanent copy
// add new leaf to tree
*current_node = new_leaf_node;
// store pointer to new node in result location
*result = new_leaf_node;
return TRUE; // return TRUE because node was created
}
// Call given function for each object of matching type in the given tree.
// Calls itself recursively.
static void dump_tree(struct rwnode *node, int id_number, void (*fn)(struct rwnode *, FILE *), FILE *env)
{
if (node->id_number == id_number)
fn(node, env);
if (node->greater_than)
dump_tree(node->greater_than, id_number, fn, env);
if (node->less_than_or_equal)
dump_tree(node->less_than_or_equal, id_number, fn, env);
}
// Calls Tree_dump_tree for each non-zero entry of the given tree table.
void Tree_dump_forest(struct rwnode **forest, int id_number, void (*fn)(struct rwnode *, FILE *), FILE *env)
{
int ii;
for (ii = 255; ii >= 0; --ii) {
if (*forest)
dump_tree(*forest, id_number, fn, env);
++forest;
}
}