blob: ce4b940fd181a1b8de01a645550466d402d175bb [file]
/*
* Copyright © 2025, Niklas Haas
* Copyright © 2018, VideoLAN and dav1d authors
* Copyright © 2018, Two Orioles, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include "function.h"
#include "internal.h"
/* Deallocate a tree */
static void func_uninit(CheckasmFunc *const f)
{
if (!f)
return;
CheckasmFuncVersion *v = f->versions.next;
while (v) {
CheckasmFuncVersion *next = v->next;
free(v->suffix);
free(v);
v = next;
}
CheckasmFunc *const left = f->child[0];
CheckasmFunc *const right = f->child[1];
free(f->report_name);
free(f);
func_uninit(right);
func_uninit(left);
}
void checkasm_func_tree_uninit(CheckasmFuncTree *tree)
{
func_uninit(tree->root);
memset(tree, 0, sizeof(*tree));
}
#define is_digit(x) ((x) >= '0' && (x) <= '9')
/* ASCIIbetical sort except preserving natural order for numbers */
static int cmp_func_names(const char *a, const char *b)
{
const char *const start = a;
int ascii_diff, digit_diff;
for (; !(ascii_diff = *(const unsigned char *) a - *(const unsigned char *) b) && *a;
a++, b++)
;
for (; is_digit(*a) && is_digit(*b); a++, b++)
;
if (a > start && is_digit(a[-1]) && (digit_diff = is_digit(*a) - is_digit(*b)))
return digit_diff;
return ascii_diff;
}
/* Perform a tree rotation in the specified direction and return the new root */
static CheckasmFunc *tree_rotate(CheckasmFunc *const f, const int dir)
{
CheckasmFunc *const r = f->child[dir ^ 1];
f->child[dir ^ 1] = r->child[dir];
r->child[dir] = f;
r->color = f->color;
f->color = 0;
return r;
}
#define is_red(f) ((f) && !(f)->color)
/* Balance a left-leaning red-black tree at the specified node */
static void tree_balance(CheckasmFunc **const root)
{
CheckasmFunc *const f = *root;
if (is_red(f->child[0]) && is_red(f->child[1])) {
f->color ^= 1;
f->child[0]->color = f->child[1]->color = 1;
} else if (!is_red(f->child[0]) && is_red(f->child[1]))
*root = tree_rotate(f, 0); /* Rotate left */
else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
*root = tree_rotate(f, 1); /* Rotate right */
}
/* Get a node with the specified name, creating it if it doesn't exist; returns
* 1 if a new node was inserted, 0 otherwise. */
static int func_get(CheckasmFunc **const root, const char *const name,
CheckasmFunc **const out_func)
{
CheckasmFunc *f = *root;
if (!f) {
/* Allocate and insert a new node into the tree */
const size_t name_length = strlen(name) + 1;
f = checkasm_mallocz(offsetof(CheckasmFunc, name) + name_length);
memcpy(f->name, name, name_length);
*out_func = *root = f;
return 1;
}
/* Search the tree for a matching node */
const int cmp = cmp_func_names(name, f->name);
if (!cmp) {
*out_func = f;
return 0;
}
int inserted = func_get(&f->child[cmp > 0], name, out_func);
if (inserted)
tree_balance(root); /* Rebalance the tree on the way up */
return inserted;
}
CheckasmFunc *checkasm_func_get(CheckasmFuncTree *tree, const char *const name)
{
CheckasmFunc *func = NULL;
int inserted = func_get(&tree->root, name, &func);
if (inserted)
tree->root->color = 1; /* Ensure root is black */
return func;
}