Initial contribution

This commit is contained in:
Michiel Beemster 2018-04-10 17:03:59 +02:00
parent 7b5cc4fa59
commit 11d9ce37aa
580 changed files with 155133 additions and 162 deletions

27
src/util/CMakeLists.txt Normal file
View file

@ -0,0 +1,27 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
include (GenerateExportHeader)
#PREPEND(srcs_platform ${platform} os_platform_errno.c os_platform_heap.c os_platform_init.c os_platform_process.c os_platform_socket.c os_platform_stdlib.c os_platform_sync.c os_platform_thread.c os_platform_time.c)
#add_library(util ut_avl.c ut_crc.c ut_expand_envvars.c ut_fibheap.c ut_handleserver.c ut_hopscotch.c ut_thread_pool.c ut_xmlparser.c)
PREPEND(srcs_util "${CMAKE_CURRENT_SOURCE_DIR}/src" ut_avl.c ut_crc.c ut_expand_envvars.c ut_fibheap.c ut_handleserver.c ut_hopscotch.c ut_thread_pool.c ut_xmlparser.c)
add_library(util ${srcs_util})
generate_export_header(util EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/exports/util/ut_export.h")
target_link_libraries(util PUBLIC OSAPI)
target_include_directories(util PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/exports/>"
"$<INSTALL_INTERFACE:${INSTALL_PREFIX}/include/>" "$<INSTALL_INTERFACE:${INSTALL_PREFIX}/exports/>")
# TODO: improve test inclusion.
if((BUILD_TESTING) AND ((NOT DEFINED MSVC_VERSION) OR (MSVC_VERSION GREATER "1800")))
add_subdirectory(tests)
endif()

View file

@ -0,0 +1,357 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_AVL_H
#define UT_AVL_H
/* The tree library never performs memory allocations or deallocations internally.
- Treedef_t: defines the properties of the tree, offsets,
comparison functions, augmented structures, flags -- these are
related to the code/data structure in which the tree is embedded,
and in nearly all cases known at compile time.
- avlTree_t: represents the tree, i.e., pointer to the root.
- avlNode_t: contains the administrative data for a single node in
the tree.
For a tree node:
struct T {
avlNode_t avlnode;
int key;
};
by definition, avlnodeoffset == offsetof(struct T, avlnode) and
keyoffset = offsetof(struct T, key). The user of the library only
ever deals in pointers to (in this case) struct T, never with
pointers to the avlNode_t, and the compare function operations on
pointers to keys, in this case pointers to "int"s. If you wish, you
can also do: keyoffset = 0, in which case the compare function
would be operating on struct T's.
The compare function is assumed to behave just like all compare
functions in the C library: < 0, =0, >0 for left argument less
than, equal to or greater than the right argument.
The "augment" function is automatically called whenever some of the
children of a node change, as well as when the "augment" function
has been called on some of the children. It allows you to maintain
a "summary" of the subtree -- currently only used in ddsi2e, in one
spot.
Trees come in various "variants", configured through "treedef"
flags:
- direct/indirect key: direct meaning the key value is embedded in
the structure containing the avlNode_t, indirect meaning a
pointer to the key value is. The compare function doesn't deal
with tree nodes, but with key values.
- re-entrant: in the style of the C library, meaning, the
comparison function gets a user-supplied 3rd argument (in
particular used by mmstat).
- unique keys/duplicate keys: when keys must be unique, some
optimizations apply; it is up to the caller to ensure one doesn't
violate the uniqueness of the keys (it'll happily crash in insert
if you don't); when duplicate keys are allowed, a forward scan of
the tree will visit them in the order of insertion.
For a tree node:
struct T {
avlnode_t avlnode;
char *key;
};
you could set the "indirect" flag, and then you simply use
strcmp(), avoiding the need for passing templates in looking up key
values. Much nicer.
There is also an orthogonal variant that is enforced through the
type system -- note that would be possible for all of the above as
well, but the number of cases simply explodes and none of the above
flags affects the dynamically changing data structures (just the
tree definition), unlike this one.
- the "C" variant keeps track of the number of nodes in the tree to
support a "count" operation in O(1) time, but is otherwise
identical.
The various initializer macros and TreedefInit functions should
make sense with this.
All functions for looking up nodes return NULL if there is no node
satisfying the requirements.
- Init: initializes a tree (really just: root = NULL, perhaps count = 0)
- Free: calls "freefun" on each node, which may free the node
- FreeArg: as "Free", but with an extra, user-supplied, argument
- Root: returns the root node
- Lookup: returns a node with key value "key" (ref allowdups flag)
- LookupIPath: like Lookup, but also filling an IPath_t structure
for efficient insertion in case of a failed lookup (or inserting
duplicates)
- LookupDPath: like Lookup, but also filling a DPath_t structure
that helps with deleting a node
- LookupPredEq: locates the node with the greatest key value <= "key"
- LookupSuccEq: similar, but smallest key value >= "key"
- LookupPred: similar, < "key"
- LookupSucc: similar, > "key"
- Insert: convenience function: LookupIPath ; InsertIPath
- Delete: convenience function: LookupDPath ; DeleteDPath
- InsertIPath: insert node based on the "path" obtained from LookupIPath
- DeleteDPath: delete node, using information in "path" to do so efficiently
- SwapNode: replace "oldn" by "newn" without modifying the tree
structure (the key need not be equal, but must be
FindPred(oldn).key < newn.key < FindSucc(oldn).key, where a
non-existing predecessor has key -inf and a non-existing
successor has key +inf, and where it is understood that the <
operator becomes <= if allowdups is set
- AugmentUpdate: to be called when something in "node" changes that
affects the subtree "summary" computed by the configured
"augment" function
- IsEmpty: returns 1 if tree is empty, 0 if not
- IsSingleton: returns 1 if tree contains exactly one node, 0 if not
- FindMin: returns the node with the smallest key value in the tree
- FindMax: similar, largest key value
- FindPred: preceding node in in-order treewalk
- FindSucc: similar, following node
- Walk: calls "f" with user-supplied argument "a" once for each
node, starting at FindMin and ending at FindMax
- ConstWalk: same, but with a const tree
- WalkRange: like Walk, but only visiting nodes with key values in
range [min,max] (that's inclusive)
- ConstWalkRange: same, but with a const tree
- WalkRangeReverse: like WalkRange, but in the reverse direction
- ConstWalkRangeReverse: same, but with a const tree
- IterFirst: starts forward iteration, starting at (and returning) FindMin
- IterSuccEq: similar, starting at LookupSuccEq
- IterSucc: similar, starting at LookupSucc
- IterNext: returns FindSucc(last returned node); may not be called
if preceding IterXXX call on same "iter" returned NULL
That's all there is to it.
Note that all calls to Walk(f,a) can be rewritten as:
for(n=IterFirst(&it); n; n=IterNext(&it)) { f(n,a) }
or as
for(n=FindMin(); n; n=FindSucc(n)) { f(n,a) }
The walk functions and iterators may not alter the tree
structure. If that is desired, the latter can easily be rewritten
as:
n=FindMin() ; while(n) { nn=FindSucc(n); f(n,a); n=nn }
because FindMin/FindSucc doesn't store any information to allow
fast processing. That'll allow every operation, with the obvious
exception of f(n) calling Delete(FindSucc(n)).
Currently, all trees maintain parent pointers, but it may be worth
doing a separate set without it, as it reduces the size of
avlNode_t. But in that case, the FindMin/FindSucc option would no
longer be a reasonable option because it would be prohibitively
expensive, whereas the IterFirst/IterNext option are alway
efficiently. If one were to do a threaded tree variant, the
implemetantion of IterFirst/IterNext would become absolute trivial
and faster still, but at the cost of significantly more overhead in
memory and updates. */
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
#define UT_AVL_MAX_TREEHEIGHT (12 * sizeof (void *))
typedef int (*ut_avlCompare_t) (_In_ const void *a, _In_ const void *b);
typedef int (*ut_avlCompare_r_t) (_In_ const void *a, _In_ const void *b, _In_opt_ void *arg);
typedef void (*ut_avlAugment_t) (_Inout_ void *node, _In_opt_ const void *left, _In_opt_ const void *right);
typedef void (*ut_avlWalk_t) (_Inout_ void *node, _Inout_opt_ void *arg);
typedef void (*ut_avlConstWalk_t) (_In_ const void *node, _Inout_opt_ void *arg);
typedef struct ut_avlNode {
struct ut_avlNode *cs[2]; /* 0 = left, 1 = right */
struct ut_avlNode *parent;
int height;
} ut_avlNode_t;
#define UT_AVL_TREEDEF_FLAG_INDKEY 1
#define UT_AVL_TREEDEF_FLAG_R 2
#define UT_AVL_TREEDEF_FLAG_ALLOWDUPS 4
typedef struct ut_avlTreedef {
#if defined (__cplusplus)
ut_avlTreedef() {}
#endif
size_t avlnodeoffset;
size_t keyoffset;
union {
int (*cmp) (void);
ut_avlCompare_t comparekk;
ut_avlCompare_r_t comparekk_r;
} u;
ut_avlAugment_t augment;
uint32_t flags;
void *cmp_arg; /* for _r variant */
} ut_avlTreedef_t;
typedef struct ut_avlCTreedef {
ut_avlTreedef_t t;
} ut_avlCTreedef_t;
typedef struct ut_avlTree {
ut_avlNode_t *root;
} ut_avlTree_t;
typedef struct ut_avlCTree {
ut_avlTree_t t;
size_t count;
} ut_avlCTree_t;
typedef struct ut_avlPath {
int depth; /* total depth of path */
int pnodeidx;
ut_avlNode_t *parent; /* (nodeidx == 0 ? NULL : *(path[nodeidx-1])) */
ut_avlNode_t **pnode[UT_AVL_MAX_TREEHEIGHT];
} ut_avlPath_t;
typedef struct ut_avlIPath {
ut_avlPath_t p;
} ut_avlIPath_t;
typedef struct ut_avlDPath {
ut_avlPath_t p;
} ut_avlDPath_t;
typedef struct ut_avlIter {
const ut_avlTreedef_t *td;
ut_avlNode_t *right;
ut_avlNode_t **todop;
ut_avlNode_t *todo[1+UT_AVL_MAX_TREEHEIGHT];
} ut_avlIter_t;
typedef struct ut_avlCIter {
ut_avlIter_t t;
} ut_avlCIter_t;
/* avlnodeoffset and keyoffset must both be in [0,2**31-1] */
#define UT_AVL_TREEDEF_INITIALIZER(avlnodeoffset, keyoffset, comparekk, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), 0, 0 }
#define UT_AVL_TREEDEF_INITIALIZER_INDKEY(avlnodeoffset, keyoffset, comparekk, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_INDKEY, 0 }
#define UT_AVL_TREEDEF_INITIALIZER_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_ALLOWDUPS, 0 }
#define UT_AVL_TREEDEF_INITIALIZER_INDKEY_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_INDKEY|UT_AVL_TREEDEF_FLAG_ALLOWDUPS, 0 }
#define UT_AVL_TREEDEF_INITIALIZER_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_R, (cmparg) }
#define UT_AVL_TREEDEF_INITIALIZER_INDKEY_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_INDKEY|UT_AVL_TREEDEF_FLAG_R, (cmparg) }
#define UT_AVL_TREEDEF_INITIALIZER_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_R|UT_AVL_TREEDEF_FLAG_ALLOWDUPS, (cmparg) }
#define UT_AVL_TREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { (avlnodeoffset), (keyoffset), { (int (*) (void)) (comparekk) }, (augment), UT_AVL_TREEDEF_FLAG_INDKEY|UT_AVL_TREEDEF_FLAG_R|UT_AVL_TREEDEF_FLAG_ALLOWDUPS, (cmparg) }
/* Not maintaining # nodes */
UTIL_EXPORT void ut_avlTreedefInit (_Out_ ut_avlTreedef_t *td, size_t avlnodeoffset, size_t keyoffset, _In_ ut_avlCompare_t comparekk, _In_opt_ ut_avlAugment_t augment, uint32_t flags) __nonnull((1,4));
UTIL_EXPORT void ut_avlTreedefInit_r (_Out_ ut_avlTreedef_t *td, size_t avlnodeoffset, size_t keyoffset, _In_ ut_avlCompare_r_t comparekk_r, _Inout_opt_ void *cmp_arg, ut_avlAugment_t augment, uint32_t flags) __nonnull((1,4));
UTIL_EXPORT void ut_avlInit (_In_ const ut_avlTreedef_t *td, _Out_ ut_avlTree_t *tree) __nonnull_all__;
UTIL_EXPORT void ut_avlFree (_In_ const ut_avlTreedef_t *td, _Inout_ _Post_invalid_ ut_avlTree_t *tree, _In_opt_ void (*freefun) (_Inout_ void *node)) __nonnull((1,2));
UTIL_EXPORT void ut_avlFreeArg (_In_ const ut_avlTreedef_t *td, _Inout_ _Post_invalid_ ut_avlTree_t *tree, _In_opt_ void (*freefun) (_Inout_ void *node, _Inout_opt_ void *arg), _Inout_opt_ void *arg) __nonnull((1,2));
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlRoot (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree) __nonnull_all__;
_Ret_notnull_ UTIL_EXPORT void *ut_avlRootNonEmpty (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlLookup (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key) __nonnull_all__;
_Ret_maybenull_ UTIL_EXPORT void *ut_avlLookupIPath (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key, _Out_ ut_avlIPath_t *path) __nonnull_all__;
_Ret_maybenull_ UTIL_EXPORT void *ut_avlLookupDPath (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key, _Out_ ut_avlDPath_t *path) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlLookupPredEq (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlLookupSuccEq (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlLookupPred (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlLookupSucc (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *key) __nonnull_all__;
UTIL_EXPORT void ut_avlInsert (_In_ const ut_avlTreedef_t *td, _Inout_ ut_avlTree_t *tree, _Inout_ void *node) __nonnull_all__;
UTIL_EXPORT void ut_avlDelete (_In_ const ut_avlTreedef_t *td, _Inout_ ut_avlTree_t *tree, _Inout_ void *node) __nonnull_all__;
UTIL_EXPORT void ut_avlInsertIPath (_In_ const ut_avlTreedef_t *td, ut_avlTree_t *tree, _Inout_ void *node, _Inout_ _Post_invalid_ ut_avlIPath_t *path) __nonnull_all__;
UTIL_EXPORT void ut_avlDeleteDPath (_In_ const ut_avlTreedef_t *td, _Inout_ ut_avlTree_t *tree, _Inout_ void *node, _Inout_ _Post_invalid_ ut_avlDPath_t *path) __nonnull_all__;
UTIL_EXPORT void ut_avlSwapNode (_In_ const ut_avlTreedef_t *td, _Inout_ ut_avlTree_t *tree, _Inout_ void *oldn, _Inout_ void *newn) __nonnull_all__;
UTIL_EXPORT void ut_avlAugmentUpdate (_In_ const ut_avlTreedef_t *td, _Inout_ void *node) __nonnull_all__;
UTIL_EXPORT int ut_avlIsEmpty (_In_ const ut_avlTree_t *tree) __nonnull_all__;
UTIL_EXPORT int ut_avlIsSingleton (_In_ const ut_avlTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlFindMin (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlFindMax (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlFindPred (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_opt_ const void *vnode) __nonnull((1,2));
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlFindSucc (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_opt_ const void *vnode) __nonnull((1,2));
UTIL_EXPORT void ut_avlWalk (_In_ const ut_avlTreedef_t *td, _In_ ut_avlTree_t *tree, _In_ ut_avlWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3));
UTIL_EXPORT void ut_avlConstWalk (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ ut_avlConstWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3));
UTIL_EXPORT void ut_avlWalkRange (_In_ const ut_avlTreedef_t *td, _In_ ut_avlTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3,4,5));
UTIL_EXPORT void ut_avlConstWalkRange (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlConstWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3,4,5));
UTIL_EXPORT void ut_avlWalkRangeReverse (_In_ const ut_avlTreedef_t *td, ut_avlTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3));
UTIL_EXPORT void ut_avlConstWalkRangeReverse (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlConstWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3));
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlIterFirst (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _Out_ _When_ (return == 0, _Post_invalid_) ut_avlIter_t *iter) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlIterSuccEq (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _Out_ _When_ (return == 0, _Post_invalid_) ut_avlIter_t *iter, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlIterSucc (_In_ const ut_avlTreedef_t *td, _In_ const ut_avlTree_t *tree, _Out_ _When_ (return == 0, _Post_invalid_) ut_avlIter_t *iter, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlIterNext (_Inout_ _When_ (return == 0, _Post_invalid_) ut_avlIter_t *iter) __nonnull_all__;
/* Maintaining # nodes */
#define UT_AVL_CTREEDEF_INITIALIZER(avlnodeoffset, keyoffset, comparekk, augment) { UT_AVL_TREEDEF_INITIALIZER (avlnodeoffset, keyoffset, comparekk, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_INDKEY(avlnodeoffset, keyoffset, comparekk, augment) { UT_AVL_TREEDEF_INITIALIZER_INDKEY (avlnodeoffset, keyoffset, comparekk, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { UT_AVL_TREEDEF_INITIALIZER_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_INDKEY_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { UT_AVL_TREEDEF_INITIALIZER_INDKEY_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { UT_AVL_TREEDEF_INITIALIZER_R (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_INDKEY_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { UT_AVL_TREEDEF_INITIALIZER_INDKEY_R (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { UT_AVL_TREEDEF_INITIALIZER_R_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define UT_AVL_CTREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { UT_AVL_TREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
UTIL_EXPORT void ut_avlCTreedefInit (_Out_ ut_avlCTreedef_t *td, size_t avlnodeoffset, size_t keyoffset, _In_ ut_avlCompare_t comparekk, _In_opt_ ut_avlAugment_t augment, uint32_t flags) __nonnull((1,4));
UTIL_EXPORT void ut_avlCTreedefInit_r (_Out_ ut_avlCTreedef_t *td, size_t avlnodeoffset, size_t keyoffset, _In_ ut_avlCompare_r_t comparekk_r, _Inout_opt_ void *cmp_arg, _In_opt_ ut_avlAugment_t augment, uint32_t flags) __nonnull((1,4));
UTIL_EXPORT void ut_avlCInit (_In_ const ut_avlCTreedef_t *td, _Out_ ut_avlCTree_t *tree) __nonnull_all__;
UTIL_EXPORT void ut_avlCFree (_In_ const ut_avlCTreedef_t *td, _Inout_ _Post_invalid_ ut_avlCTree_t *tree, _In_opt_ void (*freefun) (_Inout_ void *node)) __nonnull((1,2));
UTIL_EXPORT void ut_avlCFreeArg (_In_ const ut_avlCTreedef_t *td, _Inout_ _Post_invalid_ ut_avlCTree_t *tree, _In_opt_ void (*freefun) (_Inout_ void *node, _Inout_opt_ void *arg), _Inout_opt_ void *arg) __nonnull((1,2));
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCRoot (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree) __nonnull_all__;
_Ret_notnull_ UTIL_EXPORT void *ut_avlCRootNonEmpty (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookup (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key) __nonnull_all__;
_Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookupIPath (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key, _Out_ ut_avlIPath_t *path) __nonnull_all__;
_Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookupDPath (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key, _Out_ ut_avlDPath_t *path) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookupPredEq (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookupSuccEq (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookupPred (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCLookupSucc (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *key) __nonnull_all__;
UTIL_EXPORT void ut_avlCInsert (_In_ const ut_avlCTreedef_t *td, _Inout_ ut_avlCTree_t *tree, _Inout_ void *node) __nonnull_all__;
UTIL_EXPORT void ut_avlCDelete (_In_ const ut_avlCTreedef_t *td, _Inout_ ut_avlCTree_t *tree, _Inout_ void *node) __nonnull_all__;
UTIL_EXPORT void ut_avlCInsertIPath (_In_ const ut_avlCTreedef_t *td, _Inout_ ut_avlCTree_t *tree, _Inout_ void *node, _Inout_ _Post_invalid_ ut_avlIPath_t *path) __nonnull_all__;
UTIL_EXPORT void ut_avlCDeleteDPath (_In_ const ut_avlCTreedef_t *td, _Inout_ ut_avlCTree_t *tree, _Inout_ void *node, _Inout_ _Post_invalid_ ut_avlDPath_t *path) __nonnull_all__;
UTIL_EXPORT void ut_avlCSwapNode (_In_ const ut_avlCTreedef_t *td, _Inout_ ut_avlCTree_t *tree, _Inout_ void *oldn, _Inout_ void *newn) __nonnull_all__;
UTIL_EXPORT void ut_avlCAugmentUpdate (_In_ const ut_avlCTreedef_t *td, _Inout_ void *node) __nonnull_all__;
UTIL_EXPORT int ut_avlCIsEmpty (_In_ const ut_avlCTree_t *tree) __nonnull_all__;
UTIL_EXPORT int ut_avlCIsSingleton (_In_ const ut_avlCTree_t *tree) __nonnull_all__;
UTIL_EXPORT size_t ut_avlCCount (_In_ const ut_avlCTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCFindMin (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCFindMax (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCFindPred (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *vnode) __nonnull((1,2));
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCFindSucc (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *vnode) __nonnull((1,2));
UTIL_EXPORT void ut_avlCWalk (_In_ const ut_avlCTreedef_t *td, _In_ ut_avlCTree_t *tree, _In_ ut_avlWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3));
UTIL_EXPORT void ut_avlCConstWalk (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ ut_avlConstWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3));
UTIL_EXPORT void ut_avlCWalkRange (_In_ const ut_avlCTreedef_t *td, _In_ ut_avlCTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3,4,5));
UTIL_EXPORT void ut_avlCConstWalkRange (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlConstWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3,4,5));
UTIL_EXPORT void ut_avlCWalkRangeReverse (_In_ const ut_avlCTreedef_t *td, _In_ ut_avlCTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3,4,5));
UTIL_EXPORT void ut_avlCConstWalkRangeReverse (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _In_ const void *min, _In_ const void *max, _In_ ut_avlConstWalk_t f, _Inout_opt_ void *a) __nonnull((1,2,3,4,5));
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCIterFirst (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _Out_ _When_ (return == 0, _Post_invalid_) ut_avlCIter_t *iter) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCIterSuccEq (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _Out_ _When_ (return == 0, _Post_invalid_) ut_avlCIter_t *iter, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCIterSucc (_In_ const ut_avlCTreedef_t *td, _In_ const ut_avlCTree_t *tree, _Out_ _When_ (return == 0, _Post_invalid_) ut_avlCIter_t *iter, _In_ const void *key) __nonnull_all__;
_Check_return_ _Ret_maybenull_ UTIL_EXPORT void *ut_avlCIterNext (_Inout_ _When_ (return == 0, _Post_invalid_) ut_avlCIter_t *iter) __nonnull_all__;
#if defined (__cplusplus)
}
#endif
#endif /* UT_AVL_H */

View file

@ -0,0 +1,30 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_CRC_H
#define UT_CRC_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/* !!!!!!!!NOTE From here no more includes are allowed!!!!!!! */
UTIL_EXPORT uint32_t ut_crcCalculate (const void *buf, size_t length) __nonnull_all__ __attribute_pure__;
#if defined (__cplusplus)
}
#endif
#endif /* UT_CRC_H */

View file

@ -0,0 +1,32 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_EXPAND_ENVVARS_H
#define UT_EXPAND_ENVVARS_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/* Expands ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, but not $X */
UTIL_EXPORT char *ut_expand_envvars(const char *string);
/* Expands $X, ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, $ and \ can be escaped with \ */
UTIL_EXPORT char *ut_expand_envvars_sh(const char *string);
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -0,0 +1,53 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_FIBHEAP_H
#define UT_FIBHEAP_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
typedef struct ut_fibheapNode {
struct ut_fibheapNode *parent, *children;
struct ut_fibheapNode *prev, *next;
unsigned mark: 1;
unsigned degree: 31;
} ut_fibheapNode_t;
typedef struct ut_fibheapDef {
uintptr_t offset;
int (*cmp) (const void *va, const void *vb);
} ut_fibheapDef_t;
typedef struct ut_fibheap {
ut_fibheapNode_t *roots; /* points to root with min key value */
} ut_fibheap_t;
#define UT_FIBHEAPDEF_INITIALIZER(offset, cmp) { (offset), (cmp) }
UTIL_EXPORT void ut_fibheapDefInit (ut_fibheapDef_t *fhdef, uintptr_t offset, int (*cmp) (const void *va, const void *vb));
UTIL_EXPORT void ut_fibheapInit (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh);
UTIL_EXPORT void *ut_fibheapMin (const ut_fibheapDef_t *fhdef, const ut_fibheap_t *fh);
UTIL_EXPORT void ut_fibheapMerge (const ut_fibheapDef_t *fhdef, ut_fibheap_t *a, ut_fibheap_t *b);
UTIL_EXPORT void ut_fibheapInsert (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh, const void *vnode);
UTIL_EXPORT void ut_fibheapDelete (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh, const void *vnode);
UTIL_EXPORT void *ut_fibheapExtractMin (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh);
UTIL_EXPORT void ut_fibheapDecreaseKey (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh, const void *vnode); /* to be called AFTER decreasing the key */
#if defined (__cplusplus)
}
#endif
#endif /* UT_FIBHEAP_H */

View file

@ -0,0 +1,242 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_HANDLESERVER_H
#define UT_HANDLESERVER_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/********************************************************************************************
*
* TODO CHAM-138: Header file improvements
* - Remove some internal design decisions and masks from the header file.
* - Improve function headers where needed.
*
********************************************************************************************/
/*
* Short working ponderings.
*
* A handle will be created with a related object. If you want to protect that object by
* means of a mutex, it's your responsibility, not handleservers'.
*
* The handle server keeps an 'active claim' count. Every time a handle is claimed, the
* count is increase and decreased when the handle is released. A handle can only be
* deleted when there's no more active claim. You can use this to make sure that nobody
* is using the handle anymore, which should mean that nobody is using the related object
* anymore. So, when you can delete the handle, you can safely delete the related object.
*
* To prevent new claims (f.i. when you want to delete the handle), you can close the
* handle. This will not remove any information within the handleserver, it just prevents
* new claims. The delete will actually free handleserver internal memory.
*
* There's currently a global lock in the handle server that is used with every API call.
* Maybe the internals can be improved to circumvent the need for that...
*/
/*
* Some error return values.
*/
typedef _Return_type_success_(return == 0) enum ut_handle_retcode_t {
UT_HANDLE_OK = 0,
UT_HANDLE_ERROR = -1, /* General error. */
UT_HANDLE_CLOSED = -2, /* Handle has been previously close. */
UT_HANDLE_DELETED = -3, /* Handle has been previously deleted. */
UT_HANDLE_INVALID = -4, /* Handle is not a valid handle. */
UT_HANDLE_UNEQUAL_KIND = -5, /* Handle does not contain expected kind. */
UT_HANDLE_TIMEOUT = -6, /* Operation timed out. */
UT_HANDLE_OUT_OF_RESOURCES = -7, /* Action isn't possible because of limited resources. */
UT_HANDLE_NOT_INITALIZED = -8 /* Not initialized. */
} ut_handle_retcode_t;
/*
* The 32 bit handle
* | bits | # values | description |
* --------------------------------------------------------------------------------------
* | 31 | 2 | positive/negative (negative can be used to indicate errors) |
* | 24-30 | 127 | handle kind (value determined by client) |
* | 0-23 | 16.777.215 | index or hash (maintained by the handleserver) |
*
* When the handle is negative, it'll contain a ut_handle_retcode_t error value.
*
* FYI: the entity id within DDSI is also 24 bits...
*/
typedef _Return_type_success_(return > 0) int32_t ut_handle_t;
/*
* Handle bits
* +kkk kkkk iiii iiii iiii iiii iiii iiii
* 31| | 24| 0|
*/
#define UT_HANDLE_SIGN_MASK (0x80000000)
#define UT_HANDLE_KIND_MASK (0x7F000000)
#define UT_HANDLE_IDX_MASK (0x00FFFFFF)
#define UT_HANDLE_DONTCARE_KIND (0)
/*
* The handle link type.
*
* The lookup of an handle should be very quick.
* But to increase the performance just a little bit more, it is possible to
* acquire a handlelink. This can be used by the handleserver to circumvent
* the lookup and go straight to the handle related information.
*
* Almost every handleserver function supports the handlelink. You don't have
* to provide the link. When it is NULL, the normal lookup will be done.
*
* This handlelink is invalid after the related handle is deleted and should
* never be used afterwards.
*/
_Return_type_success_(return != NULL) struct ut_handlelink;
/*
* Initialize handleserver singleton.
*/
_Check_return_ UTIL_EXPORT ut_handle_retcode_t
ut_handleserver_init(void);
/*
* Destroy handleserver singleton.
* The handleserver is destroyed when fini() is called as often as init().
*/
UTIL_EXPORT void
ut_handleserver_fini(void);
/*
* This creates a new handle that contains the given type and is linked to the
* user data.
*
* A kind value != 0 has to be provided, just to make sure that no 0 handles
* will be created. It should also fit the UT_HANDLE_KIND_MASK.
* In other words handle creation will fail if
* ((kind & ~UT_HANDLE_KIND_MASK != 0) || (kind & UT_HANDLE_KIND_MASK == 0)).
*
* It has to do something clever to make sure that a deleted handle is not
* re-issued very quickly after it was deleted.
*
* kind - The handle kind, provided by the client.
* arg - The user data linked to the handle (may be NULL).
*
* Valid handle when returned value is positive.
* Otherwise negative handle is returned.
*/
_Pre_satisfies_((kind & UT_HANDLE_KIND_MASK) && !(kind & ~UT_HANDLE_KIND_MASK))
_Post_satisfies_((return & UT_HANDLE_KIND_MASK) == kind)
_Must_inspect_result_ UTIL_EXPORT ut_handle_t
ut_handle_create(
_In_ int32_t kind,
_In_ void *arg);
/*
* This will close the handle. All information remains, only new claims will
* fail.
*
* This is a noop on an already closed handle.
*/
UTIL_EXPORT void
ut_handle_close(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link);
/*
* This will remove the handle related information from the server administration
* to free up space.
*
* This is an implicit close().
*
* It will delete the information when there are no more active claims. It'll
* block when necessary to wait for all possible claims to be released.
*/
_Check_return_ UTIL_EXPORT ut_handle_retcode_t
ut_handle_delete(
_In_ ut_handle_t hdl,
_Inout_opt_ _Post_invalid_ struct ut_handlelink *link,
_In_ os_time timeout);
/*
* Returns the status the given handle; valid/deleted/closed/etc.
*
* Returns OK when valid.
*/
_Pre_satisfies_((kind & UT_HANDLE_KIND_MASK) && !(kind & ~UT_HANDLE_KIND_MASK))
_Check_return_ ut_handle_retcode_t
ut_handle_status(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link,
_In_ int32_t kind);
/*
* If the a valid handle is given, which matches the kind and it is not closed,
* then the related arg will be provided and the claims count is increased.
*
* Returns OK when succeeded.
*/
_Pre_satisfies_((kind & UT_HANDLE_KIND_MASK) && !(kind & ~UT_HANDLE_KIND_MASK))
_Check_return_ UTIL_EXPORT ut_handle_retcode_t
ut_handle_claim(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link,
_In_ int32_t kind,
_Out_opt_ void **arg);
/*
* The active claims count is decreased.
*/
UTIL_EXPORT void
ut_handle_release(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link);
/*
* Check if the handle is closed.
*
* This is only useful when you have already claimed a handle and it is
* possible that another thread is trying to delete the handle while you
* were (for instance) sleeping or waiting for something. Now you can
* break of your process and release the handle, making the deletion
* possible.
*/
_Check_return_ UTIL_EXPORT bool
ut_handle_is_closed(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link);
/*
* This will get the link of the handle, which can be used for performance
* increase.
*/
_Must_inspect_result_ UTIL_EXPORT struct ut_handlelink*
ut_handle_get_link(
_In_ ut_handle_t hdl);
#if defined (__cplusplus)
}
#endif
#endif /* UT_HANDLESERVER_H */

View file

@ -0,0 +1,109 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_HOPSCOTCH_H
#define UT_HOPSCOTCH_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
#if __STDC_VERSION__ >= 199901L
#define UT_HH_RESTRICT restrict
#else
#define UT_HH_RESTRICT
#endif
/* Concurrent version */
struct ut_chh;
struct ut_chhBucket;
struct ut_chhIter {
struct ut_chhBucket *bs;
uint32_t size;
uint32_t cursor;
};
/*
* The hopscotch hash table is dependent on a proper functioning hash.
* If the hash function generates a lot of hash collisions, then it will
* not be able to handle that by design.
* It is capable of handling some collisions, but not more than 32 per
* bucket (less, when other hash values are clustered around the
* collision value).
* When proper distributed hash values are generated, then hopscotch
* works nice and quickly.
*/
typedef uint32_t (*ut_hhHash_fn) (const void *);
/*
* Hopscotch needs to be able to compare two elements.
* Returns 0 when not equal.
*/
typedef int (*ut_hhEquals_fn) (const void *, const void *);
/*
* Hopscotch is will resize its internal buckets list when needed. It will
* call this garbage collection function with the old buckets list. The
* caller has to delete the list when it deems it safe to do so.
*/
typedef void (*ut_hhBucketsGc_fn) (void *);
UTIL_EXPORT struct ut_chh *ut_chhNew (uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals, ut_hhBucketsGc_fn gc_buckets);
UTIL_EXPORT void ut_chhFree (struct ut_chh * UT_HH_RESTRICT hh);
UTIL_EXPORT void *ut_chhLookup (struct ut_chh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template);
UTIL_EXPORT int ut_chhAdd (struct ut_chh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT data);
UTIL_EXPORT int ut_chhRemove (struct ut_chh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template);
UTIL_EXPORT void ut_chhEnumUnsafe (struct ut_chh * UT_HH_RESTRICT rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
void *ut_chhIterFirst (struct ut_chh * UT_HH_RESTRICT rt, struct ut_chhIter *it);
void *ut_chhIterNext (struct ut_chhIter *it);
/* Sequential version */
struct ut_hh;
struct ut_hhIter {
struct ut_hh *hh;
uint32_t cursor;
};
UTIL_EXPORT struct ut_hh *ut_hhNew (uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals);
UTIL_EXPORT void ut_hhFree (struct ut_hh * UT_HH_RESTRICT hh);
UTIL_EXPORT void *ut_hhLookup (const struct ut_hh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template);
UTIL_EXPORT int ut_hhAdd (struct ut_hh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT data);
UTIL_EXPORT int ut_hhRemove (struct ut_hh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template);
UTIL_EXPORT void ut_hhEnum (struct ut_hh * UT_HH_RESTRICT rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
UTIL_EXPORT void *ut_hhIterFirst (struct ut_hh * UT_HH_RESTRICT rt, struct ut_hhIter * UT_HH_RESTRICT iter); /* may delete nodes */
UTIL_EXPORT void *ut_hhIterNext (struct ut_hhIter * UT_HH_RESTRICT iter);
/* Sequential version, embedded data */
struct ut_ehh;
struct ut_ehhIter {
struct ut_ehh *hh;
uint32_t cursor;
};
UTIL_EXPORT struct ut_ehh *ut_ehhNew (size_t elemsz, uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals);
UTIL_EXPORT void ut_ehhFree (struct ut_ehh * UT_HH_RESTRICT hh);
UTIL_EXPORT void *ut_ehhLookup (const struct ut_ehh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template);
UTIL_EXPORT int ut_ehhAdd (struct ut_ehh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT data);
UTIL_EXPORT int ut_ehhRemove (struct ut_ehh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template);
UTIL_EXPORT void ut_ehhEnum (struct ut_ehh * UT_HH_RESTRICT rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
UTIL_EXPORT void *ut_ehhIterFirst (struct ut_ehh * UT_HH_RESTRICT rt, struct ut_ehhIter * UT_HH_RESTRICT iter); /* may delete nodes */
UTIL_EXPORT void *ut_ehhIterNext (struct ut_ehhIter * UT_HH_RESTRICT iter);
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -0,0 +1,68 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_THREAD_POOL_H
#define UT_THREAD_POOL_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/* !!!!!!!!NOTE From here no more includes are allowed!!!!!!! */
typedef struct ut_thread_pool_s *ut_thread_pool;
/*
ut_thread_pool_new: Creates a new thread pool. Returns NULL if
cannot create initial set of threads. Threads are created with
the optional atribute argument. Additional threads may be created
on demand up to max_threads.
*/
UTIL_EXPORT ut_thread_pool ut_thread_pool_new
(
uint32_t threads, /* Initial number of threads in pool (can be 0) */
uint32_t max_threads, /* Maximum number of threads in pool (0 == infinite) */
uint32_t max_queue, /* Maximum number of queued requests (0 == infinite) */
os_threadAttr * attr /* Attributes used to create pool threads (can be NULL) */
);
/* ut_thread_pool_free: Frees pool, destroying threads. */
UTIL_EXPORT void ut_thread_pool_free (ut_thread_pool pool);
/* ut_thread_pool_purge: Purge threads from pool back to initial set. */
UTIL_EXPORT void ut_thread_pool_purge (ut_thread_pool pool);
/*
ut_thread_pool_submit: Submit a thread function and associated argument
to be invoked by a thread from the pool. If no threads are available a
new thread will be created on demand to handle the function unless the
pool thread maximum has been reached, in which case the function is queued.
Note that if the pool queue has reached it's maximum os_resultBusy is returned.
*/
UTIL_EXPORT os_result ut_thread_pool_submit
(
ut_thread_pool pool, /* Thread pool instance */
void (*fn) (void *arg), /* Function to be invoked by thread from pool */
void * arg /* Argument passed to invoked function */
);
#if defined (__cplusplus)
}
#endif
#endif /* UT_THREAD_POOL_H */

View file

@ -0,0 +1,49 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef UT_XMLPARSER_H
#define UT_XMLPARSER_H
#include "os/os.h"
#include "util/ut_export.h"
#if defined (__cplusplus)
extern "C" {
#endif
typedef int (*ut_xmlpProcElemOpen_t) (void *varg, uintptr_t parentinfo, uintptr_t *eleminfo, const char *name);
typedef int (*ut_xmlpProcAttr_t) (void *varg, uintptr_t eleminfo, const char *name, const char *value);
typedef int (*ut_xmlpProcElemData_t) (void *varg, uintptr_t eleminfo, const char *data);
typedef int (*ut_xmlpProcElemClose_t) (void *varg, uintptr_t eleminfo);
typedef void (*ut_xmlpError) (void *varg, const char *msg, int line);
struct ut_xmlpCallbacks {
ut_xmlpProcElemOpen_t elem_open;
ut_xmlpProcAttr_t attr;
ut_xmlpProcElemData_t elem_data;
ut_xmlpProcElemClose_t elem_close;
ut_xmlpError error;
};
struct ut_xmlpState;
UTIL_EXPORT struct ut_xmlpState *ut_xmlpNewFile (FILE *fp, void *varg, const struct ut_xmlpCallbacks *cb);
UTIL_EXPORT struct ut_xmlpState *ut_xmlpNewString (const char *string, void *varg, const struct ut_xmlpCallbacks *cb);
UTIL_EXPORT void ut_xmlpFree (struct ut_xmlpState *st);
UTIL_EXPORT int ut_xmlpParse (struct ut_xmlpState *st);
UTIL_EXPORT int ut_xmlUnescapeInsitu (char *buffer, size_t *n);
#if defined (__cplusplus)
}
#endif
#endif

1144
src/util/src/ut_avl.c Normal file

File diff suppressed because it is too large Load diff

75
src/util/src/ut_crc.c Normal file
View file

@ -0,0 +1,75 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "util/ut_crc.h"
#include "os/os.h"
static const uint32_t crc32_table[] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
uint32_t ut_crcCalculate (const void *buf, size_t length)
{
const unsigned char *vptr = buf;
size_t i;
uint32_t reg = 0;
unsigned char top;
for (i = 0; i < length; i++)
{
top = (unsigned char) (reg >> 24);
top ^= *vptr;
reg = (reg << 8) ^ crc32_table[top];
vptr++;
}
return reg;
}

View file

@ -0,0 +1,228 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "os/os.h"
#include "util/ut_expand_envvars.h"
typedef char * (*expand_fn)(const char *src0);
static void expand_append (char **dst, size_t *sz, size_t *pos, char c)
{
if (*pos == *sz) {
*sz += 1024;
*dst = os_realloc (*dst, *sz);
}
(*dst)[*pos] = c;
(*pos)++;
}
static char *expand_env (const char *name, char op, const char *alt, expand_fn expand)
{
const char *env = os_getenv (name);
switch (op)
{
case 0:
return os_strdup (env ? env : "");
case '-':
return env && *env ? os_strdup (env) : expand (alt);
case '?':
if (env && *env) {
return os_strdup (env);
} else {
char *altx = expand (alt);
OS_ERROR("configuration parser", 0, "%s: %s\n", name, altx);
os_free (altx);
return NULL;
}
case '+':
return env && *env ? expand (alt) : os_strdup ("");
default:
abort ();
return NULL;
}
}
static char *expand_envbrace (const char **src, expand_fn expand)
{
const char *start = *src + 1;
char *name, *x;
assert (**src == '{');
(*src)++;
while (**src && **src != ':' && **src != '}') {
(*src)++;
}
if (**src == 0) {
goto err;
}
name = os_malloc ((size_t) (*src - start) + 1);
memcpy (name, start, (size_t) (*src - start));
name[*src - start] = 0;
if (**src == '}') {
(*src)++;
x = expand_env (name, 0, NULL, expand);
os_free (name);
return x;
} else {
const char *altstart;
char *alt;
char op;
int nest = 0;
assert (**src == ':');
(*src)++;
switch (**src) {
case '-': case '+': case '?':
op = **src;
(*src)++;
break;
default:
os_free(name);
goto err;
}
altstart = *src;
while (**src && (**src != '}' || nest > 0)) {
if (**src == '{') {
nest++;
} else if (**src == '}') {
assert (nest > 0);
nest--;
} else if (**src == '\\') {
(*src)++;
if (**src == 0) {
os_free(name);
goto err;
}
}
(*src)++;
}
if (**src == 0) {
os_free(name);
goto err;
}
assert (**src == '}');
alt = os_malloc ((size_t) (*src - altstart) + 1);
memcpy (alt, altstart, (size_t) (*src - altstart));
alt[*src - altstart] = 0;
(*src)++;
x = expand_env (name, op, alt, expand);
os_free (alt);
os_free (name);
return x;
}
err:
OS_ERROR("configuration parser", 0, "%*.*s: invalid expansion\n", (int) (*src - start), (int) (*src - start), start);
return NULL;
}
static char *expand_envsimple (const char **src, expand_fn expand)
{
const char *start = *src;
char *name, *x;
while (**src && (isalnum ((unsigned char) **src) || **src == '_')) {
(*src)++;
}
assert (*src > start);
name = os_malloc ((size_t) (*src - start) + 1);
memcpy (name, start, (size_t) (*src - start));
name[*src - start] = 0;
x = expand_env (name, 0, NULL, expand);
os_free (name);
return x;
}
static char *expand_envchar (const char **src, expand_fn expand)
{
char name[2];
assert (**src);
name[0] = **src;
name[1] = 0;
(*src)++;
return expand_env (name, 0, NULL, expand);
}
char *ut_expand_envvars_sh (const char *src0)
{
/* Expands $X, ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms; $ and \ can be escaped with \ */
const char *src = src0;
size_t sz = strlen (src) + 1, pos = 0;
char *dst = os_malloc (sz);
while (*src) {
if (*src == '\\') {
src++;
if (*src == 0) {
OS_ERROR("configuration parser", 0, "%s: incomplete escape at end of string\n", src0);
os_free(dst);
return NULL;
}
expand_append (&dst, &sz, &pos, *src++);
} else if (*src == '$') {
char *x, *xp;
src++;
if (*src == 0) {
OS_ERROR("configuration parser", 0, "%s: incomplete variable expansion at end of string\n", src0);
os_free(dst);
return NULL;
} else if (*src == '{') {
x = expand_envbrace (&src, &ut_expand_envvars_sh);
} else if (isalnum ((unsigned char) *src) || *src == '_') {
x = expand_envsimple (&src, &ut_expand_envvars_sh);
} else {
x = expand_envchar (&src, &ut_expand_envvars_sh);
}
if (x == NULL) {
os_free(dst);
return NULL;
}
xp = x;
while (*xp) {
expand_append (&dst, &sz, &pos, *xp++);
}
os_free (x);
} else {
expand_append (&dst, &sz, &pos, *src++);
}
}
expand_append (&dst, &sz, &pos, 0);
return dst;
}
char *ut_expand_envvars (const char *src0)
{
/* Expands ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, but not $X */
const char *src = src0;
size_t sz = strlen (src) + 1, pos = 0;
char *dst = os_malloc (sz);
while (*src) {
if (*src == '$' && *(src + 1) == '{') {
char *x, *xp;
src++;
x = expand_envbrace (&src, &ut_expand_envvars);
if (x == NULL) {
os_free(dst);
return NULL;
}
xp = x;
while (*xp) {
expand_append (&dst, &sz, &pos, *xp++);
}
os_free (x);
} else {
expand_append (&dst, &sz, &pos, *src++);
}
}
expand_append (&dst, &sz, &pos, 0);
return dst;
}

303
src/util/src/ut_fibheap.c Normal file
View file

@ -0,0 +1,303 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stddef.h>
#include <limits.h>
#include <assert.h>
#include "util/ut_fibheap.h"
/* max degree: n >= F_{d+2} >= \phi^d ==> d <= log_\phi n, where \phi
(as usual) is the golden ratio ~= 1.618. We know n <= (size of
address space) / sizeof (fh_node), log_\phi 2 ~= 1.44, sizeof
(fh_node) >= 4, therefore max degree < log_2 (size of address
space). */
#define MAX_DEGREE ((unsigned) (sizeof (void *) * CHAR_BIT - 1))
static int cmp (const ut_fibheapDef_t *fhdef, const ut_fibheapNode_t *a, const ut_fibheapNode_t *b)
{
return fhdef->cmp ((const char *) a - fhdef->offset, (const char *) b - fhdef->offset);
}
void ut_fibheapDefInit (ut_fibheapDef_t *fhdef, uintptr_t offset, int (*cmp) (const void *va, const void *vb))
{
fhdef->offset = offset;
fhdef->cmp = cmp;
}
void ut_fibheapInit (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh)
{
OS_UNUSED_ARG(fhdef);
fh->roots = NULL;
}
void *ut_fibheapMin (const ut_fibheapDef_t *fhdef, const ut_fibheap_t *fh)
{
if (fh->roots) {
return (void *) ((char *) fh->roots - fhdef->offset);
} else {
return NULL;
}
}
static void ut_fibheap_merge_nonempty_list (ut_fibheapNode_t **markptr, ut_fibheapNode_t *list)
{
assert (list != NULL);
if (*markptr == NULL) {
*markptr = list;
} else {
ut_fibheapNode_t * const mark = *markptr;
ut_fibheapNode_t * const old_mark_next = mark->next;
ut_fibheapNode_t * const old_list_prev = list->prev;
mark->next = list;
old_mark_next->prev = old_list_prev;
list->prev = mark;
old_list_prev->next = old_mark_next;
}
}
static void ut_fibheap_merge_into (const ut_fibheapDef_t *fhdef, ut_fibheap_t *a, ut_fibheapNode_t * const br)
{
if (br == NULL) {
return;
} else if (a->roots == NULL) {
a->roots = br;
} else {
const int c = cmp (fhdef, br, a->roots);
ut_fibheap_merge_nonempty_list (&a->roots, br);
if (c < 0)
a->roots = br;
}
}
void ut_fibheapMerge (const ut_fibheapDef_t *fhdef, ut_fibheap_t *a, ut_fibheap_t *b)
{
/* merges nodes from b into a, thereafter, b is empty */
ut_fibheap_merge_into (fhdef, a, b->roots);
b->roots = NULL;
}
void ut_fibheapInsert (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh, const void *vnode)
{
/* fibheap node is opaque => nothing in node changes as far as
caller is concerned => declare as const argument, then drop the
const qualifier */
ut_fibheapNode_t *node = (ut_fibheapNode_t *) ((char *) vnode + fhdef->offset);
/* new heap of degree 0 (i.e., only containing NODE) */
node->parent = node->children = NULL;
node->prev = node->next = node;
node->mark = 0;
node->degree = 0;
/* then merge it in */
ut_fibheap_merge_into (fhdef, fh, node);
}
static void ut_fibheap_add_as_child (ut_fibheapNode_t *parent, ut_fibheapNode_t *child)
{
parent->degree++;
child->parent = parent;
child->prev = child->next = child;
ut_fibheap_merge_nonempty_list (&parent->children, child);
}
static void ut_fibheap_delete_one_from_list (ut_fibheapNode_t **markptr, ut_fibheapNode_t *node)
{
if (node->next == node) {
*markptr = NULL;
} else {
ut_fibheapNode_t * const node_prev = node->prev;
ut_fibheapNode_t * const node_next = node->next;
node_prev->next = node_next;
node_next->prev = node_prev;
if (*markptr == node) {
*markptr = node_next;
}
}
}
void *ut_fibheapExtractMin (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh)
{
ut_fibheapNode_t *roots[MAX_DEGREE + 1];
ut_fibheapNode_t * const min = fh->roots;
unsigned min_degree_noninit = 0;
/* empty heap => return that, alternative would be to require the
heap to contain at least one element, but this is probably nicer
in practice */
if (min == NULL) {
return NULL;
}
/* singleton heap => no work remaining */
if (min->next == min && min->children == NULL) {
fh->roots = NULL;
return (void *) ((char *) min - fhdef->offset);
}
/* remove min from fh->roots */
ut_fibheap_delete_one_from_list (&fh->roots, min);
/* FIXME: can speed up by combining a few things & improving
locality of reference by scanning lists only once */
/* insert min'schildren as new roots -- must fix parent pointers,
and reset marks because roots are always unmarked */
if (min->children) {
ut_fibheapNode_t * const mark = min->children;
ut_fibheapNode_t *n = mark;
do {
n->parent = NULL;
n->mark = 0;
n = n->next;
} while (n != mark);
ut_fibheap_merge_nonempty_list (&fh->roots, min->children);
}
/* iteratively merge roots of equal degree, completely messing up
fh->roots, ... */
{
ut_fibheapNode_t *const mark = fh->roots;
ut_fibheapNode_t *n = mark;
do {
ut_fibheapNode_t * const n1 = n->next;
/* if n is first root with this high a degree, there's certainly
not going to be another root to merge with yet */
while (n->degree < min_degree_noninit && roots[n->degree]) {
unsigned const degree = n->degree;
ut_fibheapNode_t *u, *v;
if (cmp (fhdef, roots[degree], n) < 0) {
u = roots[degree]; v = n;
} else {
u = n; v = roots[degree];
}
roots[degree] = NULL;
ut_fibheap_add_as_child (u, v);
n = u;
}
/* n may have changed, hence need to retest whether or not
enough of roots has been initialised -- note that
initialising roots[n->degree] is unnecessary, but easier */
assert (n->degree <= MAX_DEGREE);
while (min_degree_noninit <= n->degree) {
roots[min_degree_noninit++] = NULL;
}
roots[n->degree] = n;
n = n1;
} while (n != mark);
}
/* ... but we don't mind because we have roots[], we can scan linear
memory at an astonishing rate, and we need to compare the root
keys anyway to find the minimum */
{
ut_fibheapNode_t *mark, *cursor, *newmin;
unsigned i;
for (i = 0; roots[i] == NULL; i++) {
assert (i+1 < min_degree_noninit);
}
newmin = roots[i];
assert (newmin != NULL);
mark = cursor = roots[i];
for (++i; i < min_degree_noninit; i++) {
if (roots[i]) {
ut_fibheapNode_t * const r = roots[i];
if (cmp (fhdef, r, newmin) < 0)
newmin = r;
r->prev = cursor;
cursor->next = r;
cursor = r;
}
}
mark->prev = cursor;
cursor->next = mark;
fh->roots = newmin;
}
return (void *) ((char *) min - fhdef->offset);
}
static void ut_fibheap_cutnode (ut_fibheap_t *fh, ut_fibheapNode_t *node)
{
/* by marking the node, we ensure it gets cut */
node->mark = 1;
/* traverse towards the root, cutting marked nodes on the way */
while (node->parent && node->mark) {
ut_fibheapNode_t *parent = node->parent;
assert (parent->degree > 0);
ut_fibheap_delete_one_from_list (&parent->children, node);
parent->degree--;
node->mark = 0;
node->parent = NULL;
node->next = node->prev = node;
/* we assume heap properties haven't been violated, and therefore
none of the nodes we cut can become the new minimum */
ut_fibheap_merge_nonempty_list (&fh->roots, node);
node = parent;
}
/* if we stopped because we hit an unmarked interior node, we must
mark it */
if (node->parent) {
node->mark = 1;
}
}
void ut_fibheapDecreaseKey (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh, const void *vnode)
{
/* fibheap node is opaque => nothing in node changes as far as
caller is concerned => declare as const argument, then drop the
const qualifier */
ut_fibheapNode_t *node = (ut_fibheapNode_t *) ((char *) vnode + fhdef->offset);
if (node->parent && cmp (fhdef, node->parent, node) <= 0) {
/* heap property not violated, do nothing */
} else {
if (node->parent) {
/* heap property violated by decreasing the key, but we cut it
pretending nothing has happened yet, then fix up the minimum if
this node is the new minimum */
ut_fibheap_cutnode (fh, node);
}
if (cmp (fhdef, node, fh->roots) < 0) {
fh->roots = node;
}
}
}
void ut_fibheapDelete (const ut_fibheapDef_t *fhdef, ut_fibheap_t *fh, const void *vnode)
{
/* fibheap node is opaque => nothing in node changes as far as
caller is concerned => declare as const argument, then drop the
const qualifier */
ut_fibheapNode_t *node = (ut_fibheapNode_t *) ((char *) vnode + fhdef->offset);
/* essentially decreasekey(node);extractmin while pretending the
node key is -infinity. That means we can't directly call
decreasekey, because it considers the actual value of the key. */
if (node->parent != NULL) {
ut_fibheap_cutnode (fh, node);
}
fh->roots = node;
(void) ut_fibheapExtractMin (fhdef, fh);
}

View file

@ -0,0 +1,400 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <string.h>
#include <assert.h>
#include "os/os.h"
#include "util/ut_handleserver.h"
/* Arbitrarily number of max handles. Should be enough for the mock. */
#define MAX_NR_OF_HANDLES (1000)
#define HDL_FLAG_NONE (0x00)
#define HDL_FLAG_CLOSED (0x01)
typedef struct ut_handlelink {
ut_handle_t hdl;
void *arg;
uint32_t cnt;
uint8_t flags;
} ut_handlelink;
typedef struct ut_handleserver {
ut_handlelink *hdls[MAX_NR_OF_HANDLES];
int32_t last;
os_mutex mutex;
} ut_handleserver;
/* Singleton handle server. */
static ut_handleserver *hs = NULL;
_Check_return_ static ut_handle_retcode_t
lookup_handle(_In_ ut_handle_t hdl, _In_ int32_t kind, _Out_ ut_handlelink **link);
_Check_return_ static ut_handle_t
check_handle(_In_ ut_handle_t hdl, _In_ int32_t kind);
static void
delete_handle(_In_ int32_t idx);
_Check_return_ ut_handle_retcode_t
ut_handleserver_init(void)
{
os_osInit();
/* TODO CHAM-138: Allow re-entry (something like os_osInit()). */
assert(hs == NULL);
hs = os_malloc(sizeof(ut_handleserver));
hs->last = 0;
os_mutexInit(&hs->mutex);
return UT_HANDLE_OK;
}
void
ut_handleserver_fini(void)
{
int32_t i;
/* TODO CHAM-138: Only destroy when this is the last fini (something like os_osExit()). */
assert(hs);
/* Every handle should have been deleted, but make sure. */
for (i = 0; i < hs->last; i++) {
if (hs->hdls[i] != NULL) {
/* TODO CHAM-138: Print warning. */
os_free(hs->hdls[i]);
}
}
os_mutexDestroy(&hs->mutex);
os_free(hs);
hs = NULL;
os_osExit();
}
_Pre_satisfies_((kind & UT_HANDLE_KIND_MASK) && !(kind & ~UT_HANDLE_KIND_MASK))
_Post_satisfies_((return & UT_HANDLE_KIND_MASK) == kind)
_Must_inspect_result_ ut_handle_t
ut_handle_create(
_In_ int32_t kind,
_In_ void *arg)
{
ut_handle_t hdl = (ut_handle_t)UT_HANDLE_OUT_OF_RESOURCES;
/* A kind is obligatory. */
assert(kind & UT_HANDLE_KIND_MASK);
/* The kind should extent outside its boundaries. */
assert(!(kind & ~UT_HANDLE_KIND_MASK));
if (hs == NULL) {
return (ut_handle_t)UT_HANDLE_NOT_INITALIZED;
}
os_mutexLock(&hs->mutex);
/* TODO CHAM-138: Improve the creation and management of handles. */
if (hs->last < MAX_NR_OF_HANDLES) {
hdl = hs->last;
hdl |= kind;
hs->hdls[hs->last] = os_malloc(sizeof(ut_handlelink));
hs->hdls[hs->last]->cnt = 0;
hs->hdls[hs->last]->arg = arg;
hs->hdls[hs->last]->hdl = hdl;
hs->hdls[hs->last]->flags = HDL_FLAG_NONE;
hs->last++;
}
os_mutexUnlock(&hs->mutex);
return hdl;
}
void
ut_handle_close(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link)
{
struct ut_handlelink *info = link;
ut_handle_retcode_t ret = UT_HANDLE_OK;
assert(hs);
os_mutexLock(&hs->mutex);
if (info == NULL) {
ret = lookup_handle(hdl, UT_HANDLE_DONTCARE_KIND, &info);
}
if (ret == UT_HANDLE_OK) {
assert(info);
assert(hdl == info->hdl);
info->flags |= HDL_FLAG_CLOSED;
}
os_mutexUnlock(&hs->mutex);
}
_Check_return_ ut_handle_retcode_t
ut_handle_delete(
_In_ ut_handle_t hdl,
_Inout_opt_ _Post_invalid_ struct ut_handlelink *link,
_In_ os_time timeout)
{
struct ut_handlelink *info = link;
ut_handle_retcode_t ret = UT_HANDLE_OK;
assert(hs);
os_mutexLock(&hs->mutex);
if (info == NULL) {
ret = lookup_handle(hdl, UT_HANDLE_DONTCARE_KIND, &info);
}
if (ret == UT_HANDLE_OK) {
assert(info);
assert(hdl == info->hdl);
info->flags |= HDL_FLAG_CLOSED;
/* TODO CHAM-138: Replace this polling with conditional wait. */
os_mutexUnlock(&hs->mutex);
{
const os_time zero = { 0, 0 };
const os_time delay = { 0, 10000000 };
while ((info->cnt != 0) && (os_timeCompare(timeout, zero) > 0)) {
os_nanoSleep(delay);
timeout = os_timeSub(timeout, delay);
}
}
os_mutexLock(&hs->mutex);
if (info->cnt == 0) {
delete_handle(hdl & UT_HANDLE_IDX_MASK);
} else {
ret = UT_HANDLE_TIMEOUT;
}
}
os_mutexUnlock(&hs->mutex);
return ret;
}
_Pre_satisfies_((kind & UT_HANDLE_KIND_MASK) && !(kind & ~UT_HANDLE_KIND_MASK))
_Check_return_ ut_handle_retcode_t
ut_handle_status(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link,
_In_ int32_t kind)
{
struct ut_handlelink *info = link;
ut_handle_retcode_t ret = UT_HANDLE_OK;
if (hs == NULL) {
return (ut_handle_t)UT_HANDLE_INVALID;
}
os_mutexLock(&hs->mutex);
if (info == NULL) {
ret = lookup_handle(hdl, kind, &info);
}
if (ret == UT_HANDLE_OK) {
assert(info);
assert(hdl == info->hdl);
if (info->flags & HDL_FLAG_CLOSED) {
ret = UT_HANDLE_CLOSED;
}
}
os_mutexUnlock(&hs->mutex);
return ret;
}
_Pre_satisfies_((kind & UT_HANDLE_KIND_MASK) && !(kind & ~UT_HANDLE_KIND_MASK))
_Check_return_ ut_handle_retcode_t
ut_handle_claim(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link,
_In_ int32_t kind,
_Out_opt_ void **arg)
{
struct ut_handlelink *info = link;
ut_handle_retcode_t ret = UT_HANDLE_OK;
if (arg != NULL) {
*arg = NULL;
}
if (hs == NULL) {
return (ut_handle_t)UT_HANDLE_INVALID;
}
os_mutexLock(&hs->mutex);
if (info == NULL) {
ret = lookup_handle(hdl, kind, &info);
}
if (ret == UT_HANDLE_OK) {
assert(info);
assert(hdl == info->hdl);
if (info->flags & HDL_FLAG_CLOSED) {
ret = UT_HANDLE_CLOSED;
}
}
if (ret == UT_HANDLE_OK) {
info->cnt++;
if (arg != NULL) {
*arg = info->arg;
}
}
os_mutexUnlock(&hs->mutex);
return ret;
}
void
ut_handle_release(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link)
{
struct ut_handlelink *info = link;
ut_handle_retcode_t ret = UT_HANDLE_OK;
assert(hs);
os_mutexLock(&hs->mutex);
if (info == NULL) {
ret = lookup_handle(hdl, UT_HANDLE_DONTCARE_KIND, &info);
}
if (ret == UT_HANDLE_OK) {
assert(info);
assert(hdl == info->hdl);
assert(info->cnt > 0);
info->cnt--;
}
os_mutexUnlock(&hs->mutex);
}
_Check_return_ bool
ut_handle_is_closed(
_In_ ut_handle_t hdl,
_Inout_opt_ struct ut_handlelink *link)
{
struct ut_handlelink *info = link;
ut_handle_retcode_t ret = UT_HANDLE_OK;
assert(hs);
os_mutexLock(&hs->mutex);
if (info == NULL) {
ret = lookup_handle(hdl, UT_HANDLE_DONTCARE_KIND, &info);
}
if (ret == UT_HANDLE_OK) {
assert(info);
assert(hdl == info->hdl);
if (info->flags & HDL_FLAG_CLOSED) {
ret = UT_HANDLE_CLOSED;
}
}
os_mutexUnlock(&hs->mutex);
/* Simulate closed for every error. */
return (ret != UT_HANDLE_OK);
}
_Must_inspect_result_ struct ut_handlelink*
ut_handle_get_link(
_In_ ut_handle_t hdl)
{
struct ut_handlelink *info;
ut_handle_retcode_t ret;
assert(hs);
os_mutexLock(&hs->mutex);
ret = lookup_handle(hdl, UT_HANDLE_DONTCARE_KIND, &info);
assert(((ret == UT_HANDLE_OK) && (info != NULL)) ||
((ret != UT_HANDLE_OK) && (info == NULL)) );
os_mutexUnlock(&hs->mutex);
return info;
}
_Check_return_ static ut_handle_retcode_t
lookup_handle(
_In_ ut_handle_t hdl,
_In_ int32_t kind,
_Out_ ut_handlelink **link)
{
ut_handle_retcode_t ret;
*link = NULL;
ret = check_handle(hdl, kind);
if (ret == UT_HANDLE_OK) {
int32_t idx = (hdl & UT_HANDLE_IDX_MASK);
assert(idx < MAX_NR_OF_HANDLES);
*link = hs->hdls[idx];
}
return ret;
}
_Check_return_ static ut_handle_t
check_handle(
_In_ ut_handle_t hdl,
_In_ int32_t kind)
{
/* When handle is negative, it contains a retcode. */
ut_handle_retcode_t ret = UT_HANDLE_OK;
if (hdl > 0) {
if (hdl & UT_HANDLE_KIND_MASK) {
int32_t idx = (hdl & UT_HANDLE_IDX_MASK);
if (idx < hs->last) {
assert(idx < MAX_NR_OF_HANDLES);
ut_handlelink *info = hs->hdls[idx];
if (info != NULL) {
if ((info->hdl & UT_HANDLE_KIND_MASK) == (hdl & UT_HANDLE_KIND_MASK)) {
if ((kind != UT_HANDLE_DONTCARE_KIND) &&
(kind != (hdl & UT_HANDLE_KIND_MASK))) {
/* It's a valid handle, but the caller expected a different kind. */
ret = UT_HANDLE_UNEQUAL_KIND;
}
} else {
ret = UT_HANDLE_UNEQUAL_KIND;
}
} else {
ret = UT_HANDLE_DELETED;
}
} else {
ret = UT_HANDLE_INVALID;
}
} else {
ret = UT_HANDLE_INVALID;
}
} else if (hdl == 0) {
ret = UT_HANDLE_INVALID;
} else {
/* When handle is negative, it contains a retcode. */
ret = (ut_handle_retcode_t)hdl;
}
return ret;
}
static void
delete_handle(_In_ int32_t idx)
{
assert(hs);
assert(idx < MAX_NR_OF_HANDLES);
os_free(hs->hdls[idx]);
hs->hdls[idx] = NULL;
}

951
src/util/src/ut_hopscotch.c Normal file
View file

@ -0,0 +1,951 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include "os/os.h"
#include "util/ut_hopscotch.h"
#define HH_HOP_RANGE 32
#define HH_ADD_RANGE 64
#define NOT_A_BUCKET (~(uint32_t)0)
/********** CONCURRENT VERSION ************/
#define N_BACKING_LOCKS 32
#define N_RESIZE_LOCKS 8
struct ut_chhBucket {
os_atomic_uint32_t hopinfo;
os_atomic_uint32_t timestamp;
os_atomic_uint32_t lock;
os_atomic_voidp_t data;
};
struct _Struct_size_bytes_(size) ut_chhBucketArray {
uint32_t size; /* power of 2 */
#if __STDC_VERSION__ >= 199901L
struct ut_chhBucket bs[];
#else
struct ut_chhBucket bs[1];
#endif
};
struct ut_chhBackingLock {
os_mutex lock;
os_cond cv;
};
struct ut_chh {
os_atomic_voidp_t buckets; /* struct ut_chhBucketArray * */
struct ut_chhBackingLock backingLocks[N_BACKING_LOCKS];
ut_hhHash_fn hash;
ut_hhEquals_fn equals;
os_rwlock resize_locks[N_RESIZE_LOCKS];
ut_hhBucketsGc_fn gc_buckets;
};
#define CHH_MAX_TRIES 4
#define CHH_BUSY ((void *) 1)
static int ut_chhDataValid_p (void *data)
{
return data != NULL && data != CHH_BUSY;
}
static int ut_chhInit (struct ut_chh *rt, uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals, ut_hhBucketsGc_fn gc_buckets)
{
uint32_t size;
uint32_t i;
struct ut_chhBucketArray *buckets;
size = HH_HOP_RANGE;
while (size < init_size) {
size *= 2;
}
rt->hash = hash;
rt->equals = equals;
rt->gc_buckets = gc_buckets;
_Analysis_assume_(size >= HH_HOP_RANGE);
buckets = os_malloc (offsetof (struct ut_chhBucketArray, bs) + size * sizeof (*buckets->bs));
os_atomic_stvoidp (&rt->buckets, buckets);
buckets->size = size;
for (i = 0; i < size; i++) {
struct ut_chhBucket *b = &buckets->bs[i];
os_atomic_st32 (&b->hopinfo, 0);
os_atomic_st32 (&b->timestamp, 0);
os_atomic_st32 (&b->lock, 0);
os_atomic_stvoidp (&b->data, NULL);
}
for (i = 0; i < N_BACKING_LOCKS; i++) {
struct ut_chhBackingLock *s = &rt->backingLocks[i];
os_mutexInit (&s->lock);
}
for (i = 0; i < N_BACKING_LOCKS; i++) {
struct ut_chhBackingLock *s = &rt->backingLocks[i];
os_condInit (&s->cv, &s->lock);
}
for (i = 0; i < N_RESIZE_LOCKS; i++) {
os_rwlockInit (&rt->resize_locks[i]);
}
return 0;
}
static void ut_chhFini (struct ut_chh *rt)
{
int i;
os_free (os_atomic_ldvoidp (&rt->buckets));
for (i = 0; i < N_BACKING_LOCKS; i++) {
struct ut_chhBackingLock *s = &rt->backingLocks[i];
os_condDestroy (&s->cv);
os_mutexDestroy (&s->lock);
}
for (i = 0; i < N_RESIZE_LOCKS; i++) {
os_rwlockDestroy (&rt->resize_locks[i]);
}
}
struct ut_chh *ut_chhNew (uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals, ut_hhBucketsGc_fn gc_buckets)
{
struct ut_chh *hh = os_malloc (sizeof (*hh));
if (ut_chhInit (hh, init_size, hash, equals, gc_buckets) < 0) {
os_free (hh);
return NULL;
} else {
return hh;
}
}
void ut_chhFree (struct ut_chh * UT_HH_RESTRICT hh)
{
ut_chhFini (hh);
os_free (hh);
}
#define LOCKBIT ((uint32_t)1 << 31)
static void ut_chhLockBucket (struct ut_chh *rt, uint32_t bidx)
{
/* Lock: MSB <=> LOCKBIT, LSBs <=> wait count; note that
(o&LOCKBIT)==0 means a thread can sneak in when there are
already waiters, changing it to o==0 would avoid that. */
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const b = &bsary->bs[bidx];
struct ut_chhBackingLock * const s = &rt->backingLocks[bidx % N_BACKING_LOCKS];
uint32_t o, n;
fastpath_retry:
o = os_atomic_ld32 (&b->lock);
if ((o & LOCKBIT) == 0) {
n = o | LOCKBIT;
} else {
n = o + 1;
}
if (!os_atomic_cas32 (&b->lock, o, n)) {
goto fastpath_retry;
}
if ((o & LOCKBIT) == 0) {
os_atomic_fence ();
} else {
os_mutexLock (&s->lock);
do {
while ((o = os_atomic_ld32 (&b->lock)) & LOCKBIT) {
os_condWait (&s->cv, &s->lock);
}
} while (!os_atomic_cas32 (&b->lock, o, (o - 1) | LOCKBIT));
os_mutexUnlock (&s->lock);
}
}
static void ut_chhUnlockBucket (struct ut_chh *rt, uint32_t bidx)
{
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const b = &bsary->bs[bidx];
struct ut_chhBackingLock * const s = &rt->backingLocks[bidx % N_BACKING_LOCKS];
uint32_t o, n;
retry:
o = os_atomic_ld32 (&b->lock);
assert (o & LOCKBIT);
n = o & ~LOCKBIT;
if (!os_atomic_cas32 (&b->lock, o, n)) {
goto retry;
}
if (n == 0) {
os_atomic_fence ();
} else {
os_mutexLock (&s->lock);
/* Need to broadcast because the CV is shared by multiple buckets
and the kernel wakes an arbitrary thread, it may be a thread
waiting for another bucket's lock that gets woken up, and that
can result in all threads waiting with all locks unlocked.
Broadcast avoids that, and with significantly more CVs than
cores, it shouldn't happen often. */
os_condBroadcast (&s->cv);
os_mutexUnlock (&s->lock);
}
}
static void *ut_chhLookupInternal (struct ut_chhBucketArray const * const bsary, ut_hhEquals_fn equals, const uint32_t bucket, const void *template)
{
struct ut_chhBucket const * const bs = bsary->bs;
const uint32_t idxmask = bsary->size - 1;
uint32_t timestamp;
int try_counter = 0;
uint32_t idx;
do {
uint32_t hopinfo;
timestamp = os_atomic_ld32 (&bs[bucket].timestamp);
os_atomic_fence ();
hopinfo = os_atomic_ld32 (&bs[bucket].hopinfo);
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
const uint32_t bidx = (bucket + idx) & idxmask;
void *data = os_atomic_ldvoidp (&bs[bidx].data);
if (ut_chhDataValid_p (data) && equals (data, template)) {
return data;
}
}
os_atomic_fence ();
} while (timestamp != os_atomic_ld32 (&bs[bucket].timestamp) && ++try_counter < CHH_MAX_TRIES);
if (try_counter == CHH_MAX_TRIES) {
/* Note: try_counter would not have been incremented to
CHH_MAX_TRIES if we ended the loop because the two timestamps
were equal, but this avoids loading the timestamp again */
for (idx = 0; idx < HH_HOP_RANGE; idx++) {
const uint32_t bidx = (bucket + idx) & idxmask;
void *data = os_atomic_ldvoidp (&bs[bidx].data);
if (ut_chhDataValid_p (data) && equals (data, template)) {
return data;
}
}
}
return NULL;
}
#define os_atomic_rmw32_nonatomic(var_, tmp_, expr_) do { \
os_atomic_uint32_t *var__ = (var_); \
uint32_t tmp_ = os_atomic_ld32 (var__); \
os_atomic_st32 (var__, (expr_)); \
} while (0)
void *ut_chhLookup (struct ut_chh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template)
{
struct ut_chhBucketArray const * const bsary = os_atomic_ldvoidp (&rt->buckets);
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = bsary->size - 1;
const uint32_t bucket = hash & idxmask;
return ut_chhLookupInternal (bsary, rt->equals, bucket, template);
}
static uint32_t ut_chhFindCloserFreeBucket (struct ut_chh *rt, uint32_t free_bucket, uint32_t *free_distance)
{
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const bs = bsary->bs;
const uint32_t idxmask = bsary->size - 1;
uint32_t move_bucket, free_dist;
move_bucket = (free_bucket - (HH_HOP_RANGE - 1)) & idxmask;
for (free_dist = HH_HOP_RANGE - 1; free_dist > 0; free_dist--) {
uint32_t start_hop_info = os_atomic_ld32 (&bs[move_bucket].hopinfo);
uint32_t move_free_distance = NOT_A_BUCKET;
uint32_t mask = 1;
uint32_t i;
for (i = 0; i < free_dist; i++, mask <<= 1) {
if (mask & start_hop_info) {
move_free_distance = i;
break;
}
}
if (move_free_distance != NOT_A_BUCKET) {
ut_chhLockBucket (rt, move_bucket);
if (start_hop_info == os_atomic_ld32 (&bs[move_bucket].hopinfo))
{
uint32_t new_free_bucket = (move_bucket + move_free_distance) & idxmask;
os_atomic_rmw32_nonatomic (&bs[move_bucket].hopinfo, x, x | (1u << free_dist));
os_atomic_stvoidp (&bs[free_bucket].data, os_atomic_ldvoidp (&bs[new_free_bucket].data));
os_atomic_rmw32_nonatomic (&bs[move_bucket].timestamp, x, x + 1);
os_atomic_fence ();
os_atomic_stvoidp (&bs[new_free_bucket].data, CHH_BUSY);
os_atomic_rmw32_nonatomic (&bs[move_bucket].hopinfo, x, x & ~(1u << move_free_distance));
*free_distance -= free_dist - move_free_distance;
ut_chhUnlockBucket (rt, move_bucket);
return new_free_bucket;
}
ut_chhUnlockBucket (rt, move_bucket);
}
move_bucket = (move_bucket + 1) & idxmask;
}
return NOT_A_BUCKET;
}
static void ut_chhResize (struct ut_chh *rt)
{
/* doubles the size => bucket index gains one bit at the msb =>
start bucket is unchanged or moved into the added half of the set
=> those for which the (new) msb is 0 are guaranteed to fit, and
so are those for which the (new) msb is 1 => never have to resize
recursively */
struct ut_chhBucketArray * const bsary0 = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const bs0 = bsary0->bs;
struct ut_chhBucketArray *bsary1;
struct ut_chhBucket *bs1;
uint32_t i, idxmask0, idxmask1;
assert (bsary0->size > 0);
bsary1 = os_malloc (offsetof (struct ut_chhBucketArray, bs) + 2 * bsary0->size * sizeof (*bsary1->bs));
bsary1->size = 2 * bsary0->size;
bs1 = bsary1->bs;
for (i = 0; i < bsary1->size; i++) {
os_atomic_st32 (&bs1[i].hopinfo, 0);
os_atomic_st32 (&bs1[i].timestamp, 0);
os_atomic_st32 (&bs1[i].lock, 0);
os_atomic_stvoidp (&bs1[i].data, NULL);
}
idxmask0 = bsary0->size - 1;
idxmask1 = bsary1->size - 1;
for (i = 0; i < bsary0->size; i++) {
void *data = os_atomic_ldvoidp (&bs0[i].data);
if (data && data != CHH_BUSY) {
const uint32_t hash = rt->hash (data);
const uint32_t old_start_bucket = hash & idxmask0;
const uint32_t new_start_bucket = hash & idxmask1;
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (bsary0->size + i - old_start_bucket);
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
assert (dist < HH_HOP_RANGE);
os_atomic_rmw32_nonatomic (&bs1[new_start_bucket].hopinfo, x, x | (1u << dist));
os_atomic_stvoidp (&bs1[newb].data, data);
}
}
os_atomic_fence ();
os_atomic_stvoidp (&rt->buckets, bsary1);
rt->gc_buckets (bsary0);
}
int ut_chhAdd (struct ut_chh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT data)
{
const uint32_t hash = rt->hash (data);
uint32_t size;
os_rwlockRead (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
{
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const bs = bsary->bs;
uint32_t idxmask;
uint32_t start_bucket, free_distance, free_bucket;
size = bsary->size;
idxmask = size - 1;
start_bucket = hash & idxmask;
ut_chhLockBucket (rt, start_bucket);
if (ut_chhLookupInternal (bsary, rt->equals, start_bucket, data)) {
ut_chhUnlockBucket (rt, start_bucket);
os_rwlockUnlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 0;
}
free_bucket = start_bucket;
for (free_distance = 0; free_distance < HH_ADD_RANGE; free_distance++) {
if (os_atomic_ldvoidp (&bs[free_bucket].data) == NULL &&
os_atomic_casvoidp (&bs[free_bucket].data, NULL, CHH_BUSY)) {
break;
}
free_bucket = (free_bucket + 1) & idxmask;
}
if (free_distance < HH_ADD_RANGE) {
do {
if (free_distance < HH_HOP_RANGE) {
assert (free_bucket == ((start_bucket + free_distance) & idxmask));
os_atomic_rmw32_nonatomic (&bs[start_bucket].hopinfo, x, x | (1u << free_distance));
os_atomic_fence ();
os_atomic_stvoidp (&bs[free_bucket].data, (void *) data);
ut_chhUnlockBucket (rt, start_bucket);
os_rwlockUnlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 1;
}
free_bucket = ut_chhFindCloserFreeBucket (rt, free_bucket, &free_distance);
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
} while (free_bucket != NOT_A_BUCKET);
}
ut_chhUnlockBucket (rt, start_bucket);
}
os_rwlockUnlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
{
int i;
struct ut_chhBucketArray *bsary1;
for (i = 0; i < N_RESIZE_LOCKS; i++) {
os_rwlockWrite (&rt->resize_locks[i]);
}
/* another thread may have sneaked past and grown the hash table */
bsary1 = os_atomic_ldvoidp (&rt->buckets);
if (bsary1->size == size) {
ut_chhResize (rt);
}
for (i = 0; i < N_RESIZE_LOCKS; i++) {
os_rwlockUnlock (&rt->resize_locks[i]);
}
}
return ut_chhAdd (rt, data);
}
int ut_chhRemove (struct ut_chh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template)
{
const uint32_t hash = rt->hash (template);
os_rwlockRead (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
{
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const bs = bsary->bs;
const uint32_t size = bsary->size;
const uint32_t idxmask = size - 1;
const uint32_t bucket = hash & idxmask;
uint32_t hopinfo;
uint32_t idx;
ut_chhLockBucket (rt, bucket);
hopinfo = os_atomic_ld32 (&bs[bucket].hopinfo);
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
if (hopinfo & 1) {
const uint32_t bidx = (bucket + idx) & idxmask;
void *data = os_atomic_ldvoidp (&bs[bidx].data);
if (ut_chhDataValid_p (data) && rt->equals (data, template)) {
os_atomic_stvoidp (&bs[bidx].data, NULL);
os_atomic_rmw32_nonatomic (&bs[bucket].hopinfo, x, x & ~(1u << idx));
ut_chhUnlockBucket (rt, bucket);
os_rwlockUnlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 1;
}
}
}
ut_chhUnlockBucket (rt, bucket);
}
os_rwlockUnlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 0;
}
void ut_chhEnumUnsafe (struct ut_chh * UT_HH_RESTRICT rt, void (*f) (void *a, void *f_arg), void *f_arg)
{
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
struct ut_chhBucket * const bs = bsary->bs;
uint32_t i;
for (i = 0; i < bsary->size; i++) {
void *data = os_atomic_ldvoidp (&bs[i].data);
if (data && data != CHH_BUSY) {
f (data, f_arg);
}
}
}
void *ut_chhIterNext (struct ut_chhIter *it)
{
uint32_t i;
for (i = it->cursor; i < it->size; i++) {
void *data = os_atomic_ldvoidp (&it->bs[i].data);
if (data && data != CHH_BUSY) {
it->cursor = i+1;
return data;
}
}
return NULL;
}
void *ut_chhIterFirst (struct ut_chh * UT_HH_RESTRICT rt, struct ut_chhIter *it)
{
struct ut_chhBucketArray * const bsary = os_atomic_ldvoidp (&rt->buckets);
it->bs = bsary->bs;
it->size = bsary->size;
it->cursor = 0;
return ut_chhIterNext (it);
}
/************* SEQUENTIAL VERSION ***************/
struct ut_hhBucket {
uint32_t hopinfo;
void *data;
};
struct ut_hh {
uint32_t size; /* power of 2 */
struct ut_hhBucket *buckets;
ut_hhHash_fn hash;
ut_hhEquals_fn equals;
};
static void ut_hhInit (struct ut_hh *rt, uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals)
{
uint32_t size = HH_HOP_RANGE;
uint32_t i;
while (size < init_size) {
size *= 2;
}
rt->hash = hash;
rt->equals = equals;
rt->size = size;
_Analysis_assume_(size >= HH_HOP_RANGE);
rt->buckets = os_malloc (size * sizeof (*rt->buckets));
for (i = 0; i < size; i++) {
rt->buckets[i].hopinfo = 0;
rt->buckets[i].data = NULL;
}
}
static void ut_hhFini (struct ut_hh *rt)
{
os_free (rt->buckets);
}
struct ut_hh *ut_hhNew (uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals)
{
struct ut_hh *hh = os_malloc (sizeof (*hh));
ut_hhInit (hh, init_size, hash, equals);
return hh;
}
void ut_hhFree (struct ut_hh * UT_HH_RESTRICT hh)
{
ut_hhFini (hh);
os_free (hh);
}
static void *ut_hhLookupInternal (const struct ut_hh *rt, const uint32_t bucket, const void *template)
{
const uint32_t idxmask = rt->size - 1;
uint32_t hopinfo = rt->buckets[bucket].hopinfo;
uint32_t idx;
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
const uint32_t bidx = (bucket + idx) & idxmask;
void *data = rt->buckets[bidx].data;
if (data && rt->equals (data, template))
return data;
}
return NULL;
}
void *ut_hhLookup (const struct ut_hh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template)
{
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = rt->size - 1;
const uint32_t bucket = hash & idxmask;
return ut_hhLookupInternal (rt, bucket, template);
}
static uint32_t ut_hhFindCloserFreeBucket (struct ut_hh *rt, uint32_t free_bucket, uint32_t *free_distance)
{
const uint32_t idxmask = rt->size - 1;
uint32_t move_bucket, free_dist;
move_bucket = (free_bucket - (HH_HOP_RANGE - 1)) & idxmask;
for (free_dist = HH_HOP_RANGE - 1; free_dist > 0; free_dist--) {
uint32_t move_free_distance = NOT_A_BUCKET;
uint32_t mask = 1;
uint32_t i;
for (i = 0; i < free_dist; i++, mask <<= 1) {
if (mask & rt->buckets[move_bucket].hopinfo) {
move_free_distance = i;
break;
}
}
if (move_free_distance != NOT_A_BUCKET) {
uint32_t new_free_bucket = (move_bucket + move_free_distance) & idxmask;
rt->buckets[move_bucket].hopinfo |= 1u << free_dist;
rt->buckets[free_bucket].data = rt->buckets[new_free_bucket].data;
rt->buckets[new_free_bucket].data = NULL;
rt->buckets[move_bucket].hopinfo &= ~(1u << move_free_distance);
*free_distance -= free_dist - move_free_distance;
return new_free_bucket;
}
move_bucket = (move_bucket + 1) & idxmask;
}
return NOT_A_BUCKET;
}
static void ut_hhResize (struct ut_hh *rt)
{
struct ut_hhBucket *bs1;
uint32_t i, idxmask0, idxmask1;
_Analysis_assume_(rt->size >= HH_HOP_RANGE);
bs1 = os_malloc (2 * rt->size * sizeof (*rt->buckets));
for (i = 0; i < 2 * rt->size; i++) {
bs1[i].hopinfo = 0;
bs1[i].data = NULL;
}
idxmask0 = rt->size - 1;
idxmask1 = 2 * rt->size - 1;
for (i = 0; i < rt->size; i++) {
void *data = rt->buckets[i].data;
if (data) {
const uint32_t hash = rt->hash (data);
const uint32_t old_start_bucket = hash & idxmask0;
const uint32_t new_start_bucket = hash & idxmask1;
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (rt->size + i - old_start_bucket);
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
assert (dist < HH_HOP_RANGE);
bs1[new_start_bucket].hopinfo |= 1u << dist;
bs1[newb].data = data;
}
}
os_free (rt->buckets);
rt->size *= 2;
rt->buckets = bs1;
}
int ut_hhAdd (struct ut_hh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT data)
{
const uint32_t hash = rt->hash (data);
const uint32_t idxmask = rt->size - 1;
const uint32_t start_bucket = hash & idxmask;
uint32_t free_distance, free_bucket;
if (ut_hhLookupInternal (rt, start_bucket, data)) {
return 0;
}
free_bucket = start_bucket;
for (free_distance = 0; free_distance < HH_ADD_RANGE; free_distance++) {
if (rt->buckets[free_bucket].data == NULL)
break;
free_bucket = (free_bucket + 1) & idxmask;
}
if (free_distance < HH_ADD_RANGE) {
do {
if (free_distance < HH_HOP_RANGE) {
assert ((uint32_t) free_bucket == ((start_bucket + free_distance) & idxmask));
rt->buckets[start_bucket].hopinfo |= 1u << free_distance;
rt->buckets[free_bucket].data = (void *) data;
return 1;
}
free_bucket = ut_hhFindCloserFreeBucket (rt, free_bucket, &free_distance);
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
} while (free_bucket != NOT_A_BUCKET);
}
ut_hhResize (rt);
return ut_hhAdd (rt, data);
}
int ut_hhRemove (struct ut_hh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template)
{
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = rt->size - 1;
const uint32_t bucket = hash & idxmask;
uint32_t hopinfo;
uint32_t idx;
hopinfo = rt->buckets[bucket].hopinfo;
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
if (hopinfo & 1) {
const uint32_t bidx = (bucket + idx) & idxmask;
void *data = rt->buckets[bidx].data;
if (data && rt->equals (data, template)) {
rt->buckets[bidx].data = NULL;
rt->buckets[bucket].hopinfo &= ~(1u << idx);
return 1;
}
}
}
return 0;
}
void ut_hhEnum (struct ut_hh * UT_HH_RESTRICT rt, void (*f) (void *a, void *f_arg), void *f_arg)
{
uint32_t i;
for (i = 0; i < rt->size; i++) {
void *data = rt->buckets[i].data;
if (data) {
f (data, f_arg);
}
}
}
void *ut_hhIterFirst (struct ut_hh * UT_HH_RESTRICT rt, struct ut_hhIter * UT_HH_RESTRICT iter)
{
iter->hh = rt;
iter->cursor = 0;
return ut_hhIterNext (iter);
}
void *ut_hhIterNext (struct ut_hhIter * UT_HH_RESTRICT iter)
{
struct ut_hh *rt = iter->hh;
while (iter->cursor < rt->size) {
void *data = rt->buckets[iter->cursor].data;
iter->cursor++;
if (data) {
return data;
}
}
return NULL;
}
/************* SEQUENTIAL VERSION WITH EMBEDDED DATA ***************/
struct ut_ehhBucket {
uint32_t hopinfo;
uint32_t inuse;
char data[];
};
struct ut_ehh {
uint32_t size; /* power of 2 */
size_t elemsz;
size_t bucketsz;
char *buckets; /* ehhBucket, but embedded data messes up the layout */
ut_hhHash_fn hash;
ut_hhEquals_fn equals;
};
static void ut_ehhInit (struct ut_ehh *rt, size_t elemsz, uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals)
{
uint32_t size = HH_HOP_RANGE;
uint32_t i;
while (size < init_size) {
size *= 2;
}
rt->hash = hash;
rt->equals = equals;
rt->size = size;
rt->elemsz = elemsz;
rt->bucketsz = sizeof (struct ut_ehhBucket) + ((elemsz+7) & ~(size_t)7);
_Analysis_assume_(size >= HH_HOP_RANGE);
rt->buckets = os_malloc (size * rt->bucketsz);
for (i = 0; i < size; i++) {
struct ut_ehhBucket *b = (struct ut_ehhBucket *) (rt->buckets + i * rt->bucketsz);
b->hopinfo = 0;
b->inuse = 0;
}
}
static void ut_ehhFini (struct ut_ehh *rt)
{
os_free (rt->buckets);
}
struct ut_ehh *ut_ehhNew (size_t elemsz, uint32_t init_size, ut_hhHash_fn hash, ut_hhEquals_fn equals)
{
struct ut_ehh *hh = os_malloc (sizeof (*hh));
ut_ehhInit (hh, elemsz, init_size, hash, equals);
return hh;
}
void ut_ehhFree (struct ut_ehh * UT_HH_RESTRICT hh)
{
ut_ehhFini (hh);
os_free (hh);
}
static void *ut_ehhLookupInternal (const struct ut_ehh *rt, uint32_t bucket, const void *template)
{
const struct ut_ehhBucket *b = (const struct ut_ehhBucket *) (rt->buckets + bucket * rt->bucketsz);
uint32_t hopinfo = b->hopinfo;
if (hopinfo & 1) {
if (b->inuse && rt->equals (b->data, template)) {
return (void *) b->data;
}
}
do {
hopinfo >>= 1;
if (++bucket == rt->size) {
bucket = 0;
}
if (hopinfo & 1) {
b = (const struct ut_ehhBucket *) (rt->buckets + bucket * rt->bucketsz);
if (b->inuse && rt->equals (b->data, template)) {
return (void *) b->data;
}
}
} while (hopinfo != 0);
return NULL;
}
void *ut_ehhLookup (const struct ut_ehh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template)
{
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = rt->size - 1;
const uint32_t bucket = hash & idxmask;
return ut_ehhLookupInternal (rt, bucket, template);
}
static uint32_t ut_ehhFindCloserFreeBucket (struct ut_ehh *rt, uint32_t free_bucket, uint32_t *free_distance)
{
const uint32_t idxmask = rt->size - 1;
uint32_t move_bucket, free_dist;
move_bucket = (free_bucket - (HH_HOP_RANGE - 1)) & idxmask;
for (free_dist = HH_HOP_RANGE - 1; free_dist > 0; free_dist--) {
struct ut_ehhBucket * const mb = (struct ut_ehhBucket *) (rt->buckets + move_bucket * rt->bucketsz);
uint32_t move_free_distance = NOT_A_BUCKET;
uint32_t mask = 1;
uint32_t i;
for (i = 0; i < free_dist; i++, mask <<= 1) {
if (mask & mb->hopinfo) {
move_free_distance = i;
break;
}
}
if (move_free_distance != NOT_A_BUCKET) {
uint32_t new_free_bucket = (move_bucket + move_free_distance) & idxmask;
struct ut_ehhBucket * const fb = (struct ut_ehhBucket *) (rt->buckets + free_bucket * rt->bucketsz);
struct ut_ehhBucket * const nfb = (struct ut_ehhBucket *) (rt->buckets + new_free_bucket * rt->bucketsz);
mb->hopinfo |= 1u << free_dist;
fb->inuse = 1;
memcpy (fb->data, nfb->data, rt->elemsz);
nfb->inuse = 0;
mb->hopinfo &= ~(1u << move_free_distance);
*free_distance -= free_dist - move_free_distance;
return new_free_bucket;
}
move_bucket = (move_bucket + 1) & idxmask;
}
return NOT_A_BUCKET;
}
static void ut_ehhResize (struct ut_ehh *rt)
{
char *bs1;
uint32_t i, idxmask0, idxmask1;
bs1 = os_malloc (2 * rt->size * rt->bucketsz);
for (i = 0; i < 2 * rt->size; i++) {
struct ut_ehhBucket *b = (struct ut_ehhBucket *) (bs1 + i * rt->bucketsz);
b->hopinfo = 0;
b->inuse = 0;
}
idxmask0 = rt->size - 1;
idxmask1 = 2 * rt->size - 1;
for (i = 0; i < rt->size; i++) {
struct ut_ehhBucket const * const b = (struct ut_ehhBucket *) (rt->buckets + i * rt->bucketsz);
if (b->inuse) {
const uint32_t hash = rt->hash (b->data);
const uint32_t old_start_bucket = hash & idxmask0;
const uint32_t new_start_bucket = hash & idxmask1;
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (rt->size + i - old_start_bucket);
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
struct ut_ehhBucket * const nsb = (struct ut_ehhBucket *) (bs1 + new_start_bucket * rt->bucketsz);
struct ut_ehhBucket * const nb = (struct ut_ehhBucket *) (bs1 + newb * rt->bucketsz);
assert (dist < HH_HOP_RANGE);
assert (!nb->inuse);
nsb->hopinfo |= 1u << dist;
nb->inuse = 1;
memcpy (nb->data, b->data, rt->elemsz);
}
}
os_free (rt->buckets);
rt->size *= 2;
rt->buckets = bs1;
}
int ut_ehhAdd (struct ut_ehh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT data)
{
const uint32_t hash = rt->hash (data);
const uint32_t idxmask = rt->size - 1;
const uint32_t start_bucket = hash & idxmask;
uint32_t free_distance, free_bucket;
if (ut_ehhLookupInternal (rt, start_bucket, data)) {
return 0;
}
free_bucket = start_bucket;
for (free_distance = 0; free_distance < HH_ADD_RANGE; free_distance++) {
struct ut_ehhBucket const * const fb = (struct ut_ehhBucket *) (rt->buckets + free_bucket * rt->bucketsz);
if (!fb->inuse) {
break;
}
free_bucket = (free_bucket + 1) & idxmask;
}
if (free_distance < HH_ADD_RANGE) {
do {
if (free_distance < HH_HOP_RANGE) {
struct ut_ehhBucket * const sb = (struct ut_ehhBucket *) (rt->buckets + start_bucket * rt->bucketsz);
struct ut_ehhBucket * const fb = (struct ut_ehhBucket *) (rt->buckets + free_bucket * rt->bucketsz);
assert ((uint32_t) free_bucket == ((start_bucket + free_distance) & idxmask));
sb->hopinfo |= 1u << free_distance;
fb->inuse = 1;
memcpy (fb->data, data, rt->elemsz);
assert (ut_ehhLookupInternal (rt, start_bucket, data));
return 1;
}
free_bucket = ut_ehhFindCloserFreeBucket (rt, free_bucket, &free_distance);
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
} while (free_bucket != NOT_A_BUCKET);
}
ut_ehhResize (rt);
return ut_ehhAdd (rt, data);
}
int ut_ehhRemove (struct ut_ehh * UT_HH_RESTRICT rt, const void * UT_HH_RESTRICT template)
{
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = rt->size - 1;
const uint32_t bucket = hash & idxmask;
uint32_t hopinfo;
struct ut_ehhBucket *sb;
uint32_t idx;
sb = (struct ut_ehhBucket *) (rt->buckets + bucket * rt->bucketsz);
hopinfo = sb->hopinfo;
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
if (hopinfo & 1) {
const uint32_t bidx = (bucket + idx) & idxmask;
struct ut_ehhBucket *b = (struct ut_ehhBucket *) (rt->buckets + bidx * rt->bucketsz);
if (b->inuse && rt->equals (b->data, template)) {
assert (ut_ehhLookupInternal(rt, bucket, template));
b->inuse = 0;
sb->hopinfo &= ~(1u << idx);
return 1;
}
}
}
assert (!ut_ehhLookupInternal(rt, bucket, template));
return 0;
}
void ut_ehhEnum (struct ut_ehh * UT_HH_RESTRICT rt, void (*f) (void *a, void *f_arg), void *f_arg)
{
uint32_t i;
for (i = 0; i < rt->size; i++) {
struct ut_ehhBucket *b = (struct ut_ehhBucket *) (rt->buckets + i * rt->bucketsz);
if (b->inuse) {
f (b->data, f_arg);
}
}
}
void *ut_ehhIterFirst (struct ut_ehh * UT_HH_RESTRICT rt, struct ut_ehhIter * UT_HH_RESTRICT iter)
{
iter->hh = rt;
iter->cursor = 0;
return ut_ehhIterNext (iter);
}
void *ut_ehhIterNext (struct ut_ehhIter * UT_HH_RESTRICT iter)
{
struct ut_ehh *rt = iter->hh;
while (iter->cursor < rt->size) {
struct ut_ehhBucket *b = (struct ut_ehhBucket *) (rt->buckets + iter->cursor * rt->bucketsz);
iter->cursor++;
if (b->inuse) {
return b->data;
}
}
return NULL;
}

View file

@ -0,0 +1,281 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <string.h>
#include "os/os.h"
#include "util/ut_thread_pool.h"
typedef struct ddsi_work_queue_job
{
struct ddsi_work_queue_job * m_next_job; /* Jobs list pointer */
void (*m_fn) (void *arg); /* Thread function */
void * m_arg; /* Thread function argument */
}
* ddsi_work_queue_job_t;
struct ut_thread_pool_s
{
ddsi_work_queue_job_t m_jobs; /* Job queue */
ddsi_work_queue_job_t m_jobs_tail; /* Tail of job queue */
ddsi_work_queue_job_t m_free; /* Job free list */
uint32_t m_thread_max; /* Maximum number of threads */
uint32_t m_thread_min; /* Minimum number of threads */
uint32_t m_threads; /* Current number of threads */
uint32_t m_waiting; /* Number of threads waiting for a job */
uint32_t m_job_count; /* Number of queued jobs */
uint32_t m_job_max; /* Maximum number of jobs to queue */
unsigned short m_count; /* Counter for thread name */
os_threadAttr m_attr; /* Thread creation attribute */
os_cond m_cv; /* Thread wait semaphore */
os_mutex m_mutex; /* Pool guard mutex */
};
static uint32_t ut_thread_start_fn (_In_ void * arg)
{
ddsi_work_queue_job_t job;
ut_thread_pool pool = (ut_thread_pool) arg;
/* Thread loops, pulling jobs from queue */
os_mutexLock (&pool->m_mutex);
while (pool->m_jobs != NULL) {
/* Wait for job */
os_condWait (&pool->m_cv, &pool->m_mutex);
/* Check if pool deleted or being purged */
if (pool->m_jobs) {
/* Take job from queue head */
pool->m_waiting--;
job = pool->m_jobs;
pool->m_jobs = job->m_next_job;
pool->m_job_count--;
os_mutexUnlock (&pool->m_mutex);
/* Do job */
(job->m_fn) (job->m_arg);
/* Put job back on free list */
os_mutexLock (&pool->m_mutex);
pool->m_waiting++;
job->m_next_job = pool->m_free;
pool->m_free = job;
}
}
if (--pool->m_threads) {
/* last to leave triggers thread_pool_free */
os_condBroadcast (&pool->m_cv);
}
os_mutexUnlock (&pool->m_mutex);
return 0;
}
static os_result ut_thread_pool_new_thread (ut_thread_pool pool)
{
static unsigned char pools = 0; /* Pool counter - TODO make atomic */
char name [64];
os_threadId id;
os_result res;
(void) snprintf (name, sizeof (name), "OSPL-%u-%u", pools++, pool->m_count++);
res = os_threadCreate (&id, name, &pool->m_attr, &ut_thread_start_fn, pool);
if (res == os_resultSuccess)
{
os_mutexLock (&pool->m_mutex);
pool->m_threads++;
pool->m_waiting++;
os_mutexUnlock (&pool->m_mutex);
}
return res;
}
ut_thread_pool ut_thread_pool_new (uint32_t threads, uint32_t max_threads, uint32_t max_queue, os_threadAttr * attr)
{
ut_thread_pool pool;
ddsi_work_queue_job_t job;
/* Sanity check QoS */
if (max_threads && (max_threads < threads))
{
max_threads = threads;
}
if (max_queue && (max_queue < threads))
{
max_queue = threads;
}
pool = os_malloc (sizeof (*pool));
memset (pool, 0, sizeof (*pool));
pool->m_thread_min = threads;
pool->m_thread_max = max_threads;
pool->m_job_max = max_queue;
os_threadAttrInit (&pool->m_attr);
os_mutexInit (&pool->m_mutex);
os_condInit (&pool->m_cv, &pool->m_mutex);
if (attr)
{
pool->m_attr = *attr;
}
/* Create initial threads and jobs */
while (threads--)
{
if (ut_thread_pool_new_thread (pool) != os_resultSuccess)
{
ut_thread_pool_free (pool);
pool = NULL;
break;
}
job = os_malloc (sizeof (*job));
job->m_next_job = pool->m_free;
pool->m_free = job;
}
return pool;
}
void ut_thread_pool_free (ut_thread_pool pool)
{
ddsi_work_queue_job_t job;
if (pool == NULL)
{
return;
}
os_mutexLock (&pool->m_mutex);
/* Delete all pending jobs from queue */
while (pool->m_jobs)
{
job = pool->m_jobs;
pool->m_jobs = job->m_next_job;
os_free (job);
}
/* Wake all waiting threads */
os_condBroadcast (&pool->m_cv);
os_mutexUnlock (&pool->m_mutex);
/* Wait for threads to complete */
os_mutexLock (&pool->m_mutex);
while (pool->m_threads != 0)
os_condWait (&pool->m_cv, &pool->m_mutex);
os_mutexUnlock (&pool->m_mutex);
/* Delete all free jobs from queue */
while (pool->m_free)
{
job = pool->m_free;
pool->m_free = job->m_next_job;
os_free (job);
}
os_condDestroy (&pool->m_cv);
os_mutexDestroy (&pool->m_mutex);
os_free (pool);
}
os_result ut_thread_pool_submit (ut_thread_pool pool, void (*fn) (void *arg), void * arg)
{
os_result res = os_resultSuccess;
ddsi_work_queue_job_t job;
os_mutexLock (&pool->m_mutex);
if (pool->m_job_max && pool->m_job_count >= pool->m_job_max)
{
/* Maximum number of jobs reached */
res = os_resultBusy;
}
else
{
/* Get or create new job */
if (pool->m_free)
{
job = pool->m_free;
pool->m_free = job->m_next_job;
}
else
{
job = os_malloc (sizeof (*job));
}
job->m_next_job = NULL;
job->m_fn = fn;
job->m_arg = arg;
/* Add new job to end of queue */
if (pool->m_jobs)
{
pool->m_jobs_tail->m_next_job = job;
}
else
{
pool->m_jobs = job;
}
pool->m_jobs_tail = job;
pool->m_job_count++;
/* Allocate thread if more jobs than waiting threads and within maximum */
if (pool->m_waiting < pool->m_job_count)
{
if ((pool->m_thread_max == 0) || (pool->m_threads < pool->m_thread_max))
{
/* OK if fails as have queued job */
(void) ut_thread_pool_new_thread (pool);
}
}
/* Wakeup processing thread */
os_condSignal (&pool->m_cv);
}
os_mutexUnlock (&pool->m_mutex);
return res;
}
void ut_thread_pool_purge (ut_thread_pool pool)
{
uint32_t total;
os_mutexLock (&pool->m_mutex);
total = pool->m_threads;
while (pool->m_waiting && (total > pool->m_thread_min))
{
pool->m_waiting--;
total--;
}
os_condBroadcast (&pool->m_cv);
os_mutexUnlock (&pool->m_mutex);
}

705
src/util/src/ut_xmlparser.c Normal file
View file

@ -0,0 +1,705 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "os/os.h"
#include "util/ut_xmlparser.h"
#define TOK_EOF -1
#define TOK_OPEN_TAG -2
#define TOK_ID -3
#define TOK_STRING -4
#define TOK_CLOSE_TAG -5
#define TOK_SHORTHAND_CLOSE_TAG -6
#define TOK_ERROR -7
#define NOMARKER (~(size_t)0)
struct ut_xmlpState {
size_t cbufp; /* current position in cbuf */
size_t cbufn; /* number of bytes in cbuf (cbufp <= cbufn) */
size_t cbufmax; /* allocated size of cbuf (cbufn <= cbufmax) */
size_t cbufmark; /* NORMARKER or marker position (cbufmark <= cbufp) for rewinding */
char *cbuf; /* parser input buffer */
FILE *fp; /* file to refill cbuf from, or NULL if parsing a string */
int line; /* current line number */
int prevline; /* line number at last token */
int linemark; /* line number at marker */
int peektok; /* token lookahead (peek token when no next token available) */
char *peekpayload; /* payload associated with lookahead */
int error; /* error flag to call error callback only once */
size_t tpp; /* length of token payload */
size_t tpsz; /* allocated size of tp */
char *tp; /* token payload buffer */
size_t tpescp; /* still escape sequences in tpescp .. tpp */
int nest; /* current nesting level */
void *varg; /* user argument to callback functions */
struct ut_xmlpCallbacks cb; /* user-supplied callbacks (or stubs) */
};
static int cb_null_elem_open (void *varg, uintptr_t parentinfo, uintptr_t *eleminfo, const char *name)
{
OS_UNUSED_ARG (varg);
OS_UNUSED_ARG (parentinfo);
OS_UNUSED_ARG (eleminfo);
OS_UNUSED_ARG (name);
return 0;
}
static int cb_null_attr (void *varg, uintptr_t eleminfo, const char *name, const char *value)
{
OS_UNUSED_ARG (varg);
OS_UNUSED_ARG (eleminfo);
OS_UNUSED_ARG (name);
OS_UNUSED_ARG (value);
return 0;
}
static int cb_null_elem_data (void *varg, uintptr_t eleminfo, const char *data)
{
OS_UNUSED_ARG (varg);
OS_UNUSED_ARG (eleminfo);
OS_UNUSED_ARG (data);
return 0;
}
static int cb_null_elem_close (void *varg, uintptr_t eleminfo)
{
OS_UNUSED_ARG (varg);
OS_UNUSED_ARG (eleminfo);
return 0;
}
static void cb_null_error (void *varg, const char *msg, int line)
{
OS_UNUSED_ARG (varg);
OS_UNUSED_ARG (msg);
OS_UNUSED_ARG (line);
}
static void ut_xmlpNewCommon (struct ut_xmlpState *st)
{
st->cbufp = 0;
st->cbufmark = NOMARKER;
st->tpp = 0;
st->tpescp = 0;
st->tpsz = 1024;
st->tp = os_malloc (st->tpsz);
st->line = 1;
st->prevline = 1;
st->linemark = 0;
st->peektok = 0;
st->peekpayload = NULL;
st->nest = 0;
st->error = 0;
}
static void ut_xmlpNewSetCB (struct ut_xmlpState *st, void *varg, const struct ut_xmlpCallbacks *cb)
{
st->varg = varg;
st->cb = *cb;
if (st->cb.attr == 0) st->cb.attr = cb_null_attr;
if (st->cb.elem_open == 0) st->cb.elem_open = cb_null_elem_open;
if (st->cb.elem_data == 0) st->cb.elem_data = cb_null_elem_data;
if (st->cb.elem_close == 0) st->cb.elem_close = cb_null_elem_close;
if (st->cb.error == 0) st->cb.error = cb_null_error;
}
struct ut_xmlpState *ut_xmlpNewFile (FILE *fp, void *varg, const struct ut_xmlpCallbacks *cb)
{
struct ut_xmlpState *st;
st = os_malloc (sizeof (*st));
st->cbufn = 0;
st->cbufmax = 8192;
st->cbuf = os_malloc (st->cbufmax);
st->fp = fp;
ut_xmlpNewCommon (st);
ut_xmlpNewSetCB (st, varg, cb);
return st;
}
struct ut_xmlpState *ut_xmlpNewString (const char *string, void *varg, const struct ut_xmlpCallbacks *cb)
{
struct ut_xmlpState *st;
st = os_malloc (sizeof (*st));
st->cbufn = strlen (string);
st->cbufmax = st->cbufn;
st->cbuf = (char *) string;
st->fp = NULL;
ut_xmlpNewCommon (st);
ut_xmlpNewSetCB (st, varg, cb);
return st;
}
void ut_xmlpFree (struct ut_xmlpState *st)
{
if (st->fp != NULL) {
os_free (st->cbuf);
}
os_free (st->tp);
os_free (st);
}
static int make_chars_available (struct ut_xmlpState *st, size_t nmin)
{
size_t n, pos;
pos = (st->cbufmark != NOMARKER) ? st->cbufmark : st->cbufp;
assert (st->cbufn >= st->cbufp);
assert (st->cbufmax >= st->cbufn);
assert (st->cbufmark == NOMARKER || st->cbufmark <= st->cbufp);
/* fast-path available chars */
if (st->cbufn - st->cbufp >= nmin) {
return 1;
}
/* ensure buffer space is available */
if (pos + nmin > st->cbufmax) {
memmove (st->cbuf, st->cbuf + pos, st->cbufn - pos);
st->cbufn -= pos;
st->cbufp -= pos;
if (st->cbufmark != NOMARKER) {
st->cbufmark -= pos;
}
}
/* buffer is owned by caller if fp = NULL, and by us if fp != NULL */
if (st->cbufp + st->cbufmax < nmin && st->fp != NULL) {
st->cbufmax = st->cbufp + nmin;
st->cbuf = os_realloc (st->cbuf, st->cbufmax);
}
/* try to refill buffer if a backing file is present; eof (or end-of-string) is
reached when this doesn't add any bytes to the buffer */
if (st->fp != NULL) {
n = fread (st->cbuf + st->cbufn, 1, st->cbufmax - st->cbufn, st->fp);
st->cbufn += n;
}
return (st->cbufn - st->cbufp >= nmin);
}
static void set_input_marker (struct ut_xmlpState *st)
{
assert (st->cbufmark == NOMARKER);
st->cbufmark = st->cbufp;
st->linemark = st->line;
}
static void discard_input_marker (struct ut_xmlpState *st)
{
assert (st->cbufmark != NOMARKER);
st->cbufmark = NOMARKER;
st->linemark = 0;
}
static void rewind_to_input_marker (struct ut_xmlpState *st)
{
assert (st->cbufmark != NOMARKER);
st->cbufp = st->cbufmark;
st->line = st->linemark;
discard_input_marker (st);
}
static int next_char (struct ut_xmlpState *st)
{
char c;
if (!make_chars_available (st, 1)) {
return TOK_EOF;
}
c = st->cbuf[st->cbufp++];
if (c == '\n') {
st->line++;
}
return c;
}
static int peek_char (struct ut_xmlpState *st)
{
if (!make_chars_available (st, 1)) {
return TOK_EOF;
}
return st->cbuf[st->cbufp];
}
static int peek_chars (struct ut_xmlpState *st, const char *seq, int consume)
{
size_t n = strlen (seq);
if (!make_chars_available (st, n)) {
return 0;
}
if (memcmp (st->cbuf + st->cbufp, seq, n) != 0) {
return 0;
} else {
if (consume) st->cbufp += n;
return 1;
}
}
static int qq_isspace (int x)
{
return x == ' ' || x == '\t' || x == '\v' || x == '\r' || x == '\n';
}
static int qq_isidentfirst (int x)
{
return (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z');
}
static int qq_isidentcont (int x)
{
return qq_isidentfirst (x) || (x >= '0' && x <= '9') || x == '_' || x == '-' || x == ':';
}
static char *unescape_into_utf8 (char *dst, unsigned cp)
{
if (cp < 0x80) {
*dst++ = (char) cp;
} else if (cp <= 0x7ff) {
*dst++ = (char) ((cp >> 6) + 0xc0);
*dst++ = (char) ((cp & 0x3f) + 0x80);
} else if (cp <= 0xffff) {
*dst++ = (char) ((cp >> 12) + 0xe0);
*dst++ = (char) (((cp >> 6) & 0x3f) + 0x80);
*dst++ = (char) ((cp & 0x3f) + 0x80);
} else if (cp <= 0x10ffff) {
*dst++ = (char) ((cp >> 18) + 0xf0);
*dst++ = (char) (((cp >> 12) & 0x3f) + 0x80);
*dst++ = (char) (((cp >> 6) & 0x3f) + 0x80);
*dst++ = (char) ((cp & 0x3f) + 0x80);
} else {
dst = NULL;
}
return dst;
}
static int unescape_insitu (char *buffer, size_t *n)
{
const char *src = buffer;
char const * const srcend = buffer + *n;
char *dst = buffer;
while (src < srcend)
{
if (*src != '&') {
*dst++ = *src++;
} else if (src + 1 == srcend) {
return -1;
} else {
char tmp[16], *ptmp = tmp;
src++;
while (ptmp < tmp + sizeof (tmp) && src < srcend) {
char c = *src++;
*ptmp++ = c;
if (c == ';') {
break;
}
}
if (ptmp == tmp || *(ptmp-1) != ';') {
return -1;
}
*--ptmp = 0;
if (tmp[0] == '#') {
unsigned cp;
int pos;
if (sscanf (tmp, "#x%x%n", &cp, &pos) == 1 && tmp[pos] == 0) {
;
} else if (sscanf (tmp, "#%u%n", &cp, &pos) == 1 && tmp[pos] == 0) {
;
} else {
return -1;
}
if ((dst = unescape_into_utf8(dst, cp)) == NULL) {
return -1;
}
} else if (strcmp (tmp, "lt") == 0) {
*dst++ = '<';
} else if (strcmp (tmp, "gt") == 0) {
*dst++ = '>';
} else if (strcmp (tmp, "amp") == 0) {
*dst++ = '&';
} else if (strcmp (tmp, "apos") == 0) {
*dst++ = '\'';
} else if (strcmp (tmp, "quot") == 0) {
*dst++ = '"';
} else {
return -1;
}
}
}
*n = (size_t) (dst - buffer);
return 0;
}
static void discard_payload (struct ut_xmlpState *st)
{
st->tpp = 0;
st->tpescp = 0;
}
static int append_to_payload (struct ut_xmlpState *st, int c, int islit)
{
if (!islit) {
st->tp[st->tpp++] = (char) c;
} else {
if (st->tpescp < st->tpp) {
size_t n = st->tpp - st->tpescp;
if (unescape_insitu (st->tp + st->tpescp, &n) < 0) {
discard_payload (st);
return -1;
}
st->tpp = st->tpescp + n;
}
st->tp[st->tpp++] = (char) c;
st->tpescp = st->tpp;
}
if (st->tpp == st->tpsz) {
st->tpsz += 1024;
st->tp = os_realloc (st->tp, st->tpsz);
}
return 0;
}
static int save_payload (char **payload, struct ut_xmlpState *st, int trim)
{
char *p;
if (st->tpescp < st->tpp) {
size_t n = st->tpp - st->tpescp;
if (unescape_insitu (st->tp + st->tpescp, &n) < 0) {
discard_payload (st);
return -1;
}
st->tpp = st->tpescp + n;
}
if (payload == NULL) {
p = NULL;
} else if (st->tpp == 0) {
p = os_strdup("");
} else {
size_t first = 0, last = st->tpp - 1;
if (trim) {
while (first <= last && qq_isspace (st->tp[first])) {
first++;
}
while (first <= last && qq_isspace (st->tp[last]) && last > 0) {
last--;
}
}
if (first > last) {
p = os_strdup("");
} else {
p = os_malloc (last - first + 2);
/* Could be improved, parser error will be "invalid char sequence" if malloc fails. */
memcpy (p, st->tp + first, last - first + 1);
p[last - first + 1] = 0;
}
}
discard_payload (st);
if (payload) {
*payload = p;
}
return 0;
}
static int next_token_ident (struct ut_xmlpState *st, char **payload)
{
while (qq_isidentcont (peek_char (st))) {
if (append_to_payload (st, next_char (st), 0) < 0) {
return TOK_ERROR;
}
}
if (save_payload (payload, st, 0) < 0) {
return TOK_ERROR;
} else {
return TOK_ID;
}
}
static int next_token_tag_withoutclose (struct ut_xmlpState *st, char **payload)
{
if (peek_chars (st, "<![CDATA[", 0)) {
return next_char (st);
} else {
int tok = TOK_OPEN_TAG;
/* pre: peek_char(st) == '<' */
next_char (st);
if (peek_char (st) == '/') {
tok = TOK_CLOSE_TAG;
next_char (st);
}
/* we only do tag names that are identifiers */
if (!qq_isidentfirst (peek_char (st))) {
return TOK_ERROR;
}
next_token_ident (st, payload);
return tok;
}
}
static int next_token_string (struct ut_xmlpState *st, char **payload)
{
/* pre: peek_char(st) == ('"' or '\'') */
int endm = next_char (st);
while (peek_char (st) != endm && peek_char (st) != TOK_EOF) {
if (append_to_payload (st, next_char (st), 0) < 0) {
return TOK_ERROR;
}
}
if (next_char (st) != endm) {
discard_payload (st);
return TOK_ERROR;
} else if (save_payload (payload, st, 0) < 0) {
return TOK_ERROR;
} else {
return TOK_STRING;
}
}
static int skip_comment (struct ut_xmlpState *st)
{
if (!peek_chars (st, "<!--", 1)) {
return 0;
}
while ((peek_char (st) != TOK_EOF && peek_char (st) != '-') || !peek_chars (st, "-->", 0)) {
next_char (st);
}
if (peek_chars (st, "-->", 1)) {
return 1;
} else {
return TOK_ERROR;
}
}
static void processing_instruction (struct ut_xmlpState *st, const char *end)
{
/* just after <?; skip everything up to and include ?> */
while (peek_char (st) != TOK_EOF && !peek_chars (st, end, 1)) {
next_char (st);
}
}
static void drop_peek_token (struct ut_xmlpState *st)
{
st->peektok = 0;
if (st->peekpayload) {
os_free (st->peekpayload);
st->peekpayload = NULL;
}
}
static int next_token (struct ut_xmlpState *st, char **payload)
{
/* Always return a valid pointer to allocated memory or a null
pointer, regardless of token type */
if (payload) {
*payload = NULL;
}
if (st->error) {
return TOK_ERROR;
} else if (st->peektok) {
int tok = st->peektok;
st->peektok = 0;
if (payload) {
*payload = st->peekpayload;
} else if (st->peekpayload) {
os_free (st->peekpayload);
st->peekpayload = NULL;
}
return tok;
} else {
int cmt, tok;
st->prevline = st->line;
do {
while (qq_isspace (peek_char (st))) {
next_char (st);
}
} while ((cmt = skip_comment (st)) > 0);
if (cmt == TOK_ERROR) {
tok = TOK_ERROR;
} else if (peek_chars (st, "<?", 1)) {
processing_instruction (st, "?>");
return next_token (st, payload);
} else if (peek_chars (st, "<!", 1)) {
processing_instruction (st, ">");
return next_token (st, payload);
} else {
int n = peek_char (st);
if (n == '<') {
tok = next_token_tag_withoutclose (st, payload);
} else if (n == '"' || n == '\'') {
tok = next_token_string (st, payload);
} else if (qq_isidentfirst (n)) {
tok = next_token_ident (st, payload);
} else if (peek_chars (st, "/>", 1)) {
tok = TOK_SHORTHAND_CLOSE_TAG;
} else {
tok = next_char (st);
}
}
if (tok == TOK_ERROR) {
st->error = 1;
}
return tok;
}
}
static int peek_token (struct ut_xmlpState *st)
{
int tok;
char *payload;
tok = next_token (st, &payload);
st->peektok = tok;
st->peekpayload = payload;
return tok;
}
static int parse_element (struct ut_xmlpState *st, uintptr_t parentinfo)
{
#define PE_ERROR2(c,c1,c2) do { errc = (c); errc1 = (c1); errc2 = (c2); goto err; } while (0)
#define PE_ERROR(c,c1) PE_ERROR2(c,c1,0)
#define PE_LOCAL_ERROR(c,c1) do { ret = -1; PE_ERROR ((c), (c1)); } while (0)
char *name = NULL, *aname = NULL, *ename = NULL;
const char *errc = NULL, *errc1 = NULL, *errc2 = NULL;
uintptr_t eleminfo;
int ret = 0, tok;
if (next_token (st, &name) != TOK_OPEN_TAG) {
PE_LOCAL_ERROR ("expecting '<'", 0);
}
if ((ret = st->cb.elem_open (st->varg, parentinfo, &eleminfo, name)) < 0) {
PE_ERROR ("failed in element open callback", name);
}
while (peek_token (st) == TOK_ID) {
char *content;
next_token (st, &aname);
if (next_token (st, NULL) != '=') {
PE_LOCAL_ERROR ("expecting '=' following attribute name", aname);
}
if (next_token (st, &content) != TOK_STRING) {
os_free (content);
PE_LOCAL_ERROR ("expecting string value for attribute", aname);
}
ret = st->cb.attr (st->varg, eleminfo, aname, content);
os_free (content);
if (ret < 0) {
PE_ERROR2 ("failed in attribute callback", name, aname);
}
os_free (aname);
aname = NULL;
}
tok = next_token (st, NULL);
switch (tok)
{
case TOK_SHORTHAND_CLOSE_TAG:
ret = st->cb.elem_close (st->varg, eleminfo);
goto ok;
case '>':
st->nest++;
set_input_marker (st);
if (peek_token (st) == TOK_OPEN_TAG) {
/* child elements */
discard_input_marker (st);
while (peek_token (st) == TOK_OPEN_TAG) {
if ((ret = parse_element (st, eleminfo)) < 0) {
PE_ERROR ("parse children", 0);
}
}
} else {
/* text */
char *content;
int cmt = 0;
rewind_to_input_marker (st);
drop_peek_token (st);
do {
/* gobble up content until EOF or markup */
while (peek_char (st) != '<' && peek_char (st) != TOK_EOF) {
if (append_to_payload (st, next_char (st), 0) < 0) {
PE_LOCAL_ERROR ("invalid character sequence", 0);
}
}
/* if the mark-up happens to be a CDATA, consume it, and gobble up characters
until the closing marker is reached, which then also gets consumed */
if (peek_chars (st, "<![CDATA[", 1)) {
while (!peek_chars (st, "]]>", 1) && peek_char (st) != TOK_EOF) {
if (append_to_payload (st, next_char (st), 1) < 0) {
PE_LOCAL_ERROR ("invalid character sequence", 0);
}
}
}
/* then, if the markup is a comment, skip it and try again */
} while (peek_char (st) != '<' || (cmt = skip_comment (st)) > 0);
if (cmt == TOK_ERROR) {
discard_payload (st);
PE_LOCAL_ERROR ("invalid comment", 0);
}
if (save_payload (&content, st, 1) < 0) {
PE_ERROR ("invalid character sequence", 0);
} else if (content != NULL) {
if(*content != '\0') {
ret = st->cb.elem_data (st->varg, eleminfo, content);
os_free (content);
if (ret < 0) {
PE_ERROR ("failed in data callback", 0);
}
} else {
os_free (content);
}
}
}
st->nest--;
if (next_token (st, &ename) != TOK_CLOSE_TAG || next_char (st) != '>') {
PE_LOCAL_ERROR ("expecting closing tag", name);
}
if (strcmp (name, ename) != 0) {
PE_LOCAL_ERROR ("open/close tag mismatch", ename);
}
ret = st->cb.elem_close (st->varg, eleminfo);
goto ok;
default:
PE_LOCAL_ERROR ("expecting '/>' or '>'", 0);
}
err:
if (!st->error) {
char msg[512];
(void) snprintf (msg, sizeof (msg), "%s (%s%s%s)", errc, errc1 ? errc1 : "", errc1 && errc2 ? ", " : "", errc2 ? errc2 : "");
st->cb.error (st->varg, msg, st->prevline);
st->error = 1;
}
ok:
os_free (name);
os_free (aname);
os_free (ename);
return ret;
#undef PE_LOCAL_ERROR
#undef PE_ERROR
#undef PE_ERROR2
}
int ut_xmlpParse (struct ut_xmlpState *st)
{
if (peek_token (st) == TOK_EOF) {
return 0;
} else {
int ret = parse_element (st, 0);
if (ret < 0 || next_token (st, NULL) == TOK_EOF) {
return ret;
} else {
return -1;
}
}
}
int ut_xmlUnescapeInsitu (char *buffer, size_t *n) {
return unescape_insitu (buffer, n);
}

View file

@ -0,0 +1,14 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
include(Criterion)
add_criterion_executable(criterion_util .)
target_link_libraries(criterion_util util)

View file

@ -0,0 +1,311 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "os/os.h"
#include "util/ut_handleserver.h"
#include <criterion/criterion.h>
#include <criterion/logging.h>
/* Add --verbose command line argument to get the cr_log_info traces (if there are any). */
/*****************************************************************************************/
Test(util_handleserver, basic)
{
const os_time zero = { 0, 0 };
int32_t kind = 0x10000000;
ut_handle_retcode_t ret;
ut_handle_t hdl;
int arg = 1;
void *argx;
ret = ut_handleserver_init();
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handleserver_init");
hdl = ut_handle_create(kind, (void*)&arg);
cr_assert(hdl > 0, "ut_handle_create");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim ret");
cr_assert_eq(argx, &arg, "ut_handle_claim arg");
ut_handle_release(hdl, NULL);
ret = ut_handle_delete(hdl, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_DELETED, "ut_handle_claim ret");
ut_handleserver_fini();
}
/*****************************************************************************************/
Test(util_handleserver, close)
{
const os_time zero = { 0, 0 };
int32_t kind = 0x10000000;
ut_handle_retcode_t ret;
ut_handle_t hdl;
int arg = 1;
void *argx;
bool closed;
ret = ut_handleserver_init();
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handleserver_init");
hdl = ut_handle_create(kind, (void*)&arg);
cr_assert(hdl > 0, "ut_handle_create");
closed = ut_handle_is_closed(hdl, NULL);
cr_assert_eq(closed, false, "ut_handle_is_closed ret");
ut_handle_close(hdl, NULL);
closed = ut_handle_is_closed(hdl, NULL);
cr_assert_eq(closed, true, "ut_handle_is_closed ret");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_CLOSED, "ut_handle_claim ret");
ret = ut_handle_delete(hdl, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_DELETED, "ut_handle_claim ret");
ut_handleserver_fini();
}
/*****************************************************************************************/
Test(util_handleserver, link)
{
const os_time zero = { 0, 0 };
int32_t kind = 0x10000000;
ut_handle_retcode_t ret;
struct ut_handlelink *link;
ut_handle_t hdl;
int arg = 1;
void *argx;
ret = ut_handleserver_init();
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handleserver_init");
hdl = ut_handle_create(kind, (void*)&arg);
cr_assert(hdl > 0, "ut_handle_create");
link = ut_handle_get_link(hdl);
cr_assert_neq(link, NULL, "ut_handle_get_link");
ret = ut_handle_claim(hdl, link, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim ret");
cr_assert_eq(argx, &arg, "ut_handle_claim arg");
ut_handle_release(hdl, link);
ret = ut_handle_delete(hdl, link, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
link = ut_handle_get_link(hdl);
cr_assert_eq(link, NULL, "ut_handle_get_link");
ut_handleserver_fini();
}
/*****************************************************************************************/
Test(util_handleserver, types)
{
const os_time zero = { 0, 0 };
int32_t kind1 = 0x10000000;
int32_t kind2 = 0x20000000;
ut_handle_retcode_t ret;
ut_handle_t hdl1a;
ut_handle_t hdl1b;
ut_handle_t hdl2;
int arg1a = (int)'a';
int arg1b = (int)'b';
int arg2 = (int)'2';
void *argx;
ret = ut_handleserver_init();
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handleserver_init");
hdl1a = ut_handle_create(kind1, (void*)&arg1a);
cr_assert(hdl1a > 0, "ut_handle_create");
hdl1b = ut_handle_create(kind1, (void*)&arg1b);
cr_assert(hdl1b > 0, "ut_handle_create");
hdl2 = ut_handle_create(kind2, (void*)&arg2);
cr_assert(hdl2 > 0, "ut_handle_create");
ret = ut_handle_claim(hdl1a, NULL, kind1, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim ret");
cr_assert_eq(argx, &arg1a, "ut_handle_claim arg");
ret = ut_handle_claim(hdl1b, NULL, kind1, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim ret");
cr_assert_eq(argx, &arg1b, "ut_handle_claim arg");
ret = ut_handle_claim(hdl2, NULL, kind2, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim ret");
cr_assert_eq(argx, &arg2, "ut_handle_claim arg");
ret = ut_handle_claim(hdl1a, NULL, kind2, &argx);
cr_assert_eq(ret, UT_HANDLE_UNEQUAL_KIND, "ut_handle_claim ret");
ret = ut_handle_claim(hdl1a, NULL, kind2, &argx);
cr_assert_eq(ret, UT_HANDLE_UNEQUAL_KIND, "ut_handle_claim ret");
ret = ut_handle_claim(hdl2, NULL, kind1, &argx);
cr_assert_eq(ret, UT_HANDLE_UNEQUAL_KIND, "ut_handle_claim ret");
ut_handle_release(hdl1a, NULL);
ut_handle_release(hdl1b, NULL);
ut_handle_release(hdl2, NULL);
ret = ut_handle_delete(hdl1a, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
ret = ut_handle_delete(hdl1b, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
ret = ut_handle_delete(hdl2, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
ut_handleserver_fini();
}
/*****************************************************************************************/
Test(util_handleserver, timeout)
{
const os_time zero = { 0, 0 };
int32_t kind = 0x10000000;
ut_handle_retcode_t ret;
ut_handle_t hdl;
int arg = 1;
void *argx;
ret = ut_handleserver_init();
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handleserver_init");
hdl = ut_handle_create(kind, (void*)&arg);
cr_assert(hdl > 0, "ut_handle_create");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim ret");
cr_assert_eq(argx, &arg, "ut_handle_claim arg");
ret = ut_handle_delete(hdl, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_TIMEOUT, "ut_handle_delete");
ut_handle_release(hdl, NULL);
ret = ut_handle_delete(hdl, NULL, zero);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete");
ut_handleserver_fini();
}
/*****************************************************************************************/
typedef enum thread_state_t {
STARTING,
DELETING,
STOPPED
} thread_state_t;
typedef struct thread_arg_t {
thread_state_t state;
ut_handle_t hdl;
} thread_arg_t;
static uint32_t
deleting_thread(void *a)
{
thread_arg_t *arg = (thread_arg_t*)a;
const os_time ten = { 10, 0 };
ut_handle_t ret;
arg->state = DELETING;
/* This should block until the main test released all claims. */
ret = ut_handle_delete(arg->hdl, NULL, ten);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_delete ret");
arg->state = STOPPED;
return 0;
}
os_result
thread_reached_state(thread_state_t *actual, thread_state_t expected, int32_t msec)
{
/* Convenience function. */
bool stopped = false;
os_time msec10 = { 0, 10000000 };
while ((msec > 0) && (*actual != expected)) {
os_nanoSleep(msec10);
msec -= 10;
}
return (*actual == expected) ? os_resultSuccess : os_resultTimeout;
}
Test(util_handleserver, wakeup)
{
const os_time zero = { 0, 0 };
int32_t kind = 0x10000000;
ut_handle_retcode_t ret;
ut_handle_t hdl;
int arg = 1;
void *argx;
os_threadId thread_id;
thread_arg_t thread_arg;
os_threadAttr thread_attr;
os_result osr;
ret = ut_handleserver_init();
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handleserver_init");
hdl = ut_handle_create(kind, (void*)&arg);
cr_assert(hdl > 0, "ut_handle_create");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim1 ret");
ret = ut_handle_claim(hdl, NULL, kind, &argx);
cr_assert_eq(ret, UT_HANDLE_OK, "ut_handle_claim2 ret");
/* Try deleting in other thread, which should block. */
thread_arg.hdl = hdl;
thread_arg.state = STARTING;
os_threadAttrInit(&thread_attr);
osr = os_threadCreate(&thread_id, "deleting_thread", &thread_attr, deleting_thread, (void*)&thread_arg);
cr_assert_eq(osr, os_resultSuccess, "os_threadCreate");
osr = thread_reached_state(&thread_arg.state, DELETING, 1000);
cr_assert_eq(osr, os_resultSuccess, "deleting");
osr = thread_reached_state(&thread_arg.state, STOPPED, 500);
cr_assert_eq(osr, os_resultTimeout, "deleting");
/* First release of the hdl should not unblock the thread. */
ut_handle_release(hdl, NULL);
osr = thread_reached_state(&thread_arg.state, STOPPED, 500);
cr_assert_eq(osr, os_resultTimeout, "deleting");
/* Second release of the hdl should unblock the thread. */
ut_handle_release(hdl, NULL);
osr = thread_reached_state(&thread_arg.state, STOPPED, 500);
cr_assert_eq(osr, os_resultSuccess, "deleting");
os_threadWaitExit(thread_id, NULL);
/* The handle is deleted within the thread. */
ut_handleserver_fini();
}