readd include dir

This commit is contained in:
Nils O. Selåsdal
2012-11-15 17:30:40 +01:00
parent 1d47f9d078
commit 4dca24953f
23 changed files with 2341 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
import os
Import('env', 'build_type', 'prefix', 'version_info')
ucore_env = env.Clone()
#substitute @version_xx@ strings
subst_version_info = dict(('@' + key + '@', version_info[key]) for key in version_info)
ucore_env.Substfile('ucore_version.c.in', SUBST_DICT = subst_version_info)
headers = ucore_env.Glob('*.h')
#install targets
ucore_env.Alias('install',
ucore_env.InstallPerm(os.path.join(prefix, 'include', 'ucore'), headers, 0644))
+97
View File
@@ -0,0 +1,97 @@
#ifndef IO_MUX_H_
#define IO_MUX_H_
#include "ucore_timers.h"
#define MUX_EV_READ (1 << 0x00)
#define MUX_EV_WRITE (1 << 0x01)
//#define MUX_EV_EXCEPT (1 << 0x02)
#define MUX_EV_MASK (MUX_EV_READ | MUX_EV_WRITE )
/**
* mux implementation to use
*/
enum IOMUX_TYPE {
IOMUX_TYPE_DEFAULT = 0,
IOMUX_TYPE_SELECT,
IOMUX_TYPE_EPOLL
};
/** Opaque struct */
struct IOMux;
struct IOMuxFD;
typedef void (*iomux_cb)(struct IOMux *mux, struct IOMuxFD *fd, unsigned int event);
/** Represents a watched file descriptor */
struct IOMuxFD {
/** The file descriptor */
int fd;
/** The events to watch for, MUX_EV_XX mask */
unsigned int what;
/** callback to call on an event */
iomux_cb callback;
/** values available to the caller. These will bbe passed
* back on the callback */
void *cookie_ptr;
int cookie_int;
int cookie_int2;
};
/** Creates a new IOMux.
*/
struct IOMux *iomux_create(enum IOMUX_TYPE type);
/** Deletes the IOMux and frees its resources.
* Can not be called from within a callback */
void iomux_delete(struct IOMux *mux);
/** Runs the event loop of the mux
* returns if the number of file descriptors and timers are 0 or
* an error occurs */
int iomux_run(struct IOMux *mux);
/* Adds a file descriptor to the watch set,
* returns 0 on success, an errno value on error */
int iomux_register_fd(struct IOMux *mux, struct IOMuxFD *fd);
/** Removes the file descriptor*/
int iomux_unregister_fd(struct IOMux *mux, struct IOMuxFD *fd);
/** Updates the events to watch for in fd->what
* returns 0 on success, an errno value on error */
int iomux_update_events(struct IOMux *mux, struct IOMuxFD *fd);
/**
* Convenience macro for setting the new event
*/
#define iomux_set_event(mux, fd, what)\
({\
(fd)->what = (what);\
iomux_update_events((mux), (fd));\
})
/**
* Convenience macro for clearing the new event
*/
#define iomux_clear_event(mux, fd, what)\
({\
(fd)->what &= ~(what);\
iomux_update_events((mux), (fd));\
})
/**
* Convenience macro for adding the new event to the existing events
*/
#define iomux_add_event(mux, fd, what)\
({\
(fd)->what &= (what);\
iomux_update_events((mux), (fd));\
})
/** Returns a UCTimer instance tied to this mux.
* Used to schedule timers within this mux.
*/
struct UCTimers *iomux_get_timers(struct IOMux *mux);
#endif
+47
View File
@@ -0,0 +1,47 @@
#ifndef IO_MUX_IMPL_H_
#define IO_MUX_IMPL_H_
#include "iomux.h"
#include "ucore_timers.h"
/** struct for IOMux implementations */
struct IOMux {
/* TImer instance */
struct UCTimers timers;
/* Current time. Updated before calling run_impl, and updated
* by iomux_timers_run
*/
struct timeval now;
/* Used by mux implementations to hold their own data/instance */
void *instance;
/** Run one iteration of the mux.
* The mux implementation must honor the timeout, if given.
* The mux implementation must call iomux_timers_run after the
* polling has occured (or if it's polling times out)
*
* @param mux The IOMux instance
* @param timeout absolute time of a timeout - after which timers should run.
* If timeout is NULL, no timeout should be set on the poll.
*/
int (*run_impl)(struct IOMux *mux, struct timeval *timeout);
/* Deallocate the instance specific part of the IOMux */
void (*delete_impl)(struct IOMux *mux);
/** Register an IOMuxFD for events*/
int (*register_fd_impl)(struct IOMux *mux, struct IOMuxFD *fd);
/** Unregister an IOMuxFD */
int (*unregister_fd_impl)(struct IOMux *mux, struct IOMuxFD *fd);
/** Update an IOMuxFD (due to its events ('what') has changed*/
int (*update_events_impl)(struct IOMux *mux, struct IOMuxFD *fd);
};
int iomux_select_init(struct IOMux *mux);
int iomux_epoll_init(struct IOMux *mux);
int iomux_timers_run(struct IOMux *mux);
#endif
+741
View File
@@ -0,0 +1,741 @@
/* $NetBSD: tree.h,v 1.16 2008/03/21 13:07:15 ad Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* 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 AUTHOR ``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 AUTHOR 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.
*/
#ifndef _SYS_TREE_H_
#define _SYS_TREE_H_
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define RB_INITIALIZER(root) \
{ NULL }
#define RB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#define RB_LEFT(elm, field) (elm)->field.rbe_left
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_COLOR(elm, field) (elm)->field.rbe_color
#define RB_ROOT(head) (head)->rbh_root
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(elm, parent, field) do { \
RB_PARENT(elm, field) = parent; \
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
RB_COLOR(elm, field) = RB_RED; \
} while (/*CONSTCOND*/ 0)
#define RB_SET_BLACKRED(black, red, field) do { \
RB_COLOR(black, field) = RB_BLACK; \
RB_COLOR(red, field) = RB_RED; \
} while (/*CONSTCOND*/ 0)
#ifndef RB_AUGMENT
#define RB_AUGMENT(x) (void)(x)
#endif
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
} \
RB_AUGMENT(elm); \
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
else \
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_LEFT(tmp, field) = (elm); \
RB_PARENT(elm, field) = (tmp); \
RB_AUGMENT(tmp); \
if ((RB_PARENT(tmp, field))) \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
(tmp) = RB_LEFT(elm, field); \
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
} \
RB_AUGMENT(elm); \
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
else \
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_RIGHT(tmp, field) = (elm); \
RB_PARENT(elm, field) = (tmp); \
RB_AUGMENT(tmp); \
if ((RB_PARENT(tmp, field))) \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define RB_PROTOTYPE(name, type, field, cmp) \
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
attr struct type *name##_RB_FIND(struct name *, struct type *); \
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
attr struct type *name##_RB_NEXT(struct type *); \
attr struct type *name##_RB_PREV(struct type *); \
attr struct type *name##_RB_MINMAX(struct name *, int); \
\
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define RB_GENERATE(name, type, field, cmp) \
RB_GENERATE_INTERNAL(name, type, field, cmp,)
#define RB_GENERATE_STATIC(name, type, field, cmp) \
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
attr void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = RB_PARENT(elm, field)) != NULL && \
RB_COLOR(parent, field) == RB_RED) { \
gparent = RB_PARENT(parent, field); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_COLOR(tmp, field) = RB_BLACK; \
RB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_RIGHT(parent, field) == elm) { \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(parent, gparent, field); \
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
} else { \
tmp = RB_LEFT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_COLOR(tmp, field) = RB_BLACK; \
RB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_LEFT(parent, field) == elm) { \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(parent, gparent, field); \
RB_ROTATE_LEFT(head, gparent, tmp, field); \
} \
} \
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
} \
\
attr void \
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
elm != RB_ROOT(head)) { \
if (RB_LEFT(parent, field) == elm) { \
tmp = RB_RIGHT(parent, field); \
if (RB_COLOR(tmp, field) == RB_RED) { \
RB_SET_BLACKRED(tmp, parent, field); \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
RB_COLOR(tmp, field) = RB_RED; \
elm = parent; \
parent = RB_PARENT(elm, field); \
} else { \
if (RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
struct type *oleft; \
if ((oleft = RB_LEFT(tmp, field)) \
!= NULL) \
RB_COLOR(oleft, field) = RB_BLACK;\
RB_COLOR(tmp, field) = RB_RED; \
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
tmp = RB_RIGHT(parent, field); \
} \
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
RB_COLOR(parent, field) = RB_BLACK; \
if (RB_RIGHT(tmp, field)) \
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
RB_ROTATE_LEFT(head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} else { \
tmp = RB_LEFT(parent, field); \
if (RB_COLOR(tmp, field) == RB_RED) { \
RB_SET_BLACKRED(tmp, parent, field); \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
RB_COLOR(tmp, field) = RB_RED; \
elm = parent; \
parent = RB_PARENT(elm, field); \
} else { \
if (RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
struct type *oright; \
if ((oright = RB_RIGHT(tmp, field)) \
!= NULL) \
RB_COLOR(oright, field) = RB_BLACK;\
RB_COLOR(tmp, field) = RB_RED; \
RB_ROTATE_LEFT(head, tmp, oright, field);\
tmp = RB_LEFT(parent, field); \
} \
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
RB_COLOR(parent, field) = RB_BLACK; \
if (RB_LEFT(tmp, field)) \
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
RB_ROTATE_RIGHT(head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
RB_COLOR(elm, field) = RB_BLACK; \
} \
\
attr struct type * \
name##_RB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (RB_LEFT(elm, field) == NULL) \
child = RB_RIGHT(elm, field); \
else if (RB_RIGHT(elm, field) == NULL) \
child = RB_LEFT(elm, field); \
else { \
struct type *left; \
elm = RB_RIGHT(elm, field); \
while ((left = RB_LEFT(elm, field)) != NULL) \
elm = left; \
child = RB_RIGHT(elm, field); \
parent = RB_PARENT(elm, field); \
color = RB_COLOR(elm, field); \
if (child) \
RB_PARENT(child, field) = parent; \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
if (RB_PARENT(elm, field) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (RB_PARENT(old, field)) { \
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
RB_LEFT(RB_PARENT(old, field), field) = elm;\
else \
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
RB_AUGMENT(RB_PARENT(old, field)); \
} else \
RB_ROOT(head) = elm; \
RB_PARENT(RB_LEFT(old, field), field) = elm; \
if (RB_RIGHT(old, field)) \
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
if (parent) { \
left = parent; \
do { \
RB_AUGMENT(left); \
} while ((left = RB_PARENT(left, field)) != NULL); \
} \
goto color; \
} \
parent = RB_PARENT(elm, field); \
color = RB_COLOR(elm, field); \
if (child) \
RB_PARENT(child, field) = parent; \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
color: \
if (color == RB_BLACK) \
name##_RB_REMOVE_COLOR(head, parent, child); \
return (old); \
} \
\
/* Inserts a node into the RB tree */ \
attr struct type * \
name##_RB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = RB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
RB_SET(elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
RB_LEFT(parent, field) = elm; \
else \
RB_RIGHT(parent, field) = elm; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = elm; \
name##_RB_INSERT_COLOR(head, elm); \
return (NULL); \
} \
\
/* Finds the node with the same key as elm */ \
attr struct type * \
name##_RB_FIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
} \
\
/* Finds the first node greater than or equal to the search key */ \
attr struct type * \
name##_RB_NFIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *res = NULL; \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) { \
res = tmp; \
tmp = RB_LEFT(tmp, field); \
} \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (res); \
} \
\
/* ARGSUSED */ \
attr struct type * \
name##_RB_NEXT(struct type *elm) \
{ \
if (RB_RIGHT(elm, field)) { \
elm = RB_RIGHT(elm, field); \
while (RB_LEFT(elm, field)) \
elm = RB_LEFT(elm, field); \
} else { \
if (RB_PARENT(elm, field) && \
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
elm = RB_PARENT(elm, field); \
else { \
while (RB_PARENT(elm, field) && \
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
elm = RB_PARENT(elm, field); \
elm = RB_PARENT(elm, field); \
} \
} \
return (elm); \
} \
\
/* ARGSUSED */ \
attr struct type * \
name##_RB_PREV(struct type *elm) \
{ \
if (RB_LEFT(elm, field)) { \
elm = RB_LEFT(elm, field); \
while (RB_RIGHT(elm, field)) \
elm = RB_RIGHT(elm, field); \
} else { \
if (RB_PARENT(elm, field) && \
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
elm = RB_PARENT(elm, field); \
else { \
while (RB_PARENT(elm, field) && \
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
elm = RB_PARENT(elm, field); \
elm = RB_PARENT(elm, field); \
} \
} \
return (elm); \
} \
\
attr struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = RB_LEFT(tmp, field); \
else \
tmp = RB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define RB_NEGINF -1
#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
#define RB_FOREACH(x, name, head) \
for ((x) = RB_MIN(name, head); \
(x) != NULL; \
(x) = name##_RB_NEXT(x))
#define RB_FOREACH_REVERSE(x, name, head) \
for ((x) = RB_MAX(name, head); \
(x) != NULL; \
(x) = name##_RB_PREV(x))
#endif /* _SYS_TREE_H_ */
+8
View File
@@ -0,0 +1,8 @@
#ifndef UC_BACKTRACE_H_
#define UC_BACKTRACE_H_
#define UC_BACTRACE_STDERR uc_backtrace_fd(3)
void uc_backtrace_fd(int fd);
#endif
+81
View File
@@ -0,0 +1,81 @@
#ifndef BITEVEC_H_
#define BITEVEC_H_
#ifdef __cplusplus
extern "C" {
#endif
struct bitvec {
//storage for the bits
unsigned long *vec;
//length of the above vec. (Not the number of bits !)
size_t vec_len;
};
/**
* Evaluates the length required for a bitvec to store nbit bits.
*/
#define BITVEC_VEC_LEN(nbits) ((nbits/(sizeof(unsigned long)*CHAR_BIT)) + (nbits % (sizeof(unsigned long)*CHAR_BIT) == 0 ? 0 : 1))
/**
* Use as :
* unsigned long v[10];
* struct bitvec v = BITVEC_STATIC_INIT(v);
*/
#define BITVEC_STATIC_INIT(vec_data)\
{\
vec_data,\
sizeof vec_data/sizeof vec_data[0]}
/**
* Returns a newly malloced bitvec, capable of storing at least nbits bits
* All bits are initially zero.
*
*/
struct bitvec *bitvec_new(size_t nbits);
/** free a bitvec previously allocated by bitvec_new
*/
void bitvec_free(struct bitvec *v);
//Note that accessing a bit beyond the nbits originally initialized
//for the given bitvec is undedefined
/** Sets bit no. b */
void set_bit(struct bitvec *v,int b);
/** Clears bit no. b*/
void clear_bit(struct bitvec *v,int b);
/** Gets the current value (0 or 1) of bit no. b*/
int get_bit(const struct bitvec *v,int b);
/** Sets all bits to zero */
void clear_all(struct bitvec *v);
/** Sets all bits to one */
void set_all(struct bitvec *v);
/** Initialize bits from a array of ints, each array element maps to one bit.
* The bits are initialized from the int array so zero maps to zero and non-zero maps to one.
* e..g to set the 5 first bits, to 01110:
* int a[] = {0,1,1,1,0};
* set_bits_from_array(v,a,5);
* */
void set_bits_from_array(struct bitvec *v,char *array,size_t array_len);
// The _s ("secure") versions does boundary checking and assert() if they
// try to access a bit out of bounds.
void set_bit_s(struct bitvec *v,int b);
void clear_bit_s(struct bitvec *v,int b);
int get_bit_s(const struct bitvec *v,int b);
#ifdef __cplusplus
}
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
#ifndef UCORE_BUFFER_H_
#define UCORE_BUFFER_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** A GBuf is a growing reference counted buffer.
* The buffer is dynamically allocated, and can grow
* as you append data to it. Reference counting takes care
* of automatically freeing the GBuf and its buffer when it
* reaches 0
*/
typedef struct GBuf GBuf;
struct GBuf {
/** Allocated buffer length.*/
size_t len;
/** Used space in the buffer */
size_t used;
/** Reference count */
size_t ref_cnt;
/** The data */
void *buf;
};
/** Allocatea new GBuf.
*
* @param initsz Intial capacity of the GBuf
* @return New GBuf, or NULL if allocation fails,
* The returned GBuf will have ref_cnt=1
*/
GBuf* uc_new_gbuf(size_t initsz);
/** Increment the reference count of the GBuf.
*
* @param buf Bufffer
*/
void uc_gbuf_ref(GBuf *buf);
/** Decrement the reference count of the GBuf, free it if ref_cnt reaches 0
*
* @param buf Bufffer
*/
void uc_gbuf_unref(GBuf *buf);
/** Append data to the GBuf.
* Data is coped into the buffer, starting at &buf->data[used]
* The GBuf automatically grows if needed.
*
* @param buf buffer to append to
* @param data data to copy to the GBuf
* @param len length of @data to copy
* @return 0 on success, -1 on failure when allocation fails if he buffer
* needs to grow
*/
int uc_gbuf_append(GBuf *buf,void *data,size_t len);
/** Expand the capacity of the GBuf.
*
* @param buf buffer to grow
* @return 0 on success, -1 on failure when allocation fails
*/
int uc_gbuf_grow(GBuf *buf, size_t addlen);
/** Remaining unised space of the GBuf.
* @param buff buffer
* @return length of unused space
*/
size_t uc_gbuf_remaining(const GBuf *buf)
{
return buf->len - buf->used;
}
#ifdef __cplusplus
}
#endif
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef UCORE_HASH_H_
#define UCORE_HASH_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint32_t
uc_crc32(const uint8_t *buf, size_t len);
uint32_t
uc_djbhash(const char *str);
unsigned int
uc_elfhash(const char *str, unsigned int len);
uint64_t
uc_hash64shift(uint64_t key);
uint32_t
uc_hashuint(uint32_t a);
uint32_t
uc_murmurmash2(const void *key, int len, uint32_t seed );
uint32_t
uc_pjwhash(const char *str,size_t len);
uint32_t
uc_sax_hash(void *key, size_t len);
#ifdef __cplusplus
}
#endif
#endif
+21
View File
@@ -0,0 +1,21 @@
#ifndef UCORE_HEAPSORT_H_
#define UCORE_HEAPSORT_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
///compare function. Needs only to return < 0 if
//the first element is less than the second
typedef int (*uc_hs_cmp)(const void *, const void *);
void
uc_heapsort(void *base, size_t count, size_t width,
uc_hs_cmp cmp);
#ifdef __cplusplus
}
#endif
#endif
+252
View File
@@ -0,0 +1,252 @@
#ifndef UCORE_LOGGING_H_
#define UCORE_LOGGING_H_
/** Logging functions.
* All logging functions except uc_log_init are thread safe,
* so logging can be performed from any thread after the logging
* system have been initialized.
*
* The logging system has the concept of 'modules'. Modules are defined
* by the application, and would match the logical parts of an application.
* uc_log_init() tells the logging library about these modules, and would normally
* be used like:
*
* enum {
* POLLER,
* DB,
* IO
*};
*
* struct uc_log_module modules[] = {
* {
* .id = POLLER,
* .short_name = "POLLER",
* .long_name = "Poller thread",
* .log_level = UC_LL_DEBUG,
* },
* {
* .id = DB,
* .short_name = "DB",
* .long_name = "Database handler",
* .log_level == UC_LL_DEBUG
* },
* {
* .id = IO,
* .short_name = "IO",
* .long_name = "I/O worker",
* .log_level == UC_LL_DEBUG
* }
* };
* struct uc_log_modules m = {
* .mods = modules,
* .cnt = ARRAY_SIZE(modules)
* };
*
* uc_log_init(&m);
*
* So the application defines 3 modules logging
* will be categorized by.
* Note that the values (the enum in this case)
* defining all the .id memmbers of uc_log_module
* must start at 0 and be concecutive.
*
* Now, the database code in the application can do
* logging as e.g.
*
* UC_LOGF(UC_LL_INFO, DB, "connecting to server %s\n", ip);
*
* And the resulting log line will include the short_name
* modules[DB].short_name
*
*/
#ifdef __cplusplus
extern "C" {
#endif
/** The log levels defined by ucore logging*/
enum UC_LOG_LEVEL {
UC_LL_NONE = 0,
UC_LL_DEBUG = 1,
UC_LL_INFO = 3,
UC_LL_WARNING = 5,
UC_LL_ERROR = 7,
};
/** A destination for the logging messages*/
enum UC_LOG_DESTINATION {
/** Logs using the unix syslog system */
UC_LDEST_SYSLOG,
/** Prints log messages to stderr. */
UC_LDEST_STDERR,
/** Prints log messages to a file */
UC_LDEST_FILE,
};
/** A module of a program, that will perform logging.
* This is used group logging from parts of a larger program,
* and allow the log message to identigy which module the
* log message originates from
*/
struct uc_log_module {
/** The id of the log module.
* This is the id one specifies in the various log
* functions/macros*/
int id;
/** A short name for the module.
* This will appear in log messages */
const char *short_name;
/** A longer description for the log module*/
const char *long_name;
/** The log level of this module. Only
* log statements with a level >= the log_level
* are actually logged */
int log_level;
};
/* A collection of struct uc_log_module */
struct uc_log_modules {
/* Pointer to the first element of the array */
struct uc_log_module *mods;
/** The number of elements in the mods array. */
int cnt;
};
struct uc_log_destination;
/* Returns a string representation of the log level.
*
* @param ll The log level
* @return String representation of the log level. (This will be a string literal)
*/
const char *uc_ll_2_str(enum UC_LOG_LEVEL l);
/* creates a new uc_log_destination for printing on stderr.
*
* @param log_level log level of this destination. only log messages
* with a log level >= the log level of the destination are actually logged.
* @param log_location whether to log info about the source file and line number
* in a log message.
*
* @return An opaque uc_log_destination that can be added as a destination to
* the ucore logging system
*/
struct uc_log_destination *uc_log_new_stderr(int log_level, int log_location);
/* creates a new uc_log_destination for logging to syslog
*
* @param ident The ident as used in the syslog openlog() funcion
* @param facility The facility as used in the syslog openlog() function()
* @param log_level log level of this destination. only log messages
* with a log level >= the log level of the destination are actually logged.
* @param log_location whether to log info about the source file and line number
* in a log message.
* @return An opaque uc_log_destination that can be added as a destination to
* the ucore logging system
*/
struct uc_log_destination *uc_log_new_syslog(const char *ident, int facility, int log_level, int log_location);
/* creates a new uc_log_destination for printing on stderr.
*
* @param log_level log level of this destination. only log messages
* with a log level >= the log level of the destination are actually logged.
* @param log_location whether to log info about the source file and line number
* in a log message.
* @param return An opaque uc_log_destination that can be added as a destination to
* the ucore logging system
*/
struct uc_log_destination *uc_log_new_file(const char *filename, int log_level, int log_location);
/** Add a uc_log_destination to the logging system.
*
* @param dest The uc_log_destination to add.
*/
void uc_log_add_destination(struct uc_log_destination *dest);
/** Remove a log destination.
* Note that this does not free the destination, it can be readded later.
* For file destination, the log file is NOT closed.
*
* @param dest The uc_log_destination to remove.
*/
void uc_log_remove_destination(struct uc_log_destination *dest);
/** Remove and free a destination.
* This frees the memory allocated to the destination. For
* UC_LDEST_FILE the log file is closed.
*
* If the destination is not already removed, it will be removed first,
* there's no need to call uc_log_remove_destination first.
* The destination cannot be used again after this function has been called.
*
* @param dest The destination to delete.
*/
void uc_log_delete_destination(struct uc_log_destination *dest);
/** Change the log level of a module.
*
* @param module The module to change, matched against the id member of a struct uc_log_module
* @param level The log level for this module
*
* @return 0 on success, non-zero if the module was not found.
*/
int uc_log_module_set_loglevel(int module, enum UC_LOG_LEVEL level);
/** Closes and re-opens all the UC_LDEST_FILE log destinations.
* Intended for use when a log file is rotated externally.
*
* @param return 0 on success, non-zero if an error occured when re-opening a file.
*/
int uc_log_reopen_files(void);
/** Init the logging system. This function must be called before performing any other
* logging action.
*
* @param user_struct containing an array of all the modules defined by the application.
*/
void uc_log_init(struct uc_log_modules *user_modules);
void uc_logf(int log_level, int module, int raw,
const char *file, int line, const char *fmt, ...)
__attribute__((format(printf, 6, 7)));
#ifdef DEBUG
# define UC_DEBUGF(mod, fmt, ...)\
uc_logf(UC_LL_DEBUG, mod,0 , __FILE__, __LINE__, fmt, ## __VA_ARGS__)
# define UC_DEBUGFR(mod, fmt, ...)\
uc_logf(UC_LL_DEBUG, mod,1 , , __LINE__, fmt, ## __VA_ARGS__)
#else
# define UC_DEBUGF(mod, fmt, ...)
# define UC_DEBUGFR(mod, fmt, ...)
#endif
/** Main macro that should be used for logging.
*
* For log messages with UC_LL_DEBUG, the UC_DEBUGF() macro can be used,
* UC_DEBUGF will only be compiled in if the DEBUG macro is defined
*
* @param lvl log level of this log message
* @param mod module id of this log messagex.
* @param fmt printf style format string
* @param ... printf style arguments
*/
#define UC_LOGF(lvl, mod, fmt, ...) \
uc_logf(lvl, mod, 0, __FILE__, __LINE__, fmt, ## __VA_ARGS__)
/** Raw logging ,Like UC_LOGF, but only prints the supplied
* data, not timestamp, log level etc.
*/
#define UC_LOGFR(lvl, mod, fmt, ...) \
uc_logf(lvl, mod, 1, __FILE__, __LINE__, fmt, ## __VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef UCORE_MATH_H_
#define UCORE_MATH_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint32_t
uc_gcd_32(uint32_t a, uint32_t b);
uint64_t
uc_gcd_64(uint64_t a, uint64_t b);
uint32_t
uc_phi_32(uint32_t N);
uint64_t
uc_phi_64(uint64_t N);
static inline uint32_t
uc_nextpow2(uint32_t x)
{
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
return x;
}
#ifdef __cplusplus
}
#endif
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef UCORE_MERSENNE_TWISTER_H
#define UCORE_MERSENNE_TWISTER_H
/** Context used for the PRNG*/
#define MT_RAND_N 624
typedef struct {
unsigned int x[MT_RAND_N];
int i;
} MTRand;
/** Initialize a Mersenne Twister PRNG context.
* @param seed Starter seed for the PRNG
* @param r context for the PRNG
*/
void mtsrand(int seed, MTRand *r);
/** Generate a new random number.
* Generated range is 0 to UINT_MAX
*
* @param r context for the PRNG
* @return A pseudo random number
*/
unsigned int mtrand(MTRand *r);
#endif
+257
View File
@@ -0,0 +1,257 @@
#ifndef UCORE_PACK_H_
#define UCORE_PACK_H_
#include <stdint.h>
#ifdef __GNUC__
# define UC_INLINE inline __attribute__((always_inline))
#else
# define UC_INLINE inline
#endif
/** Functions for serializing/de-serializing integers to and from
* byte arrays.
* These functions are independant of the host endian. Only
* thing to care about is the endian format of the data in
* the byte array.
*/
/** Serialize a uint16_t to a byte array in little endian format.
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 2.
*/
static UC_INLINE void uc_pack_16_le(uint16_t v, uint8_t *r)
{
r[0] = v;
r[1] = v >> 8;
}
/** De-serialize a uint16_t from a byte array in little endian format.
* @param r The byte array to containing the integer in. Must be at least of size 2.
* @param return The deserialized integer from the byte array.
*/
static UC_INLINE uint16_t uc_unpack_16_le(const uint8_t *r)
{
uint32_t v;
v = r[0];
v |= r[1] << 8;
return v;
}
/** Serialize a uint32_t to a 3 byte(24 bits) array in little endian format.
* Only the bottom 24 bits of the uint32_t are used.
*
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 3.
*/
static UC_INLINE void uc_pack_24_le(uint32_t v, uint8_t *r)
{
r[0] = v;
r[1] = v >> 8;
r[2] = v >> 16;
}
/** De-serialize a uint32_t from a 3 byte (24 bit) byte array in little endian format.
* @param r The byte array to containing the integer in. Must be at least of size 3.
* @param return The deserialized integer from the byte array.
*/
static UC_INLINE uint32_t uc_unpack_24_le(const uint8_t *r)
{
uint32_t v;
v = r[0];
v |= r[1] << 8;
v |= r[2] << 16;
return v;
}
/** Serialize a uint32_t to a byte array in little endian format.
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 4.
*/
static UC_INLINE void uc_pack_32_le(uint32_t v, uint8_t *r)
{
r[0] = v;
r[1] = v >> 8;
r[2] = v >> 16;
r[3] = v >> 24;
}
/** De-serialize a uint32_t from a byte array in little endian format.
* @param r The byte array to containing the integer in. Must be at least of size 4.
* @param return The deserialized integer from the byte array.
*/
static UC_INLINE uint32_t uc_unpack_32_le(const uint8_t *r)
{
uint32_t v;
v = r[0];
v |= r[1] << 8;
v |= r[2] << 16;
v |= r[3] << 24;
return v;
}
/** Serialize a uint64_t to a byte array in little endian format.
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 8
*/
static UC_INLINE void uc_pack_64_le(uint64_t v, uint8_t *r)
{
r[0] = v;
r[1] = v >> 8;
r[2] = v >> 16;
r[3] = v >> 24;
r[4] = v >> 32ULL;
r[5] = v >> 40ULL;
r[6] = v >> 48ULL;
r[7] = v >> 56ULL;
}
/** De-serialize a uint64_t from a byte array in little endian format.
* @param r The byte array to containing the integer in. Must be at least of size 8.
* @param return The deserialized integer from the byte array
*/
static UC_INLINE uint64_t uc_unpack_64_le(const uint8_t *r)
{
uint64_t v;
v = r[0];
v |= r[1] << 8;
v |= r[2] << 16;
v |= (uint32_t)r[3] << 24;
v |= (uint64_t)r[4] << 32ULL;
v |= (uint64_t)r[5] << 40ULL;
v |= (uint64_t)r[6] << 48ULL;
v |= (uint64_t)r[7] << 56ULL;
return v;
}
/** Serialize a uint16_t to a byte array in big endian format.
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 2.
*/
static UC_INLINE void uc_pack_16_be(uint32_t v, uint8_t *r)
{
r[0] = v >> 8;
r[1] = v;
}
/** De-serialize a uint16_t from a byte array in big endian format.
* @param r The byte array to containing the integer in. Must be at least of size 2.
* @param return The deserialized integer from the byte array
*/
static UC_INLINE uint16_t uc_unpack_16_be(const uint8_t *r)
{
uint16_t v;
v = r[0] << 8;
v |= r[1];
return v;
}
/** Serialize a uint32_t to a 3 byte(24 bits) array in big endian format.
* Only the bottom 24 bits of the uint32_t are used.
*
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 3.
*/
static UC_INLINE void uc_pack_24_be(uint32_t v, uint8_t *r)
{
r[0] = v >> 16;
r[1] = v >> 8;
r[2] = v;
}
/** De-serialize a uint32_t from a 3 byte (24 bit) byte array in big endian format.
* @param r The byte array to containing the integer in. Must be at least of size 3.
* @param return The deserialized integer from the byte array.
*/
static UC_INLINE uint32_t uc_unpack_24_be(const uint8_t *r)
{
uint32_t v;
v = r[0] << 16;
v |= r[1] << 8;
v |= r[2];
return v;
}
/** Serialize a uint32_t to a byte array in big endian format.
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 4.
*/
static UC_INLINE void uc_pack_32_be(uint32_t v, uint8_t *r)
{
r[0] = v >> 24;
r[1] = v >> 16;
r[2] = v >> 8;
r[3] = v;
}
/** De-serialize a uint32_t from a byte array in big endian format.
* @param r The byte array to containing the integer in. Must be at least of size 4.
* @param return The deserialized integer from the byte array
*/
static UC_INLINE uint32_t uc_unpack_32_be(const uint8_t *r)
{
uint32_t v;
v = r[0] << 24;
v |= r[1] << 16;
v |= r[2] << 8;
v |= r[3];
return v;
}
/** Serialize a uint16_t to a byte array in big endian format.
* @param v The value to serialize
* @param r The byte array to place the integer in. Must be at least of size 8
*/
static UC_INLINE void uc_pack_64_be(uint64_t v, uint8_t *r)
{
r[0] = v >> 56ULL;
r[1] = v >> 48ULL;
r[2] = v >> 40ULL;
r[3] = v >> 32ULL;
r[4] = v >> 24;
r[5] = v >> 16;
r[6] = v >> 8;
r[7] = v;
}
/** De-serialize a uint64_t from a byte array in big endian format.
* @param r The byte array to containing the integer in. Must be at least of size 4.
* @param return The deserialized integer from the byte array
*/
static UC_INLINE uint64_t uc_unpack_64_be(const uint8_t *r)
{
uint64_t v;
v = (uint64_t)r[0] << 56ULL;
v |= (uint64_t)r[1] << 48ULL;
v |= (uint64_t)r[2] << 40ULL;
v |= (uint64_t)r[3] << 32ULL;
v |= (uint32_t)r[4] << 24;
v |= r[5] << 16;
v |= r[6] << 8;
v |= r[7];
return v;
}
#undef UC_INLINE
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef RBTREE_H_
#define RBTREE_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef enum RBColor RBColor;
enum RBColor {
Red,
Black
};
typedef struct RBNode RBNode;
struct RBNode {
unsigned char color; //Red/Black
RBNode *parent;
RBNode *right;
RBNode *left;
void *data;
};
typedef struct RBTree RBTree;
struct RBTree {
RBNode *node;
int (*compare)(const void *a, const void *b);
};
void uc_rb_remove(RBTree *root, RBNode *node);
RBNode *uc_rb_find(RBTree *root, const void *val);
RBNode *uc_rb_insert(RBTree *root, RBNode *child);
RBNode *uc_rb_first(const RBTree *root);
RBNode *uc_rb_next(RBNode *node);
RBNode *uc_rb_last(const RBTree *root);
#ifdef __cplusplus
}
#endif
#endif
+20
View File
@@ -0,0 +1,20 @@
#ifndef UCORE_READ_FILE_H_
#define UCORE_READ_FILE_H_y
#include <stddef.h>
/** Read the content of a file.
* The returned char* is malloced memory and must be freed by the caller.
* The content is terminated by a nul byte, regardless of whether the content
* is binary or text. The returned length does not include this nul byte.
*
* @param file_name name of the file
* @param length Returned length of the content read
* @param max Fail if the read content length is greater than max.
* @return The content read from the file, or NULL if something failed
* (inspect errno to see why it failed)
*/
char *
uc_read_file(const char *file_name, size_t *length, size_t max);
#endif
+70
View File
@@ -0,0 +1,70 @@
#ifndef SALLOC_H_
#define SALLOC_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Sequntial memory allocator
*
* Allocates bigger chunks of memory at a time, to save the amount of malloc call,
* and enables you to free all that memory in one sweep.
*
* This means that once allocated, a piece of memory obtained from the
* allocator cannot be free'd individually, only the entire SAlloc can be free'd.
*
* Memory is allocated using the underlying malloc() in chunk sizes.
* Once a particular SAlloc has given out all memory in a chunk, a new chunk
* is allocated again, using the underlying malloc().
*
*/
/** Opaque handle to a SAlloc */
typedef struct SAlloc SAlloc;
/** Create a new SAlloc with the given size for each chunk.
* e.g. uc_new_salloc(sizeof(struct Foo) * 1000); will immedeatly
* allocate memory to hold 1000 struct Foo's. Once all that memory is
* handed out from this SAlloc, another piece of memory is allocaterd
* for another 1000 struct Foo's.. All these memory pieces are released
* when the uc_free_salloc() is called.
*
* @param chunksz Size of each piece of memory block allocated.
* @return A new handle for a SAlloc, or NULL.
*/
SAlloc *uc_new_salloc(size_t chunksz);
/** Allocate memory from a SAlloc.
* @param sz size of the memory. This must be <= the chunksz the SAlloc was
* created with.
* @return The new memory, or NULL if sz > chunksz or an underlying
* malloc call fails.
*/
void * uc_s_alloc(SAlloc *p,size_t sz);
/** Just like uc_s_alloc, but zero out the memory before returning it.
*/
void * uc_s_allocz(SAlloc *p,size_t sz);
/** Free the SAlloc and all memory associated with this SAlloc.
* The SAlloc is not usable any more after this call.
*
* @param p The SAlloc to free.
*/
void uc_free_salloc(SAlloc *p);
/** Reset this SAlloc, effectivly releasing all the memory obtained.
* The SAlloc is still usable after this call.
* The actual memory previously allocated from this SAlloc is cached,
* and will be reused by subsequent uc_s_alloc calls.
*
* @param p The SAlloc to reset.
*/
void uc_reset_salloc(SAlloc *p);
#ifdef __cplusplus
}
#endif
#endif
+21
View File
@@ -0,0 +1,21 @@
#ifndef SEQ_H_
#define SEQ_H_
#include <stdint.h>
// Check if a is before b, taking care of wrap arounds
static inline int seq_before(uint32_t a, uint32_t b)
{
return (int32_t)(a - b) < 0;
}
// check if a is before b, taking care of wrap arounds
#define seq_after(a, b) before(b, a)
//check if a is between b or c
static inline int seqbetween(uint32_t a, uint32_t b, uint32_t c)
{
return c - b >= a - b;
}
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef UCORE_STRING_H_
#define UCORE_STRING_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
size_t
uc_base64_enc(const unsigned char *data,size_t len,unsigned char *result);
size_t
uc_base64_dec(const unsigned char *data, size_t len, unsigned char *result);
char*
uc_hex_encode(const uint8_t *in, size_t len, char *out);
uint8_t*
uc_hex_decode(const char *in, size_t maxlen,uint8_t *out);
char*
uc_hex_encode_delim(const uint8_t *in, size_t inlen, char *out, int outlen, char *delim);
void
uc_str_tolower(char *s);
void
uc_str_toupper(char *s);
#define UC_BCD_LEN(ascii_len) ((ascii_len) / 2 + ((ascii_len) % 2))
#define UC_ASCII_LEN(bcd_len) ((bcd_len) * 2 + 1)
size_t
uc_ascii2bcd(const char *ascii, unsigned char *bcdout, unsigned char filler);
size_t
uc_bcd2ascii(const unsigned char *bcd, size_t bcdlen, char *asciiout);
char*
uc_sprintb(char *result, unsigned int value);
char*
uc_sprintbc(char *result, unsigned char value);
char*
uc_sprintbll(char *result, unsigned long long value);
char*
uc_sprintbs(char *result, unsigned short value);
int
getfields(char *str, char **args, int max, int mflag, const char *set);
//ocnvert bytes to a human representable test, (i.e. 12kB, 160 MB 1.78 GB etc.
///result should be at least of size 12,
///returns the result argument
char *
uc_human_bytesz(uint64_t bytes, char *result, size_t result_len);
#ifdef __cplusplus
}
#endif
#endif
+248
View File
@@ -0,0 +1,248 @@
#ifndef _THREADQUEUE_H_
#define _THREADQUEUE_H_ 1
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup ThreadQueue ThreadQueue
* @{
*
* Little API for waitable queues, typically used for passing messages
* between threads.
*
* @author Nils O. Selåsdal <NOS@Utel.no>
*/
/**
* A thread message used to add and retrieve messages
* from in a queue.
* An application must not touch this struct
* when a message is present (i.e. it has been added
* to a queue, but not retrieved yet) in a queue.
*
*/
struct uc_threadmsg{
/**
* A message type the application can use to
* discriminate different messages.
* A negative value will cause a uc_threadmsg to
* be added to the head of a queue.
*/
long msgtype;
/**
* Internal pointer used by the uc_thread_queue.
* Applications must not use this member.
*/
struct uc_threadmsg *next;
};
/**
* Callback function for the walk and destroy functions.
*/
typedef void (*uc_thread_queue_walk_func) (struct uc_threadmsg *msg);
/**
* A ThreadQueue
*
*
* You should threat this struct as opaque never ever access any of
* the variables in this struct.
* You have been warned.
*
* The uc_thread_queue_ functions only deal with struct uc_threadmsg
* structs.
* In order for the message passing to be useful, more data needs to be
* associated with a message, it's up to the application to manage this.
* One way is to e.g. add the struct uc_threadmsg as the first member
* of a larger struct, and recover the larger struct after getting a
* struct uc_threadmsg out of the queue. e.g.
*
* @code
* struct my_msg {
* struct uc_threadmsg tmsg;
* int foo;
* char bar[32];
* };
*
* struct my_msg *msg = malloc(sizeof *msg);
* msg->tmsg.msgtype = MSGTYP1;
* ...
* uc_thread_queue_add(queue, &msg->tmsg);
*
* The receiver end does e.g.
*
* struct uc_threadmsg *tmsg;
* uc_thread_queue_get(queue, NULL, &tmsg);
* switch(tmsg->msgtype) {
* case MYMSG1; {
* struct my_msg *msg = (struct my_msg*)tmsg;
* ...
* free(msg);
* break
* ...
* }
* }
*
* @endcode
*
*
*
*
*/
struct uc_threadqueue {
/**
* Number of elements in the queue.
* Use #threadqueue_length to read it.
*/
long num_elements;
/** Max number of elements this queue will hold */
long max_elements;
/**
* Mutex for the queue.
*/
pthread_mutex_t mutex;
/**
* Condition variable for readers on the queue.
*/
pthread_cond_t read_cond;
/**
* Condition variable for writers on the queue (if the queue is full).
*/
pthread_cond_t write_cond;
/**
* Number of threads blocking on writing to the queue
*/
long num_read_waiters;
/**
* Number of threads blocking on reading from the queue
*/
long num_write_waiters;
/**
* Internal pointers for the messages in the queue.
*/
struct uc_threadmsg *first,**last;
};
/**
* Initializes a queue.
*
* thread_queue_init initializes a new threadqueue. A new queue must always
* be initialized before it is used.
* A max number of elements the queue will hold must be given.
* Adding more elements than a queue will hold will e.g. cause
* uc_thread_queue_add to block until space becomes available.
*
* @param queue Pointer to the queue that should be initialized
* @param max_elements Max number of elements this queue can hold.
*
* @return 0 on success EINVAL if queue is NULL or max_elements <= 0, or
* another errno value if pthread_ functions fails
*/
int uc_thread_queue_init(struct uc_threadqueue *queue, long max_elements);
/**
* Adds a message to a queue
*
* thread_queue_add adds a "message" to the specified queue.
* It is up to the application to manage the data and memory indicated by the
* struct uc_threadmsg.
* The struct uc_threadmsg is assumed to be an intrusive pointer,
* e.g. it can be the first member of a larger struct actually containing the
* data to be passed over.
*
* Nothing is copied so the application must keep track on (de)allocation of the pointers.
* A message type can also be also specified, to e.g. help discriminate
* messages when messages are received.
*
* If the message_type member of @msg is negative, the message is added to
* the front of the queue, i.e. it can be considered a "priority" message.
*
* @param queue Pointer to the queue on where the message should be added.
* @param data the "message".
* @return 0 on succes ENOMEM if out of memory EINVAL if queue is NULL, or other
* errno values if pthread functions failed.
*/
int uc_thread_queue_add(struct uc_threadqueue *queue, struct uc_threadmsg *msg);
/**
* Gets a message from a queue
*
* thread_queue_get gets a message from the specified queue, it will block
* the caling thread untill a message arrives, or the (optional) timeout occurs.
* If timeout is NULL, there will be no timeout, and thread_queue_get will wait
* untill a message arrives.
*
* struct timespec is defined as:
* @code
* struct timespec {
* long tv_sec; // seconds
* long tv_nsec; // nanoseconds
* };
* @endcode
*
* @param queue Pointer to the queue to wait on for a message.
* @param timeout timeout on how long to wait on a message, or NULL for no timeout
* @param msg pointer where the uc_threadmsg* is stored
*
* @return 0 on success EINVAL if queue is NULL ETIMEDOUT if timeout occurs
*/
int uc_thread_queue_get(struct uc_threadqueue *queue, const struct timespec *timeout, struct uc_threadmsg **msg);
/**
* Gets the length of a queue
*
* threadqueue_length returns the number of messages waiting in the queue
*
* @param queue Pointer to the queue for which to get the length
* @return the length(number of pending messages) in the queue
*/
long uc_thread_queue_length( struct uc_threadqueue *queue );
/**
* Destroy the queue.
*
* If free_func is != NULL, free_func will be called for every item, allowing you to free
* the item.
* You cannot call this if there are someone currently adding or getting messages
* from the queue.
* After a queue have been cleaned, it cannot be used again untill #thread_queue_init
* has been called on the queue.
*
* @param queue Pointer to the queue that should be cleaned
* @param free_func pointer to function that will be called for each item.
* The function must not in anyway interact with the queue.
* @return 0 on success EINVAL if queue is NULL EBUSY if someone is holding any locks on the queue
*/
int uc_thread_queue_destroy(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func);
/**
* Walk the elements of the queue, intended for debugging
*
* The supplied function will be called for each item in the queue, internal queue
* locks are held while the function is called, so the supplied function must not
* interact with the queue, else deadlock occors.
*
* @param queue Pointer to the queue to walk
* @param free_func pointer to function that will be called for each item.
* The function must not in anyway interact with the queue.
* @return 0 on success
*/
int uc_thread_queue_walk(struct uc_threadqueue *queue, uc_thread_queue_walk_func walk_func);
/**
* @}*/
#ifdef __cplusplus
}
#endif
#endif
+63
View File
@@ -0,0 +1,63 @@
#ifndef UCORE_TIMERS_H_
#define UCORE_TIMERS_H_
#include <stddef.h>
#include <sys/queue.h>
#include <sys/time.h>
#include "ucore_rbtree.h"
struct UCTimer;
struct UCTimers;
//timer states
enum {
//not running
UC_TIMER_INACTIVE = 0,
//running
UC_TIMER_ACTIVE = 1,
//running and pending to be fired in this run of uc_timers_run
UC_TIMER_PENDING = 2
};
typedef void (*uc_timer_cb)(struct UCTimers *timers, struct UCTimer *timer);
struct UCTimer {
//internal members
RBNode rb_node; //the RBNode will hold a data pointer to the
//the UCTimer it's a member of. We might get rid of that member, and use CONTAINER_OF
//to find the UCTimer from a RBNode
//
//absolute time when the timer should be fired
struct timeval timeout;
size_t seq; //needed to keep timeout unique
//as the rbtree can only handle unique values
unsigned char state;
TAILQ_ENTRY(UCTimer) ready_entry; //entry in ready_list in UCTimers
//callback function
uc_timer_cb callback;
//user data for the callback
void *cookie_ptr;
int cookie_int;
int cookie_int2;
};
struct UCTimers {
RBTree timers;
size_t seq; //used to generate unique timers
TAILQ_HEAD(, UCTimer) ready_list; //pending timers to be fired while inside uc_timers_run
};
void uc_timer_remove(struct UCTimers *timers, struct UCTimer *timer);
void uc_timers_init(struct UCTimers *t);
void uc_timer_add(struct UCTimers *timers, struct UCTimer *timer, int sec, int usec);
void uc_timer_remove(struct UCTimers *timers, struct UCTimer *timer);
int uc_timers_first(const struct UCTimers *timers, struct timeval *first);
size_t uc_timer_count(const struct UCTimers *timers);
int uc_timer_running(const struct UCTimer *timer);
int uc_timers_run(struct UCTimers *timers, const struct timeval *now);
#endif
+55
View File
@@ -0,0 +1,55 @@
#ifndef UCORE_UTILS_H_
#define UCORE_UTILS_H_
#include <stddef.h>
//Gnerate a compiler error if the compile time
//constant expression fails
#define STATIC_ASSERT(expr) \
do { \
enum { assert_static__ = 1/(expr) }; \
} while (0)
#ifdef DEBUG
#define TRACEF(fmt, ...)\
do { \
fprintf(stderr, "%s:%s(%d)" fmt, __FILE__, __FUNCTION__, __LINE,__VA_ARGS__);\
} while(0)
#else
#define TRACEF(fmt, ...)
#endif
//MAX of a and b
#define UC_MAX(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
//min of a and b
#define UC_MIN(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
//given 'ptr' as a pointer to a struct 'member',
//find the struct that ptr is the member of, where
//'type' is the type of the containing struct
//e.g.
//struct foo {
// int i;
// struct bar zap;
//};
// ...
//struct bar *p = ..; //the local p is a pointer to
//a 'zap' member inside a struct foo.
//give us the struct foo*:
//struct foo *f = CONTAINER_OF(p, struct foo, zap);
#define CONTAINER_OF(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) ); })
#define UC_ALIGN(val,align) (((val)+(align)-1UL)&~((align)-1UL))
#endif
+7
View File
@@ -0,0 +1,7 @@
const int ucore_version_major = @version_major@;
const int ucore_version_minor = @version_minor@;
const int ucore_version_patch = @version_patch@;
const char ucore_version_str[] = "@version_major@.@version_minor@.@version_patch@.@version_revision@";
+47
View File
@@ -0,0 +1,47 @@
#ifndef UCORE_VERSION_H_
#define UCORE_VERSION_H_
/**
* @mainpage
* @htmlonly
* <pre>
* Copyright (c) 2002-2012 Nils O. Selåsdal <NOS@Utel.no>.
* All rights reserved, all wrongs reversed.
*
* 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 AUTHOR ``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 AUTHOR 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.
* </pre>
* @endhtmlonly
*/
/* Major version of the ucore library*/
extern const int ucore_version_major;
/* Minor version of the ucore library*/
extern const int ucore_version_minor;
/* Patch version of the ucore library*/
extern const int ucore_version_patch;
/** The version number as a string.
* Might contain non-numeric suffix indicating
* e.g. development version or similar*/
extern const char ucore_version_str[];
#endif