Initial contribution
This commit is contained in:
parent
7b5cc4fa59
commit
11d9ce37aa
580 changed files with 155133 additions and 162 deletions
27
src/util/CMakeLists.txt
Normal file
27
src/util/CMakeLists.txt
Normal 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()
|
357
src/util/include/util/ut_avl.h
Normal file
357
src/util/include/util/ut_avl.h
Normal 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 */
|
30
src/util/include/util/ut_crc.h
Normal file
30
src/util/include/util/ut_crc.h
Normal 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 */
|
32
src/util/include/util/ut_expand_envvars.h
Normal file
32
src/util/include/util/ut_expand_envvars.h
Normal 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
|
53
src/util/include/util/ut_fibheap.h
Normal file
53
src/util/include/util/ut_fibheap.h
Normal 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 */
|
242
src/util/include/util/ut_handleserver.h
Normal file
242
src/util/include/util/ut_handleserver.h
Normal 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 */
|
109
src/util/include/util/ut_hopscotch.h
Normal file
109
src/util/include/util/ut_hopscotch.h
Normal 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
|
68
src/util/include/util/ut_thread_pool.h
Normal file
68
src/util/include/util/ut_thread_pool.h
Normal 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 */
|
49
src/util/include/util/ut_xmlparser.h
Normal file
49
src/util/include/util/ut_xmlparser.h
Normal 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
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
75
src/util/src/ut_crc.c
Normal 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;
|
||||
}
|
228
src/util/src/ut_expand_envvars.c
Normal file
228
src/util/src/ut_expand_envvars.c
Normal 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
303
src/util/src/ut_fibheap.c
Normal 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);
|
||||
}
|
400
src/util/src/ut_handleserver.c
Normal file
400
src/util/src/ut_handleserver.c
Normal 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
951
src/util/src/ut_hopscotch.c
Normal 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;
|
||||
}
|
281
src/util/src/ut_thread_pool.c
Normal file
281
src/util/src/ut_thread_pool.c
Normal 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
705
src/util/src/ut_xmlparser.c
Normal 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);
|
||||
}
|
14
src/util/tests/CMakeLists.txt
Normal file
14
src/util/tests/CMakeLists.txt
Normal 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)
|
311
src/util/tests/handleserver.c
Normal file
311
src/util/tests/handleserver.c
Normal 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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue