Merge remote-tracking branch 'upstream/master' into sloballoc

This commit is contained in:
Sven M. Hallberg 2016-12-06 18:21:06 +01:00
commit 0f9be192b6
23 changed files with 588 additions and 191 deletions

View file

@ -1,23 +1,24 @@
# -*- python -*-
import os.path
Import('env testruns')
dist_headers = [
"hammer.h",
"allocator.h",
"compiler_specifics.h",
"glue.h",
"internal.h",
"platform.h"
'hammer.h',
'allocator.h',
'compiler_specifics.h',
'glue.h',
'internal.h',
'platform.h'
]
parsers_headers = [
"parsers/parser_internal.h"
'parsers/parser_internal.h'
]
backends_headers = [
"backends/regex.h",
"backends/contextfree.h"
'backends/regex.h',
'backends/contextfree.h'
]
parsers = ['parsers/%s.c'%s for s in
@ -48,7 +49,7 @@ parsers = ['parsers/%s.c'%s for s in
'unimplemented',
'whitespace',
'xor',
'value']]
'value']]
backends = ['backends/%s.c' % s for s in
['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0']]
@ -63,12 +64,19 @@ misc_hammer_parts = [
'desugar.c',
'glue.c',
'hammer.c',
'platform_bsdlike.c',
'pprint.c',
'registry.c',
'system_allocator.c',
'sloballoc.c']
if env['PLATFORM'] == 'win32':
misc_hammer_parts += [
'platform_win32.c',
'tsearch.c',
]
else:
misc_hammer_parts += ['platform_bsdlike.c']
ctests = ['t_benchmark.c',
't_bitreader.c',
't_bitwriter.c',
@ -78,26 +86,40 @@ ctests = ['t_benchmark.c',
't_mm.c',
't_regression.c']
static_library_name = 'hammer'
build_shared_library=True
if env['PLATFORM'] == 'win32':
# FIXME(windows): symbols in hammer are not exported yet, a shared lib would be useless
build_shared_library=False
# prevent collision between .lib from dll and .lib for static lib
static_library_name = 'hammer_s'
libhammer_shared = env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts)
libhammer_static = env.StaticLibrary('hammer', parsers + backends + misc_hammer_parts)
Default(libhammer_shared, libhammer_static)
libhammer_static = env.StaticLibrary(static_library_name, parsers + backends + misc_hammer_parts)
if build_shared_library:
Default(libhammer_shared, libhammer_static)
env.Install('$libpath', [libhammer_static, libhammer_shared])
else:
Default(libhammer_static)
env.Install('$libpath', [libhammer_static])
env.Install("$libpath", [libhammer_static, libhammer_shared])
env.Install("$incpath", dist_headers)
env.Install("$parsersincpath", parsers_headers)
env.Install("$backendsincpath", backends_headers)
env.Install("$pkgconfigpath", "../../../libhammer.pc")
env.Install('$incpath', dist_headers)
env.Install('$parsersincpath', parsers_headers)
env.Install('$backendsincpath', backends_headers)
env.Install('$pkgconfigpath', '../../../libhammer.pc')
testenv = env.Clone()
testenv.ParseConfig('pkg-config --cflags --libs glib-2.0')
testenv.Append(LIBS=['hammer'])
testenv.Prepend(LIBPATH=['.'])
ctestexec = testenv.Program('test_suite', ctests + ['test_suite.c'], LINKFLAGS="--coverage" if testenv.GetOption("coverage") else None)
ctest = Alias('testc', [ctestexec], "".join(["env LD_LIBRARY_PATH=", os.path.dirname(ctestexec[0].path), " ", ctestexec[0].path]))
AlwaysBuild(ctest)
testruns.append(ctest)
if GetOption('with_tests'):
testenv = env.Clone()
testenv.ParseConfig('pkg-config --cflags --libs glib-2.0')
testenv.Append(LIBS=['hammer'])
testenv.Prepend(LIBPATH=['.'])
ctestexec = testenv.Program('test_suite', ctests + ['test_suite.c'], LINKFLAGS='--coverage' if testenv.GetOption('coverage') else None)
ctest = Alias('testc', [ctestexec], ''.join(['env LD_LIBRARY_PATH=', os.path.dirname(ctestexec[0].path), ' ', ctestexec[0].path]))
AlwaysBuild(ctest)
testruns.append(ctest)
Export("libhammer_static libhammer_shared")
Export('libhammer_static libhammer_shared')
for b in env['bindings']:
env.SConscript(["bindings/%s/SConscript" % b])
env.SConscript(['bindings/%s/SConscript' % b])

View file

@ -206,16 +206,43 @@ bool svm_stack_ensure_cap(HAllocator *mm__, HSVMContext *ctx, size_t addl) {
return true;
}
/*
* GCC produces the following diagnostic on this function:
*
* error: argument 'trace' might be clobbered by 'longjmp' or 'vfork' [-Werror=clobbered]
*
* However, this is spurious; what is happening is that the trace
* argument gets reused to store cur, and GCC doesn't know enough
* about setjmp to know that the second return only returns nonzero
* (and therefore the now-clobbered value of trace is invalid.)
*
* A side effect of disabling this warning is that we need to be
* careful about undefined behaviour involving automatic
* variables. Specifically, any automatic variable in this function
* whose value gets modified after setjmp has an undefined value after
* the second return; here, the only variables that could matter for
* are arena and ctx (because they're referenced in "goto fail").
*/
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wclobbered"
#endif
HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace, const uint8_t *input, int len) {
// orig_prog is only used for the action table
HSVMContext ctx;
HSVMContext *ctx = NULL;
HArena *arena = h_new_arena(mm__, 0);
ctx.stack_count = 0;
ctx.stack_capacity = 16;
ctx.stack = h_new(HParsedToken*, ctx.stack_capacity);
if (arena == NULL) {
return NULL;
}
ctx = h_new(HSVMContext, 1);
if (!ctx) goto fail;
ctx->stack_count = 0;
ctx->stack_capacity = 16;
ctx->stack = h_new(HParsedToken*, ctx->stack_capacity);
// out of memory handling
if(!arena || !ctx.stack)
if(!arena || !ctx->stack)
goto fail;
jmp_buf except;
h_arena_set_except(arena, &except);
@ -227,20 +254,20 @@ HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace,
for (cur = trace; cur; cur = cur->next) {
switch (cur->opcode) {
case SVM_PUSH:
if (!svm_stack_ensure_cap(mm__, &ctx, 1)) {
if (!svm_stack_ensure_cap(mm__, ctx, 1)) {
goto fail;
}
tmp_res = a_new(HParsedToken, 1);
tmp_res->token_type = TT_MARK;
tmp_res->index = cur->input_pos;
tmp_res->bit_offset = 0;
ctx.stack[ctx.stack_count++] = tmp_res;
ctx->stack[ctx->stack_count++] = tmp_res;
break;
case SVM_NOP:
break;
case SVM_ACTION:
// Action should modify stack appropriately
if (!orig_prog->actions[cur->arg].action(arena, &ctx, orig_prog->actions[cur->arg].env)) {
if (!orig_prog->actions[cur->arg].action(arena, ctx, orig_prog->actions[cur->arg].env)) {
// action failed... abort somehow
goto fail;
@ -249,9 +276,9 @@ HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace,
case SVM_CAPTURE:
// Top of stack must be a mark
// This replaces said mark in-place with a TT_BYTES.
assert(ctx.stack[ctx.stack_count-1]->token_type == TT_MARK);
assert(ctx->stack[ctx->stack_count-1]->token_type == TT_MARK);
tmp_res = ctx.stack[ctx.stack_count-1];
tmp_res = ctx->stack[ctx->stack_count-1];
tmp_res->token_type = TT_BYTES;
// TODO: Will need to copy if bit_offset is nonzero
assert(tmp_res->bit_offset == 0);
@ -260,25 +287,33 @@ HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace,
tmp_res->bytes.len = cur->input_pos - tmp_res->index;
break;
case SVM_ACCEPT:
assert(ctx.stack_count <= 1);
assert(ctx->stack_count <= 1);
HParseResult *res = a_new(HParseResult, 1);
if (ctx.stack_count == 1) {
res->ast = ctx.stack[0];
if (ctx->stack_count == 1) {
res->ast = ctx->stack[0];
} else {
res->ast = NULL;
}
res->bit_length = cur->input_pos * 8;
res->arena = arena;
h_arena_set_except(arena, NULL);
h_free(ctx.stack);
h_free(ctx->stack);
h_free(ctx);
return res;
}
}
fail:
if (arena) h_delete_arena(arena);
if (ctx.stack) h_free(ctx.stack);
if (ctx) {
if (ctx->stack) h_free(ctx->stack);
h_free(ctx);
}
return NULL;
}
// Reenable -Wclobber
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
uint16_t h_rvm_create_action(HRVMProg *prog, HSVMActionFunc action_func, void* env) {
for (uint16_t i = 0; i < prog->action_count; i++) {

View file

@ -21,12 +21,37 @@ Ruby bindings for [hammer](https://github.com/UpstandingHackers/hammer), a parsi
## Installation
TODO
1. Download the hammer source code, and make it available system wide with the bindings.
`git clone https://github.com/UpstandingHackers/hammer`
`cd hammer`
`scons bindings=ruby`
`sudo scons bindings=ruby install`
2. On linux, you will have to do
`sudo ldconfig`
3. Build the gem
`gem build hammer-parser.gemspec`
4. Install the gem
`gem install hammer-parser-x.x.x.gem`
## Examples
Add hammer to your Gemfile.
`gem 'hammer-parser'`
Use hammer in your project.
`require 'hammer'`
### Building a parser
```ruby

View file

@ -5,9 +5,9 @@ Gem::Specification.new do |s|
s.summary = 'Ruby bindings to the hammer parsing library.'
s.description = s.summary # TODO: longer description?
s.authors = ['Meredith L. Patterson', 'TQ Hirsch', 'Jakob Rath']
# TODO:
# s.email = ...
# s.homepage = ...
s.email = 'hammer@upstandinghackers.com'
s.homepage = 'https://github.com/UpstandingHackers/hammer'
s.license = 'GPL-2.0'
files = []
files << 'README.md'

View file

@ -52,14 +52,14 @@ HSlist* h_slist_copy(HSlist *slist) {
h_slist_push(ret, head->elem);
tail = ret->head;
head = head->next;
}
while (head != NULL) {
// append head item to tail in a new node
HSlistNode *node = h_arena_malloc(slist->arena, sizeof(HSlistNode));
node->elem = head->elem;
node->next = NULL;
tail = tail->next = node;
head = head->next;
while (head != NULL) {
// append head item to tail in a new node
HSlistNode *node = h_arena_malloc(slist->arena, sizeof(HSlistNode));
node->elem = head->elem;
node->next = NULL;
tail = tail->next = node;
head = head->next;
}
}
return ret;
}

View file

@ -778,10 +778,25 @@ void h_benchmark_report(FILE* stream, HBenchmarkResults* results);
//void h_benchmark_dump_optimized_code(FILE* stream, HBenchmarkResults* results);
// }}}
// {{{ result_buf printers (used by token type registry)
struct result_buf;
bool h_append_buf(struct result_buf *buf, const char* input, int len);
bool h_append_buf_c(struct result_buf *buf, char v);
bool h_append_buf_formatted(struct result_buf *buf, char* format, ...);
// }}}
// {{{ Token type registry
/// Allocate a new, unused (as far as this function knows) token type.
HTokenType h_allocate_token_type(const char* name);
/// Allocate a new token type with an unambiguous print function.
HTokenType h_allocate_token_new(
const char* name,
void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf));
/// Get the token type associated with name. Returns -1 if name is unkown
HTokenType h_get_token_type_number(const char* name);

View file

@ -422,6 +422,18 @@ struct HParserVtable_ {
bool higher; // false if primitive
};
// {{{ Token type registry internal
typedef struct HTTEntry_ {
const char* name;
HTokenType value;
void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf);
} HTTEntry;
const HTTEntry* h_get_token_type_entry(HTokenType token_type);
// }}}
bool h_false(void*);
bool h_true(void*);
bool h_not_regular(HRVMProg*, void*);

View file

@ -1,6 +1,20 @@
#include <stdarg.h>
#include "parser_internal.h"
#if defined(__STDC_VERSION__) && ( \
(__STDC_VERSION__ >= 201112L && !defined(__STDC_NO_VLA__)) || \
(__STDC_VERSION__ >= 199901L) \
)
# define STACK_VLA(type,name,size) type name[size]
#else
# if defined(_MSC_VER)
# include <malloc.h> // for _alloca
# define STACK_VLA(type,name,size) type* name = _alloca(size)
# else
# error "Missing VLA implementation for this compiler"
# endif
#endif
typedef struct {
size_t len;
HParser **p_array;
@ -53,11 +67,14 @@ static void desugar_choice(HAllocator *mm__, HCFStack *stk__, void *env) {
static bool choice_ctrvm(HRVMProg *prog, void* env) {
HSequence *s = (HSequence*)env;
uint16_t gotos[s->len];
// NOTE(uucidl): stack allocation since this backend uses
// setjmp/longjmp for error handling.
STACK_VLA(uint16_t, gotos, s->len);
for (size_t i=0; i<s->len; ++i) {
uint16_t insn = h_rvm_insert_insn(prog, RVM_FORK, 0);
if (!h_compile_regex(prog, s->p_array[i]))
if (!h_compile_regex(prog, s->p_array[i])) {
return false;
}
gotos[i] = h_rvm_insert_insn(prog, RVM_GOTO, 65535);
h_rvm_patch_arg(prog, insn, h_rvm_get_ip(prog));
}

View file

@ -1,6 +1,8 @@
#include "parser_internal.h"
static HParseResult* parse_nothing() {
static HParseResult* parse_nothing(void* x,HParseState* y) {
(void)(x);
(void)(y);
// not a mistake, this parser always fails
return NULL;
}

View file

@ -41,10 +41,10 @@ void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta) {
else {
fprintf(stream, "%*s", indent, "");
for (size_t i = 0; i < tok->bytes.len; i++) {
fprintf(stream,
"%c%02hhx",
(i == 0) ? '<' : '.',
tok->bytes.token[i]);
fprintf(stream,
"%c%02hhx",
(i == 0) ? '<' : '.',
tok->bytes.token[i]);
}
fprintf(stream, ">\n");
}
@ -54,7 +54,7 @@ void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta) {
fprintf(stream, "%*ss -%#" PRIx64 "\n", indent, "", -tok->sint);
else
fprintf(stream, "%*ss %#" PRIx64 "\n", indent, "", tok->sint);
break;
case TT_UINT:
fprintf(stream, "%*su %#" PRIx64 "\n", indent, "", tok->uint);
@ -96,7 +96,7 @@ static inline bool ensure_capacity(struct result_buf *buf, int amt) {
return true;
}
static inline bool append_buf(struct result_buf *buf, const char* input, int len) {
bool h_append_buf(struct result_buf *buf, const char* input, int len) {
if (ensure_capacity(buf, len)) {
memcpy(buf->output + buf->len, input, len);
buf->len += len;
@ -106,7 +106,7 @@ static inline bool append_buf(struct result_buf *buf, const char* input, int len
}
}
static inline bool append_buf_c(struct result_buf *buf, char v) {
bool h_append_buf_c(struct result_buf *buf, char v) {
if (ensure_capacity(buf, 1)) {
buf->output[buf->len++] = v;
return true;
@ -116,7 +116,7 @@ static inline bool append_buf_c(struct result_buf *buf, char v) {
}
/** append a formatted string to the result buffer */
static inline bool append_buf_formatted(struct result_buf *buf, char* format, ...)
bool h_append_buf_formatted(struct result_buf *buf, char* format, ...)
{
char* tmpbuf;
int len;
@ -125,7 +125,7 @@ static inline bool append_buf_formatted(struct result_buf *buf, char* format, ..
va_start(ap, format);
len = h_platform_vasprintf(&tmpbuf, format, ap);
result = append_buf(buf, tmpbuf, len);
result = h_append_buf(buf, tmpbuf, len);
free(tmpbuf);
va_end(ap);
@ -134,52 +134,59 @@ static inline bool append_buf_formatted(struct result_buf *buf, char* format, ..
static void unamb_sub(const HParsedToken* tok, struct result_buf *buf) {
if (!tok) {
append_buf(buf, "NULL", 4);
h_append_buf(buf, "NULL", 4);
return;
}
switch (tok->token_type) {
case TT_NONE:
append_buf(buf, "null", 4);
h_append_buf(buf, "null", 4);
break;
case TT_BYTES:
if (tok->bytes.len == 0)
append_buf(buf, "<>", 2);
h_append_buf(buf, "<>", 2);
else {
for (size_t i = 0; i < tok->bytes.len; i++) {
const char *HEX = "0123456789abcdef";
append_buf_c(buf, (i == 0) ? '<': '.');
char c = tok->bytes.token[i];
append_buf_c(buf, HEX[(c >> 4) & 0xf]);
append_buf_c(buf, HEX[(c >> 0) & 0xf]);
const char *HEX = "0123456789abcdef";
h_append_buf_c(buf, (i == 0) ? '<': '.');
char c = tok->bytes.token[i];
h_append_buf_c(buf, HEX[(c >> 4) & 0xf]);
h_append_buf_c(buf, HEX[(c >> 0) & 0xf]);
}
append_buf_c(buf, '>');
h_append_buf_c(buf, '>');
}
break;
case TT_SINT:
if (tok->sint < 0)
append_buf_formatted(buf, "s-%#" PRIx64, -tok->sint);
h_append_buf_formatted(buf, "s-%#" PRIx64, -tok->sint);
else
append_buf_formatted(buf, "s%#" PRIx64, tok->sint);
h_append_buf_formatted(buf, "s%#" PRIx64, tok->sint);
break;
case TT_UINT:
append_buf_formatted(buf, "u%#" PRIx64, tok->uint);
h_append_buf_formatted(buf, "u%#" PRIx64, tok->uint);
break;
case TT_ERR:
append_buf(buf, "ERR", 3);
h_append_buf(buf, "ERR", 3);
break;
case TT_SEQUENCE: {
append_buf_c(buf, '(');
h_append_buf_c(buf, '(');
for (size_t i = 0; i < tok->seq->used; i++) {
if (i > 0)
append_buf_c(buf, ' ');
h_append_buf_c(buf, ' ');
unamb_sub(tok->seq->elements[i], buf);
}
append_buf_c(buf, ')');
h_append_buf_c(buf, ')');
}
break;
default:
fprintf(stderr, "Unexpected token type %d\n", tok->token_type);
assert_message(0, "Should not reach here.");
default: {
const HTTEntry *e = h_get_token_type_entry(tok->token_type);
if (e) {
h_append_buf_c(buf, '{');
e->unamb_sub(tok, buf);
h_append_buf_c(buf, '}');
} else {
assert_message(0, "Bogus token type.");
}
}
}
}
@ -192,7 +199,7 @@ char* h_write_result_unamb(const HParsedToken* tok) {
};
assert(buf.output != NULL);
unamb_sub(tok, &buf);
append_buf_c(&buf, 0);
h_append_buf_c(&buf, 0);
return buf.output;
}

View file

@ -15,18 +15,19 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <search.h>
#include <stdlib.h>
#include "hammer.h"
#include "internal.h"
#include "tsearch.h"
typedef struct Entry_ {
const char* name;
HTokenType value;
} Entry;
#if defined(_MSC_VER)
#define h_strdup _strdup
#else
#define h_strdup strdup
#endif
static void *tt_registry = NULL;
static Entry** tt_by_id = NULL;
static HTTEntry** tt_by_id = NULL;
static unsigned int tt_by_id_sz = 0;
#define TT_START TT_USER
static HTokenType tt_next = TT_START;
@ -34,23 +35,31 @@ static HTokenType tt_next = TT_START;
/*
// TODO: These are for the extension registry, which does not yet have a good name.
static void *ext_registry = NULL;
static Entry** ext_by_id = NULL;
static HTTEntry** ext_by_id = NULL;
static int ext_by_id_sz = 0;
static int ext_next = 0;
*/
static int compare_entries(const void* v1, const void* v2) {
const Entry *e1 = (Entry*)v1, *e2 = (Entry*)v2;
const HTTEntry *e1 = (HTTEntry*)v1, *e2 = (HTTEntry*)v2;
return strcmp(e1->name, e2->name);
}
HTokenType h_allocate_token_type(const char* name) {
Entry* new_entry = h_alloc(&system_allocator, sizeof(*new_entry));
static void default_unamb_sub(const HParsedToken* tok,
struct result_buf* buf) {
h_append_buf_formatted(buf, "XXX AMBIGUOUS USER TYPE %d", tok->token_type);
}
HTokenType h_allocate_token_new(
const char* name,
void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf)) {
HTTEntry* new_entry = h_alloc(&system_allocator, sizeof(*new_entry));
assert(new_entry != NULL);
new_entry->name = name;
new_entry->value = 0;
Entry* probe = *(Entry**)tsearch(new_entry, &tt_registry, compare_entries);
new_entry->unamb_sub = unamb_sub;
HTTEntry* probe = *(HTTEntry**)tsearch(new_entry, &tt_registry, compare_entries);
if (probe->value != 0) {
// Token type already exists...
// TODO: treat this as a bug?
@ -58,7 +67,7 @@ HTokenType h_allocate_token_type(const char* name) {
return probe->value;
} else {
// new value
probe->name = strdup(probe->name); // drop ownership of name
probe->name = h_strdup(probe->name); // drop ownership of name
probe->value = tt_next++;
if ((probe->value - TT_START) >= tt_by_id_sz) {
if (tt_by_id_sz == 0) {
@ -75,10 +84,13 @@ HTokenType h_allocate_token_type(const char* name) {
return probe->value;
}
}
HTokenType h_allocate_token_type(const char* name) {
return h_allocate_token_new(name, default_unamb_sub);
}
HTokenType h_get_token_type_number(const char* name) {
Entry e;
HTTEntry e;
e.name = name;
Entry **ret = (Entry**)tfind(&e, &tt_registry, compare_entries);
HTTEntry **ret = (HTTEntry**)tfind(&e, &tt_registry, compare_entries);
if (ret == NULL)
return 0;
else
@ -90,3 +102,9 @@ const char* h_get_token_type_name(HTokenType token_type) {
else
return tt_by_id[token_type - TT_START]->name;
}
const HTTEntry* h_get_token_type_entry(HTokenType token_type) {
if (token_type >= tt_next || token_type < TT_START)
return NULL;
else
return tt_by_id[token_type - TT_START];
}

15
src/search.h Normal file
View file

@ -0,0 +1,15 @@
#if defined(_MSC_VER)
/* find or insert datum into search tree */
void *tsearch(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *));
/* delete node with given key */
void * tdelete(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *));
/* Walk the nodes of a tree */
void twalk(const void *vroot, void (*action)(const void *, VISIT, int));
#else
#include <search.h>
#endif

141
src/tsearch.c Normal file
View file

@ -0,0 +1,141 @@
/* $OpenBSD: tsearch.c,v 1.9 2015/08/20 21:49:29 deraadt Exp $ */
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
#include <stdlib.h>
#include "tsearch.h"
typedef struct node_t {
char *key;
struct node_t *left, *right;
} node;
/* find or insert datum into search tree */
void *
tsearch(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node *q;
char *key = (char *)vkey;
node **rootp = (node **)vrootp;
if (rootp == (struct node_t **)0)
return ((void *)0);
while (*rootp != (struct node_t *)0) { /* Knuth's T1: */
int r;
if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */
return ((void *)*rootp); /* we found it! */
rootp = (r < 0) ?
&(*rootp)->left : /* T3: follow left branch */
&(*rootp)->right; /* T4: follow right branch */
}
q = malloc(sizeof(node)); /* T5: key not found */
if (q != (struct node_t *)0) { /* make new node */
*rootp = q; /* link new node to old */
q->key = key; /* initialize new node */
q->left = q->right = (struct node_t *)0;
}
return ((void *)q);
}
/* delete node with given key */
void *
tdelete(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node **rootp = (node **)vrootp;
char *key = (char *)vkey;
node *p = (node *)1;
node *q;
node *r;
int cmp;
if (rootp == (struct node_t **)0 || *rootp == (struct node_t *)0)
return ((struct node_t *)0);
while ((cmp = (*compar)(key, (*rootp)->key)) != 0) {
p = *rootp;
rootp = (cmp < 0) ?
&(*rootp)->left : /* follow left branch */
&(*rootp)->right; /* follow right branch */
if (*rootp == (struct node_t *)0)
return ((void *)0); /* key not found */
}
r = (*rootp)->right; /* D1: */
if ((q = (*rootp)->left) == (struct node_t *)0) /* Left (struct node_t *)0? */
q = r;
else if (r != (struct node_t *)0) { /* Right link is null? */
if (r->left == (struct node_t *)0) { /* D2: Find successor */
r->left = q;
q = r;
} else { /* D3: Find (struct node_t *)0 link */
for (q = r->left; q->left != (struct node_t *)0; q = r->left)
r = q;
r->left = q->right;
q->left = (*rootp)->left;
q->right = (*rootp)->right;
}
}
free((struct node_t *) *rootp); /* D4: Free node */
*rootp = q; /* link parent to new node */
return(p);
}
/* Walk the nodes of a tree */
static void
trecurse(node *root, void (*action)(const void *, VISIT, int), int level)
{
if (root->left == (struct node_t *)0 && root->right == (struct node_t *)0)
(*action)(root, leaf, level);
else {
(*action)(root, preorder, level);
if (root->left != (struct node_t *)0)
trecurse(root->left, action, level + 1);
(*action)(root, postorder, level);
if (root->right != (struct node_t *)0)
trecurse(root->right, action, level + 1);
(*action)(root, endorder, level);
}
}
/* Walk the nodes of a tree */
void
twalk(const void *vroot, void (*action)(const void *, VISIT, int))
{
node *root = (node *)vroot;
if (root != (node *)0 && action != (void (*)(const void *, VISIT, int))0)
trecurse(root, action, 0);
}
/* $OpenBSD: tfind.c,v 1.6 2014/03/16 18:38:30 guenther Exp $ */
/* find a node, or return 0 */
void *
tfind(const void *vkey, void * const *vrootp,
int (*compar)(const void *, const void *))
{
char *key = (char *)vkey;
node **rootp = (node **)vrootp;
if (rootp == (struct node_t **)0)
return ((struct node_t *)0);
while (*rootp != (struct node_t *)0) { /* T1: */
int r;
if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */
return (*rootp); /* key found */
rootp = (r < 0) ?
&(*rootp)->left : /* T3: follow left branch */
&(*rootp)->right; /* T4: follow right branch */
}
return (node *)0;
}

26
src/tsearch.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef HAMMER_TSEARCH__H
#define HAMMER_TSEARCH__H
#if defined(_MSC_VER)
typedef enum { preorder, postorder, endorder, leaf } VISIT;
/* find or insert datum into search tree */
void *tsearch(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *));
/* delete node with given key */
void * tdelete(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *));
/* Walk the nodes of a tree */
void twalk(const void *vroot, void (*action)(const void *, VISIT, int));
/* find a node, or return 0 */
void *tfind(const void *vkey, void * const *vrootp,
int (*compar)(const void *, const void *));
#else
#include <search.h>
#endif
#endif /* HAMMER_TSEARCH__H */