Initial commit.
This commit is contained in:
commit
57e0a96f18
8 changed files with 588 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
sl
|
||||||
|
|
28
LICENSE
Normal file
28
LICENSE
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (c) 2015, Darko Poljak
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
* Neither the name of mergesort nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
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 HOLDER 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.
|
||||||
|
|
4
README.rst
Normal file
4
README.rst
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
skiplist
|
||||||
|
=========
|
||||||
|
|
||||||
|
Academic example of skip list implementation in C.
|
134
main.c
Normal file
134
main.c
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "skiplist.h"
|
||||||
|
#include "x.h"
|
||||||
|
|
||||||
|
#define MAXBUF 80
|
||||||
|
#define KEYCNT 1000
|
||||||
|
|
||||||
|
int dodebug = 1;
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
slist sl;
|
||||||
|
int key;
|
||||||
|
sl_node *x;
|
||||||
|
int res;
|
||||||
|
int i;
|
||||||
|
int keys[KEYCNT];
|
||||||
|
int failed = 0;
|
||||||
|
int cntnew;
|
||||||
|
int cntsame;
|
||||||
|
/*
|
||||||
|
char foo[MAXBUF];
|
||||||
|
|
||||||
|
sl_init(&sl);
|
||||||
|
sl_print(&sl);
|
||||||
|
|
||||||
|
printf("addition\n");
|
||||||
|
while (fgets(foo, MAXBUF - 1, stdin) != NULL) {
|
||||||
|
if (foo[0] == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
key = atoi(foo);
|
||||||
|
sl_add(&sl, key, NULL);
|
||||||
|
sl_print(&sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("search\n");
|
||||||
|
while (fgets(foo, MAXBUF - 1, stdin) != NULL) {
|
||||||
|
if (foo[0] == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
key = atoi(foo);
|
||||||
|
x = sl_find(&sl, key);
|
||||||
|
if (x) {
|
||||||
|
printf("found: %d\n", x->key);
|
||||||
|
} else {
|
||||||
|
printf("not found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("remove\n");
|
||||||
|
while (fgets(foo, MAXBUF - 1, stdin) != NULL) {
|
||||||
|
if (foo[0] == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
key = atoi(foo);
|
||||||
|
res = sl_remove(&sl, key);
|
||||||
|
sl_print(&sl);
|
||||||
|
if (res) {
|
||||||
|
printf("removed\n");
|
||||||
|
} else {
|
||||||
|
printf("not found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sl_free(&sl);
|
||||||
|
*/
|
||||||
|
|
||||||
|
sl_init(&sl);
|
||||||
|
dodebug = 0;
|
||||||
|
failed = 0;
|
||||||
|
cntnew = cntsame = 0;
|
||||||
|
for (i = 0; i < KEYCNT; ++i) {
|
||||||
|
key = random() % KEYCNT;
|
||||||
|
keys[i] = key;
|
||||||
|
res = sl_add(&sl, key, NULL);
|
||||||
|
switch(res) {
|
||||||
|
case SL_ADD_NEW:
|
||||||
|
++cntnew;
|
||||||
|
break;
|
||||||
|
case SL_ADD_SAME:
|
||||||
|
++cntsame;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
failed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("added %d new keys, %d same keys\n", cntnew, cntsame);
|
||||||
|
dodebug = 1;
|
||||||
|
sl_print(&sl);
|
||||||
|
dodebug = 0;
|
||||||
|
cntnew = cntsame = 0;
|
||||||
|
for (i = 0; i < KEYCNT; ++i) {
|
||||||
|
key = keys[i];
|
||||||
|
x = sl_find(&sl, key);
|
||||||
|
if (!x) {
|
||||||
|
failed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failed) {
|
||||||
|
fatal("find failed, report bug\n");
|
||||||
|
}
|
||||||
|
cntnew = cntsame = 0;
|
||||||
|
for (i = 0; i < KEYCNT; ++i) {
|
||||||
|
key = keys[i];
|
||||||
|
res = sl_remove(&sl, key);
|
||||||
|
switch (res) {
|
||||||
|
case 0:
|
||||||
|
++cntsame;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
++cntnew;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sl_find(&sl, key)) {
|
||||||
|
failed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failed) {
|
||||||
|
fatal("remove failed, report bug\n");
|
||||||
|
}
|
||||||
|
printf("removed %d new keys, %d same keys\n", cntnew, cntsame);
|
||||||
|
sl_free(&sl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
292
skiplist.c
Normal file
292
skiplist.c
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
#include <strings.h>
|
||||||
|
#include "skiplist.h"
|
||||||
|
#include "x.h"
|
||||||
|
|
||||||
|
sl_node *_sl_create_node(int key, void *data)
|
||||||
|
{
|
||||||
|
sl_node *x = xmalloc(sizeof(sl_node));
|
||||||
|
|
||||||
|
x->key = key;
|
||||||
|
x->data = data;
|
||||||
|
x->next = x->prev = x->below = x->above = NULL;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_init(slist *sl)
|
||||||
|
{
|
||||||
|
sl_node *x = _sl_create_node(KEY_INF_MIN, NULL);
|
||||||
|
sl_node *y = _sl_create_node(KEY_INF_MAX, NULL);
|
||||||
|
|
||||||
|
x->next = y;
|
||||||
|
y->prev = x;
|
||||||
|
|
||||||
|
sl->topleft = x;
|
||||||
|
sl->bottomleft = x;
|
||||||
|
sl->topright = y;
|
||||||
|
sl->bottomright = y;
|
||||||
|
|
||||||
|
sl->maxlevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sl_add_level(slist *sl)
|
||||||
|
{
|
||||||
|
sl_node *x = _sl_create_node(KEY_INF_MIN, NULL);
|
||||||
|
sl_node *y = _sl_create_node(KEY_INF_MAX, NULL);
|
||||||
|
|
||||||
|
x->next = y;
|
||||||
|
y->prev = x;
|
||||||
|
|
||||||
|
x->below = sl->topleft;
|
||||||
|
y->below = sl->topright;
|
||||||
|
sl->topleft->above = x;
|
||||||
|
sl->topright->above = y;
|
||||||
|
sl->topleft = x;
|
||||||
|
sl->topright = y;
|
||||||
|
|
||||||
|
++sl->maxlevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_free(slist *sl)
|
||||||
|
{
|
||||||
|
sl_node *foo;
|
||||||
|
sl_node *x;
|
||||||
|
sl_node *y;
|
||||||
|
|
||||||
|
for (foo = sl->bottomleft; foo != NULL;) {
|
||||||
|
x = foo;
|
||||||
|
foo = foo->next;
|
||||||
|
while (x != NULL) {
|
||||||
|
y = x;
|
||||||
|
x = x->above;
|
||||||
|
free(y->data);
|
||||||
|
free(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _print_key(int key)
|
||||||
|
{
|
||||||
|
if (key == KEY_INF_MIN) {
|
||||||
|
debug("-INF -> ");
|
||||||
|
} else if (key == KEY_INF_MAX) {
|
||||||
|
debug("+INF -> ");
|
||||||
|
} else {
|
||||||
|
debug("%d -> ", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sl_println()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 79; ++i) {
|
||||||
|
debug("-");
|
||||||
|
}
|
||||||
|
debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_print(const slist *sl)
|
||||||
|
{
|
||||||
|
sl_node *foo;
|
||||||
|
sl_node *x;
|
||||||
|
int level = sl->maxlevel;
|
||||||
|
|
||||||
|
_sl_println();
|
||||||
|
for (foo = sl->topleft; foo != NULL; foo = foo->below, --level) {
|
||||||
|
debug("level %d: ", level);
|
||||||
|
for (x = foo; x != NULL; x = x->next) {
|
||||||
|
_print_key(x->key);
|
||||||
|
}
|
||||||
|
debug("\n");
|
||||||
|
}
|
||||||
|
_sl_println();
|
||||||
|
debug("from below to above:\n");
|
||||||
|
for (foo = sl->bottomleft; foo != NULL; foo = foo->next) {
|
||||||
|
for (x = foo; x != NULL; x = x->above) {
|
||||||
|
_print_key(x->key);
|
||||||
|
}
|
||||||
|
debug("\n");
|
||||||
|
}
|
||||||
|
_sl_println();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HEAD 1
|
||||||
|
#define TAIL 0
|
||||||
|
|
||||||
|
int _toss_coin(int maxlevel) {
|
||||||
|
int i;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while(1) {
|
||||||
|
x = random() & 0x1;
|
||||||
|
if (x == TAIL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
/* inc level only by one at one iter */
|
||||||
|
if (i == maxlevel + 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean top levels with only INFINITY nodes until only one such
|
||||||
|
* level exists.
|
||||||
|
*/
|
||||||
|
void _sl_clean_top(slist *sl)
|
||||||
|
{
|
||||||
|
sl_node *foo;
|
||||||
|
|
||||||
|
for (foo = sl->topleft; foo->below != NULL; foo = foo->below) {
|
||||||
|
if (foo->next->key == KEY_INF_MAX
|
||||||
|
&& foo->below->next->key == KEY_INF_MAX) {
|
||||||
|
sl->topleft = foo->below;
|
||||||
|
sl->topright = foo->below->next;
|
||||||
|
sl->topleft->above = NULL;
|
||||||
|
sl->topright->above = NULL;
|
||||||
|
--sl->maxlevel;
|
||||||
|
free(foo->data);
|
||||||
|
free(foo->next);
|
||||||
|
free(foo);
|
||||||
|
debug("cleaned one level\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sl_add(slist *sl, int key, void *data)
|
||||||
|
{
|
||||||
|
sl_node *foo;
|
||||||
|
sl_node *x;
|
||||||
|
int i;
|
||||||
|
int k;
|
||||||
|
int level;
|
||||||
|
sl_node **update;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (key == KEY_INF_MIN || key == KEY_INF_MAX) {
|
||||||
|
return SL_ADD_INV;
|
||||||
|
}
|
||||||
|
i = _toss_coin(sl->maxlevel);
|
||||||
|
debug("toss coin result: %d\n", i);
|
||||||
|
if (i >= sl->maxlevel) {
|
||||||
|
/* add one more level */
|
||||||
|
_sl_add_level(sl);
|
||||||
|
debug("added level\n");
|
||||||
|
}
|
||||||
|
update = (sl_node **) xmalloc(sizeof(sl_node *) * (sl->maxlevel + 1));
|
||||||
|
bzero(update, sizeof(sl_node *) * (sl->maxlevel + 1));
|
||||||
|
level = sl->maxlevel;
|
||||||
|
|
||||||
|
foo = sl->topleft;
|
||||||
|
while (1) {
|
||||||
|
while (key >= foo->next->key) {
|
||||||
|
foo = foo->next;
|
||||||
|
}
|
||||||
|
if (foo->below == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
update[level--] = foo;
|
||||||
|
foo = foo->below;
|
||||||
|
}
|
||||||
|
update[level] = foo;
|
||||||
|
|
||||||
|
debug("search finished at key: %d\n", foo->key);
|
||||||
|
debug("update list keys:\n");
|
||||||
|
for (k = 0; k <= sl->maxlevel; ++k) {
|
||||||
|
if (update[k] != 0) {
|
||||||
|
debug("%d\n", update[k]->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("adding\n");
|
||||||
|
if (foo->key == key) {
|
||||||
|
res = SL_ADD_SAME;
|
||||||
|
debug("key exists\n");
|
||||||
|
do {
|
||||||
|
foo->data = data;
|
||||||
|
foo = foo->above;
|
||||||
|
} while (foo != NULL);
|
||||||
|
} else {
|
||||||
|
res = SL_ADD_NEW;
|
||||||
|
debug("new key\n");
|
||||||
|
while (level <= i) {
|
||||||
|
x = _sl_create_node(key, data);
|
||||||
|
x->next = update[level]->next;
|
||||||
|
update[level]->next->prev = x;
|
||||||
|
x->prev = update[level];
|
||||||
|
update[level]->next = x;
|
||||||
|
if (level > 0) {
|
||||||
|
x->below = update[level - 1]->next;
|
||||||
|
update[level - 1]->next->above = x;
|
||||||
|
}
|
||||||
|
++level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_sl_clean_top(sl);
|
||||||
|
free(update);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl_node *sl_find(slist *sl, int key)
|
||||||
|
{
|
||||||
|
sl_node *foo;
|
||||||
|
|
||||||
|
foo = sl->topleft;
|
||||||
|
while (1) {
|
||||||
|
while (key >= foo->next->key) {
|
||||||
|
foo = foo->next;
|
||||||
|
}
|
||||||
|
if (foo->below == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foo = foo->below;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foo->key != key) {
|
||||||
|
foo = NULL;
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sl_remove(slist *sl, int key)
|
||||||
|
{
|
||||||
|
sl_node *foo;
|
||||||
|
sl_node *x;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
foo = sl->topleft;
|
||||||
|
while (1) {
|
||||||
|
while (key >= foo->next->key) {
|
||||||
|
foo = foo->next;
|
||||||
|
}
|
||||||
|
if (foo->below == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foo = foo->below;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foo->key == key) {
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
while (foo != NULL) {
|
||||||
|
foo->prev->next = foo->next;
|
||||||
|
foo->next->prev = foo->prev;
|
||||||
|
free(foo->data);
|
||||||
|
x = foo;
|
||||||
|
foo = foo->above;
|
||||||
|
free(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sl_clean_top(sl);
|
||||||
|
} else {
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
63
skiplist.h
Normal file
63
skiplist.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef __SKIPLIST
|
||||||
|
#define __SKIPLIST
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define KEY_INF_MIN INT_MIN /* -INFINITY */
|
||||||
|
#define KEY_INF_MAX INT_MAX /* +INFINITY */
|
||||||
|
|
||||||
|
#define SL_ADD_NEW 1 /* added new key */
|
||||||
|
#define SL_ADD_SAME 0 /* added existing key */
|
||||||
|
#define SL_ADD_INV -1 /* added invalid key (+-INFINITY) */
|
||||||
|
|
||||||
|
/* skip list node */
|
||||||
|
typedef struct skiplist_node {
|
||||||
|
int key;
|
||||||
|
void *data;
|
||||||
|
struct skiplist_node *next;
|
||||||
|
struct skiplist_node *prev;
|
||||||
|
struct skiplist_node *below;
|
||||||
|
struct skiplist_node *above;
|
||||||
|
} sl_node;
|
||||||
|
|
||||||
|
/* skiplist object */
|
||||||
|
typedef struct skiplist {
|
||||||
|
sl_node *topleft;
|
||||||
|
sl_node *bottomleft;
|
||||||
|
sl_node *topright;
|
||||||
|
sl_node *bottomright;
|
||||||
|
int maxlevel;
|
||||||
|
} slist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize empty skip list.
|
||||||
|
*/
|
||||||
|
void sl_init(slist *sl);
|
||||||
|
/*
|
||||||
|
* Free skip list memory. After free list is not initialized
|
||||||
|
* for subsequent use.
|
||||||
|
*/
|
||||||
|
void sl_free(slist *sl);
|
||||||
|
/*
|
||||||
|
* Print skip list level by level and in down to up form.
|
||||||
|
*/
|
||||||
|
void sl_print(const slist *sl);
|
||||||
|
/*
|
||||||
|
* Add new key with data into skip list.
|
||||||
|
*/
|
||||||
|
int sl_add(slist *sl, int key, void *data);
|
||||||
|
/*
|
||||||
|
* Find node in skip list with given key.
|
||||||
|
* If key is not found returns NULL.
|
||||||
|
*/
|
||||||
|
sl_node *sl_find(slist *sl, int key);
|
||||||
|
/*
|
||||||
|
* Remove node from skip list with given key.
|
||||||
|
* If key is found and removed return 1, otherwise 0.
|
||||||
|
*/
|
||||||
|
int sl_remove(slist *sl, int key);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
45
x.c
Normal file
45
x.c
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "x.h"
|
||||||
|
|
||||||
|
extern int dodebug;
|
||||||
|
|
||||||
|
static void _log_stderr(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
char buf[MAXLINE];
|
||||||
|
|
||||||
|
vsnprintf(buf, MAXLINE-1, fmt, ap);
|
||||||
|
fflush(stdout);
|
||||||
|
fputs(buf, stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fatal(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
_log_stderr(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (dodebug) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
_log_stderr(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *xmalloc(size_t size)
|
||||||
|
{
|
||||||
|
void *foo = malloc(size);
|
||||||
|
|
||||||
|
if (foo == NULL) {
|
||||||
|
fatal("Not enough memory\n");
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
18
x.h
Normal file
18
x.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define MAXLINE 1024
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print message and exit.
|
||||||
|
*/
|
||||||
|
void fatal(const char *fmt, ...);
|
||||||
|
/*
|
||||||
|
* malloc, if failed then fatal exit.
|
||||||
|
*/
|
||||||
|
void *xmalloc(size_t size);
|
||||||
|
/*
|
||||||
|
* Print debug message if debug is enabled.
|
||||||
|
*/
|
||||||
|
void debug(const char *fmt, ...);
|
Loading…
Reference in a new issue