move util library into ddsrt

As was the plan with the introduction of ddsrt; this includes renaming
the identifiers to match the capitalization style and removes old junk.

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2019-04-16 10:23:32 +02:00 committed by eboasson
parent 42500e7fb8
commit 712ca3149f
62 changed files with 1702 additions and 1869 deletions

View file

@ -78,6 +78,21 @@ list(APPEND sources
"${source_path}/strtod.c"
"${source_path}/strtol.c")
list(APPEND headers
"${source_path}/dds/ddsrt/avl.h"
"${source_path}/dds/ddsrt/expand_envvars.h"
"${source_path}/dds/ddsrt/fibheap.h"
"${source_path}/dds/ddsrt/hopscotch.h"
"${source_path}/dds/ddsrt/thread_pool.h")
list(APPEND sources
"${source_path}/avl.c"
"${source_path}/expand_envvars.c"
"${source_path}/fibheap.c"
"${source_path}/hopscotch.c"
"${source_path}/thread_pool.c"
"${source_path}/xmlparser.c")
# Not every target offers the same set of features. For embedded targets the
# set of features may even be different between builds. e.g. a FreeRTOS build
# could use the lightweight IP stack, but later change to FreeRTOS+TCP.

View file

@ -0,0 +1,359 @@
/*
* 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 DDSRT_AVL_H
#define DDSRT_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 <stdint.h>
#include <stdlib.h>
#include "dds/export.h"
#include "dds/ddsrt/attributes.h"
#if defined (__cplusplus)
extern "C" {
#endif
#define DDSRT_AVL_MAX_TREEHEIGHT (12 * sizeof (void *))
typedef int (*ddsrt_avl_compare_t) (const void *a, const void *b);
typedef int (*ddsrt_avl_compare_r_t) (const void *a, const void *b, void *arg);
typedef void (*ddsrt_avl_augment_t) (void *node, const void *left, const void *right);
typedef void (*ddsrt_avl_walk_t) (void *node, void *arg);
typedef void (*ddsrt_avl_const_walk_t) (const void *node, void *arg);
typedef struct ddsrt_avl_node {
struct ddsrt_avl_node *cs[2]; /* 0 = left, 1 = right */
struct ddsrt_avl_node *parent;
int height;
} ddsrt_avl_node_t;
#define DDSRT_AVL_TREEDEF_FLAG_INDKEY 1
#define DDSRT_AVL_TREEDEF_FLAG_R 2
#define DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS 4
typedef struct ddsrt_avl_treedef {
#if defined (__cplusplus)
ddsrt_avl_treedef() {}
#endif
size_t avlnodeoffset;
size_t keyoffset;
union {
ddsrt_avl_compare_t comparekk;
ddsrt_avl_compare_r_t comparekk_r;
} u;
ddsrt_avl_augment_t augment;
uint32_t flags;
void *cmp_arg; /* for _r variant */
} ddsrt_avl_treedef_t;
typedef struct ddsrt_avl_ctreedef {
ddsrt_avl_treedef_t t;
} ddsrt_avl_ctreedef_t;
typedef struct ddsrt_avl_tree {
ddsrt_avl_node_t *root;
} ddsrt_avl_tree_t;
typedef struct ddsrt_avl_ctree {
ddsrt_avl_tree_t t;
size_t count;
} ddsrt_avl_ctree_t;
typedef struct ddsrt_avl_path {
int depth; /* total depth of path */
int pnodeidx;
ddsrt_avl_node_t *parent; /* (nodeidx == 0 ? NULL : *(path[nodeidx-1])) */
ddsrt_avl_node_t **pnode[DDSRT_AVL_MAX_TREEHEIGHT];
} ddsrt_avl_path_t;
typedef struct ddsrt_avl_ipath {
ddsrt_avl_path_t p;
} ddsrt_avl_ipath_t;
typedef struct ddsrt_avl_dpath {
ddsrt_avl_path_t p;
} ddsrt_avl_dpath_t;
typedef struct ddsrt_avl_iter {
const ddsrt_avl_treedef_t *td;
ddsrt_avl_node_t *right;
ddsrt_avl_node_t **todop;
ddsrt_avl_node_t *todo[1+DDSRT_AVL_MAX_TREEHEIGHT];
} ddsrt_avl_iter_t;
typedef struct ddsrt_avl_citer {
ddsrt_avl_iter_t t;
} ddsrt_avl_citer_t;
/* avlnodeoffset and keyoffset must both be in [0,2**31-1] */
#define DDSRT_AVL_TREEDEF_INITIALIZER(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), 0, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY|DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_R(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_R, (cmparg) }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY|DDSRT_AVL_TREEDEF_FLAG_R, (cmparg) }
#define DDSRT_AVL_TREEDEF_INITIALIZER_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_R|DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, (cmparg) }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY|DDSRT_AVL_TREEDEF_FLAG_R|DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, (cmparg) }
/* Not maintaining # nodes */
DDS_EXPORT void ddsrt_avl_treedef_init (ddsrt_avl_treedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_t comparekk, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));
DDS_EXPORT void ddsrt_avl_treedef_init_r (ddsrt_avl_treedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_r_t comparekk_r, void *cmp_arg, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));
DDS_EXPORT void ddsrt_avl_init (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_free (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void (*freefun) (void *node)) ddsrt_nonnull((1,2));
DDS_EXPORT void ddsrt_avl_free_arg (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void (*freefun) (void *node, void *arg), void *arg) ddsrt_nonnull((1,2));
DDS_EXPORT void *ddsrt_avl_root (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_root_non_empty (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup_ipath (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup_dpath (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup_pred_eq (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup_succ_eq (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup_pred (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_lookup_succ (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_insert (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *node) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_delete (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *node) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_insert_ipath (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *node, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_delete_dpath (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *node, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_swap_node (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *oldn, void *newn) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_augment_update (const ddsrt_avl_treedef_t *td, void *node) ddsrt_nonnull_all;
DDS_EXPORT int ddsrt_avl_is_empty (const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT int ddsrt_avl_is_singleton (const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_find_min (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_find_max (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_find_pred (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *vnode) ddsrt_nonnull((1,2));
DDS_EXPORT void *ddsrt_avl_find_succ (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *vnode) ddsrt_nonnull((1,2));
DDS_EXPORT void ddsrt_avl_walk (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3));
DDS_EXPORT void ddsrt_avl_const_walk (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3));
DDS_EXPORT void ddsrt_avl_walk_range (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));
DDS_EXPORT void ddsrt_avl_const_walk_range (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));
DDS_EXPORT void ddsrt_avl_walk_range_reverse (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3));
DDS_EXPORT void ddsrt_avl_const_walk_range_reverse (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3));
DDS_EXPORT void *ddsrt_avl_iter_first (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_iter_t *iter) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_iter_succ_eq (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_iter_t *iter, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_iter_succ (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_iter_t *iter, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_iter_next (ddsrt_avl_iter_t *iter) ddsrt_nonnull_all;
/* Maintaining # nodes */
#define DDSRT_AVL_CTREEDEF_INITIALIZER(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_R (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_R_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
DDS_EXPORT void ddsrt_avl_ctreedef_init (ddsrt_avl_ctreedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_t comparekk, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));
DDS_EXPORT void ddsrt_avl_ctreedef_init_r (ddsrt_avl_ctreedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_r_t comparekk_r, void *cmp_arg, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));
DDS_EXPORT void ddsrt_avl_cinit (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_cfree (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void (*freefun) (void *node)) ddsrt_nonnull((1,2));
DDS_EXPORT void ddsrt_avl_cfree_arg (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void (*freefun) (void *node, void *arg), void *arg) ddsrt_nonnull((1,2));
DDS_EXPORT void *ddsrt_avl_croot (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_croot_non_empty (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup_ipath (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup_dpath (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup_pred_eq (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup_succ_eq (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup_pred (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_clookup_succ (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_cinsert (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *node) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_cdelete (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *node) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_cinsert_ipath (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *node, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_cdelete_dpath (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *node, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_cswap_node (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *oldn, void *newn) ddsrt_nonnull_all;
DDS_EXPORT void ddsrt_avl_caugment_update (const ddsrt_avl_ctreedef_t *td, void *node) ddsrt_nonnull_all;
DDS_EXPORT int ddsrt_avl_cis_empty (const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT int ddsrt_avl_cis_singleton (const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT size_t ddsrt_avl_ccount (const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_cfind_min (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_cfind_max (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_cfind_pred (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *vnode) ddsrt_nonnull((1,2));
DDS_EXPORT void *ddsrt_avl_cfind_succ (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *vnode) ddsrt_nonnull((1,2));
DDS_EXPORT void ddsrt_avl_cwalk (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3));
DDS_EXPORT void ddsrt_avl_cconst_walk (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3));
DDS_EXPORT void ddsrt_avl_cwalk_range (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));
DDS_EXPORT void ddsrt_avl_cconst_walk_range (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));
DDS_EXPORT void ddsrt_avl_cwalk_range_reverse (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));
DDS_EXPORT void ddsrt_avl_cconst_walk_range_reverse (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));
DDS_EXPORT void *ddsrt_avl_citer_first (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_citer_t *iter) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_citer_succ_eq (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_citer_t *iter, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_citer_succ (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_citer_t *iter, const void *key) ddsrt_nonnull_all;
DDS_EXPORT void *ddsrt_avl_citer_next (ddsrt_avl_citer_t *iter) ddsrt_nonnull_all;
#if defined (__cplusplus)
}
#endif
#endif /* DDSRT_AVL_H */

View file

@ -0,0 +1,31 @@
/*
* 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 DDSRT_EXPAND_ENVVARS_H
#define DDSRT_EXPAND_ENVVARS_H
#include "dds/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/* Expands ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, but not $X */
DDS_EXPORT char *ddsrt_expand_envvars(const char *string);
/* Expands $X, ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, $ and \ can be escaped with \ */
DDS_EXPORT char *ddsrt_expand_envvars_sh(const char *string);
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -0,0 +1,54 @@
/*
* 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 DDSRT_FIBHEAP_H
#define DDSRT_FIBHEAP_H
#include <stdint.h>
#include "dds/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
typedef struct ddsrt_fibheap_node {
struct ddsrt_fibheap_node *parent, *children;
struct ddsrt_fibheap_node *prev, *next;
unsigned mark: 1;
unsigned degree: 31;
} ddsrt_fibheap_node_t;
typedef struct ddsrt_fibheap_def {
uintptr_t offset;
int (*cmp) (const void *va, const void *vb);
} ddsrt_fibheap_def_t;
typedef struct ddsrt_fibheap {
ddsrt_fibheap_node_t *roots; /* points to root with min key value */
} ddsrt_fibheap_t;
#define DDSRT_FIBHEAPDEF_INITIALIZER(offset, cmp) { (offset), (cmp) }
DDS_EXPORT void ddsrt_fibheap_def_init (ddsrt_fibheap_def_t *fhdef, uintptr_t offset, int (*cmp) (const void *va, const void *vb));
DDS_EXPORT void ddsrt_fibheap_init (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh);
DDS_EXPORT void *ddsrt_fibheap_min (const ddsrt_fibheap_def_t *fhdef, const ddsrt_fibheap_t *fh);
DDS_EXPORT void ddsrt_fibheap_merge (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *a, ddsrt_fibheap_t *b);
DDS_EXPORT void ddsrt_fibheap_insert (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh, const void *vnode);
DDS_EXPORT void ddsrt_fibheap_delete (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh, const void *vnode);
DDS_EXPORT void *ddsrt_fibheap_extract_min (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh);
DDS_EXPORT void ddsrt_fibheap_decrease_key (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh, const void *vnode); /* to be called AFTER decreasing the key */
#if defined (__cplusplus)
}
#endif
#endif /* DDSRT_FIBHEAP_H */

View file

@ -0,0 +1,104 @@
/*
* 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 DDSRT_HOPSCOTCH_H
#define DDSRT_HOPSCOTCH_H
#include <stdint.h>
#include "dds/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/* Concurrent version */
struct ddsrt_chh;
struct ddsrt_chh_bucket;
struct ddsrt_chh_iter {
struct ddsrt_chh_bucket *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 (*ddsrt_hh_hash_fn) (const void *);
/*
* Hopscotch needs to be able to compare two elements.
* Returns 0 when not equal.
*/
typedef int (*ddsrt_hh_equals_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 (*ddsrt_hh_buckets_gc_fn) (void *);
DDS_EXPORT struct ddsrt_chh *ddsrt_chh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals, ddsrt_hh_buckets_gc_fn gc_buckets);
DDS_EXPORT void ddsrt_chh_free (struct ddsrt_chh * __restrict hh);
DDS_EXPORT void *ddsrt_chh_lookup (struct ddsrt_chh * __restrict rt, const void * __restrict template);
DDS_EXPORT int ddsrt_chh_add (struct ddsrt_chh * __restrict rt, const void * __restrict data);
DDS_EXPORT int ddsrt_chh_remove (struct ddsrt_chh * __restrict rt, const void * __restrict template);
DDS_EXPORT void ddsrt_chh_enum_unsafe (struct ddsrt_chh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
void *ddsrt_chh_iter_first (struct ddsrt_chh * __restrict rt, struct ddsrt_chh_iter *it);
void *ddsrt_chh_iter_next (struct ddsrt_chh_iter *it);
/* Sequential version */
struct ddsrt_hh;
struct ddsrt_hh_iter {
struct ddsrt_hh *hh;
uint32_t cursor;
};
DDS_EXPORT struct ddsrt_hh *ddsrt_hh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals);
DDS_EXPORT void ddsrt_hh_free (struct ddsrt_hh * __restrict hh);
DDS_EXPORT void *ddsrt_hh_lookup (const struct ddsrt_hh * __restrict rt, const void * __restrict template);
DDS_EXPORT int ddsrt_hh_add (struct ddsrt_hh * __restrict rt, const void * __restrict data);
DDS_EXPORT int ddsrt_hh_remove (struct ddsrt_hh * __restrict rt, const void * __restrict template);
DDS_EXPORT void ddsrt_hh_enum (struct ddsrt_hh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
DDS_EXPORT void *ddsrt_hh_iter_first (struct ddsrt_hh * __restrict rt, struct ddsrt_hh_iter * __restrict iter); /* may delete nodes */
DDS_EXPORT void *ddsrt_hh_iter_next (struct ddsrt_hh_iter * __restrict iter);
/* Sequential version, embedded data */
struct ddsrt_ehh;
struct ddsrt_ehh_iter {
struct ddsrt_ehh *hh;
uint32_t cursor;
};
DDS_EXPORT struct ddsrt_ehh *ddsrt_ehh_new (size_t elemsz, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals);
DDS_EXPORT void ddsrt_ehh_free (struct ddsrt_ehh * __restrict hh);
DDS_EXPORT void *ddsrt_ehh_lookup (const struct ddsrt_ehh * __restrict rt, const void * __restrict template);
DDS_EXPORT int ddsrt_ehh_add (struct ddsrt_ehh * __restrict rt, const void * __restrict data);
DDS_EXPORT int ddsrt_ehh_remove (struct ddsrt_ehh * __restrict rt, const void * __restrict template);
DDS_EXPORT void ddsrt_ehh_enum (struct ddsrt_ehh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
DDS_EXPORT void *ddsrt_ehh_iter_first (struct ddsrt_ehh * __restrict rt, struct ddsrt_ehh_iter * __restrict iter); /* may delete nodes */
DDS_EXPORT void *ddsrt_ehh_iter_next (struct ddsrt_ehh_iter * __restrict iter);
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -0,0 +1,69 @@
/*
* 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 DDSRT_THREAD_POOL_H
#define DDSRT_THREAD_POOL_H
#include <stdint.h>
#include "dds/export.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
#if defined (__cplusplus)
extern "C" {
#endif
typedef struct ddsrt_thread_pool_s *ddsrt_thread_pool;
/*
ddsrt_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.
*/
DDS_EXPORT ddsrt_thread_pool ddsrt_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) */
ddsrt_threadattr_t * attr /* Attributes used to create pool threads (can be NULL) */
);
/* ddsrt_thread_pool_free: Frees pool, destroying threads. */
DDS_EXPORT void ddsrt_thread_pool_free (ddsrt_thread_pool pool);
/* ddsrt_thread_pool_purge: Purge threads from pool back to initial set. */
DDS_EXPORT void ddsrt_thread_pool_purge (ddsrt_thread_pool pool);
/*
ddsrt_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 DDS_RETCODE_TRY_AGAIN is returned.
*/
DDS_EXPORT dds_retcode_t ddsrt_thread_pool_submit
(
ddsrt_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 /* DDSRT_THREAD_POOL_H */

View file

@ -0,0 +1,52 @@
/*
* 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 DDSRT_XMLPARSER_H
#define DDSRT_XMLPARSER_H
#include <stdint.h>
#include "dds/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
typedef int (*ddsrt_xmlp_proc_elem_open_t) (void *varg, uintptr_t parentinfo, uintptr_t *eleminfo, const char *name);
typedef int (*ddsrt_xmlp_proc_attr_t) (void *varg, uintptr_t eleminfo, const char *name, const char *value);
typedef int (*ddsrt_xmlp_proc_elem_data_t) (void *varg, uintptr_t eleminfo, const char *data);
typedef int (*ddsrt_xmlp_proc_elem_close_t) (void *varg, uintptr_t eleminfo);
typedef void (*ddsrt_xmlp_error) (void *varg, const char *msg, int line);
struct ddsrt_xmlp_callbacks {
ddsrt_xmlp_proc_elem_open_t elem_open;
ddsrt_xmlp_proc_attr_t attr;
ddsrt_xmlp_proc_elem_data_t elem_data;
ddsrt_xmlp_proc_elem_close_t elem_close;
ddsrt_xmlp_error error;
};
struct ddsrt_xmlp_state;
DDS_EXPORT struct ddsrt_xmlp_state *ddsrt_xmlp_new_file (FILE *fp, void *varg, const struct ddsrt_xmlp_callbacks *cb);
DDS_EXPORT struct ddsrt_xmlp_state *ddsrt_xmlp_new_string (const char *string, void *varg, const struct ddsrt_xmlp_callbacks *cb);
DDS_EXPORT void ddsrt_xmlp_set_requireEOF (struct ddsrt_xmlp_state *st, int require_eof);
DDS_EXPORT size_t ddsrt_xmlp_get_bufpos (const struct ddsrt_xmlp_state *st);
DDS_EXPORT void ddsrt_xmlp_free (struct ddsrt_xmlp_state *st);
DDS_EXPORT int ddsrt_xmlp_parse (struct ddsrt_xmlp_state *st);
DDS_EXPORT int ddsrt_xmlUnescapeInsitu (char *buffer, size_t *n);
#if defined (__cplusplus)
}
#endif
#endif

1148
src/ddsrt/src/avl.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
/*
* 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 "dds/ddsrt/environ.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/log.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/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 = ddsrt_realloc (*dst, *sz);
}
(*dst)[*pos] = c;
(*pos)++;
}
static char *expand_env (const char *name, char op, const char *alt, expand_fn expand)
{
char *env = NULL;
(void)ddsrt_getenv (name, &env);
switch (op)
{
case 0:
return ddsrt_strdup (env ? env : "");
case '-':
return env && *env ? ddsrt_strdup (env) : expand (alt);
case '?':
if (env && *env) {
return ddsrt_strdup (env);
} else {
char *altx = expand (alt);
DDS_ERROR("%s: %s\n", name, altx);
ddsrt_free (altx);
return NULL;
}
case '+':
return env && *env ? expand (alt) : ddsrt_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 = ddsrt_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);
ddsrt_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:
ddsrt_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) {
ddsrt_free(name);
goto err;
}
}
(*src)++;
}
if (**src == 0) {
ddsrt_free(name);
goto err;
}
assert (**src == '}');
alt = ddsrt_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);
ddsrt_free (alt);
ddsrt_free (name);
return x;
}
err:
DDS_ERROR("%*.*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 = ddsrt_malloc ((size_t) (*src - start) + 1);
memcpy (name, start, (size_t) (*src - start));
name[*src - start] = 0;
x = expand_env (name, 0, NULL, expand);
ddsrt_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 *ddsrt_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 = ddsrt_malloc (sz);
while (*src) {
if (*src == '\\') {
src++;
if (*src == 0) {
DDS_ERROR("%s: incomplete escape at end of string\n", src0);
ddsrt_free(dst);
return NULL;
}
expand_append (&dst, &sz, &pos, *src++);
} else if (*src == '$') {
char *x, *xp;
src++;
if (*src == 0) {
DDS_ERROR("%s: incomplete variable expansion at end of string\n", src0);
ddsrt_free(dst);
return NULL;
} else if (*src == '{') {
x = expand_envbrace (&src, &ddsrt_expand_envvars_sh);
} else if (isalnum ((unsigned char) *src) || *src == '_') {
x = expand_envsimple (&src, &ddsrt_expand_envvars_sh);
} else {
x = expand_envchar (&src, &ddsrt_expand_envvars_sh);
}
if (x == NULL) {
ddsrt_free(dst);
return NULL;
}
xp = x;
while (*xp) {
expand_append (&dst, &sz, &pos, *xp++);
}
ddsrt_free (x);
} else {
expand_append (&dst, &sz, &pos, *src++);
}
}
expand_append (&dst, &sz, &pos, 0);
return dst;
}
char *ddsrt_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 = ddsrt_malloc (sz);
while (*src) {
if (*src == '$' && *(src + 1) == '{') {
char *x, *xp;
src++;
x = expand_envbrace (&src, &ddsrt_expand_envvars);
if (x == NULL) {
ddsrt_free(dst);
return NULL;
}
xp = x;
while (*xp) {
expand_append (&dst, &sz, &pos, *xp++);
}
ddsrt_free (x);
} else {
expand_append (&dst, &sz, &pos, *src++);
}
}
expand_append (&dst, &sz, &pos, 0);
return dst;
}

304
src/ddsrt/src/fibheap.c Normal file
View file

@ -0,0 +1,304 @@
/*
* 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 "dds/ddsrt/misc.h"
#include "dds/ddsrt/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 ddsrt_fibheap_def_t *fhdef, const ddsrt_fibheap_node_t *a, const ddsrt_fibheap_node_t *b)
{
return fhdef->cmp ((const char *) a - fhdef->offset, (const char *) b - fhdef->offset);
}
void ddsrt_fibheap_def_init (ddsrt_fibheap_def_t *fhdef, uintptr_t offset, int (*cmp) (const void *va, const void *vb))
{
fhdef->offset = offset;
fhdef->cmp = cmp;
}
void ddsrt_fibheap_init (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh)
{
DDSRT_UNUSED_ARG(fhdef);
fh->roots = NULL;
}
void *ddsrt_fibheap_min (const ddsrt_fibheap_def_t *fhdef, const ddsrt_fibheap_t *fh)
{
if (fh->roots) {
return (void *) ((char *) fh->roots - fhdef->offset);
} else {
return NULL;
}
}
static void ddsrt_fibheap_merge_nonempty_list (ddsrt_fibheap_node_t **markptr, ddsrt_fibheap_node_t *list)
{
assert (list != NULL);
if (*markptr == NULL) {
*markptr = list;
} else {
ddsrt_fibheap_node_t * const mark = *markptr;
ddsrt_fibheap_node_t * const old_mark_next = mark->next;
ddsrt_fibheap_node_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 ddsrt_fibheap_merge_into (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *a, ddsrt_fibheap_node_t * const br)
{
if (br == NULL) {
return;
} else if (a->roots == NULL) {
a->roots = br;
} else {
const int c = cmp (fhdef, br, a->roots);
ddsrt_fibheap_merge_nonempty_list (&a->roots, br);
if (c < 0)
a->roots = br;
}
}
void ddsrt_fibheap_merge (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *a, ddsrt_fibheap_t *b)
{
/* merges nodes from b into a, thereafter, b is empty */
ddsrt_fibheap_merge_into (fhdef, a, b->roots);
b->roots = NULL;
}
void ddsrt_fibheap_insert (const ddsrt_fibheap_def_t *fhdef, ddsrt_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 */
ddsrt_fibheap_node_t *node = (ddsrt_fibheap_node_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 */
ddsrt_fibheap_merge_into (fhdef, fh, node);
}
static void ddsrt_fibheap_add_as_child (ddsrt_fibheap_node_t *parent, ddsrt_fibheap_node_t *child)
{
parent->degree++;
child->parent = parent;
child->prev = child->next = child;
ddsrt_fibheap_merge_nonempty_list (&parent->children, child);
}
static void ddsrt_fibheap_delete_one_from_list (ddsrt_fibheap_node_t **markptr, ddsrt_fibheap_node_t *node)
{
if (node->next == node) {
*markptr = NULL;
} else {
ddsrt_fibheap_node_t * const node_prev = node->prev;
ddsrt_fibheap_node_t * const node_next = node->next;
node_prev->next = node_next;
node_next->prev = node_prev;
if (*markptr == node) {
*markptr = node_next;
}
}
}
void *ddsrt_fibheap_extract_min (const ddsrt_fibheap_def_t *fhdef, ddsrt_fibheap_t *fh)
{
ddsrt_fibheap_node_t *roots[MAX_DEGREE + 1];
ddsrt_fibheap_node_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 */
ddsrt_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) {
ddsrt_fibheap_node_t * const mark = min->children;
ddsrt_fibheap_node_t *n = mark;
do {
n->parent = NULL;
n->mark = 0;
n = n->next;
} while (n != mark);
ddsrt_fibheap_merge_nonempty_list (&fh->roots, min->children);
}
/* iteratively merge roots of equal degree, completely messing up
fh->roots, ... */
{
ddsrt_fibheap_node_t *const mark = fh->roots;
ddsrt_fibheap_node_t *n = mark;
do {
ddsrt_fibheap_node_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;
ddsrt_fibheap_node_t *u, *v;
if (cmp (fhdef, roots[degree], n) < 0) {
u = roots[degree]; v = n;
} else {
u = n; v = roots[degree];
}
roots[degree] = NULL;
ddsrt_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 */
{
ddsrt_fibheap_node_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]) {
ddsrt_fibheap_node_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 ddsrt_fibheap_cutnode (ddsrt_fibheap_t *fh, ddsrt_fibheap_node_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) {
ddsrt_fibheap_node_t *parent = node->parent;
assert (parent->degree > 0);
ddsrt_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 */
ddsrt_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 ddsrt_fibheap_decrease_key (const ddsrt_fibheap_def_t *fhdef, ddsrt_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 */
ddsrt_fibheap_node_t *node = (ddsrt_fibheap_node_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 */
ddsrt_fibheap_cutnode (fh, node);
}
if (cmp (fhdef, node, fh->roots) < 0) {
fh->roots = node;
}
}
}
void ddsrt_fibheap_delete (const ddsrt_fibheap_def_t *fhdef, ddsrt_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 */
ddsrt_fibheap_node_t *node = (ddsrt_fibheap_node_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) {
ddsrt_fibheap_cutnode (fh, node);
}
fh->roots = node;
(void) ddsrt_fibheap_extract_min (fhdef, fh);
}

943
src/ddsrt/src/hopscotch.c Normal file
View file

@ -0,0 +1,943 @@
/*
* 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 "dds/ddsrt/atomics.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/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 ddsrt_chh_bucket {
ddsrt_atomic_uint32_t hopinfo;
ddsrt_atomic_uint32_t timestamp;
ddsrt_atomic_uint32_t lock;
ddsrt_atomic_voidp_t data;
};
struct ddsrt_chh_bucket_array {
uint32_t size; /* power of 2 */
struct ddsrt_chh_bucket bs[];
};
struct ddsrt_chh_backing_lock {
ddsrt_mutex_t lock;
ddsrt_cond_t cv;
};
struct ddsrt_chh {
ddsrt_atomic_voidp_t buckets; /* struct ddsrt_chh_bucket_array * */
struct ddsrt_chh_backing_lock backingLocks[N_BACKING_LOCKS];
ddsrt_hh_hash_fn hash;
ddsrt_hh_equals_fn equals;
ddsrt_rwlock_t resize_locks[N_RESIZE_LOCKS];
ddsrt_hh_buckets_gc_fn gc_buckets;
};
#define CHH_MAX_TRIES 4
#define CHH_BUSY ((void *) 1)
static int ddsrt_chh_data_valid_p (void *data)
{
return data != NULL && data != CHH_BUSY;
}
static int ddsrt_chh_init (struct ddsrt_chh *rt, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals, ddsrt_hh_buckets_gc_fn gc_buckets)
{
uint32_t size;
uint32_t i;
struct ddsrt_chh_bucket_array *buckets;
size = HH_HOP_RANGE;
while (size < init_size) {
size *= 2;
}
rt->hash = hash;
rt->equals = equals;
rt->gc_buckets = gc_buckets;
buckets = ddsrt_malloc (offsetof (struct ddsrt_chh_bucket_array, bs) + size * sizeof (*buckets->bs));
ddsrt_atomic_stvoidp (&rt->buckets, buckets);
buckets->size = size;
for (i = 0; i < size; i++) {
struct ddsrt_chh_bucket *b = &buckets->bs[i];
ddsrt_atomic_st32 (&b->hopinfo, 0);
ddsrt_atomic_st32 (&b->timestamp, 0);
ddsrt_atomic_st32 (&b->lock, 0);
ddsrt_atomic_stvoidp (&b->data, NULL);
}
for (i = 0; i < N_BACKING_LOCKS; i++) {
struct ddsrt_chh_backing_lock *s = &rt->backingLocks[i];
ddsrt_mutex_init (&s->lock);
ddsrt_cond_init (&s->cv);
}
for (i = 0; i < N_RESIZE_LOCKS; i++) {
ddsrt_rwlock_init (&rt->resize_locks[i]);
}
return 0;
}
static void ddsrt_chh_fini (struct ddsrt_chh *rt)
{
int i;
ddsrt_free (ddsrt_atomic_ldvoidp (&rt->buckets));
for (i = 0; i < N_BACKING_LOCKS; i++) {
struct ddsrt_chh_backing_lock *s = &rt->backingLocks[i];
ddsrt_cond_destroy (&s->cv);
ddsrt_mutex_destroy (&s->lock);
}
for (i = 0; i < N_RESIZE_LOCKS; i++) {
ddsrt_rwlock_destroy (&rt->resize_locks[i]);
}
}
struct ddsrt_chh *ddsrt_chh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals, ddsrt_hh_buckets_gc_fn gc_buckets)
{
struct ddsrt_chh *hh = ddsrt_malloc (sizeof (*hh));
if (ddsrt_chh_init (hh, init_size, hash, equals, gc_buckets) < 0) {
ddsrt_free (hh);
return NULL;
} else {
return hh;
}
}
void ddsrt_chh_free (struct ddsrt_chh * __restrict hh)
{
ddsrt_chh_fini (hh);
ddsrt_free (hh);
}
#define LOCKBIT ((uint32_t)1 << 31)
static void ddsrt_chh_lock_bucket (struct ddsrt_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 ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * const b = &bsary->bs[bidx];
struct ddsrt_chh_backing_lock * const s = &rt->backingLocks[bidx % N_BACKING_LOCKS];
uint32_t o, n;
fastpath_retry:
o = ddsrt_atomic_ld32 (&b->lock);
if ((o & LOCKBIT) == 0) {
n = o | LOCKBIT;
} else {
n = o + 1;
}
if (!ddsrt_atomic_cas32 (&b->lock, o, n)) {
goto fastpath_retry;
}
if ((o & LOCKBIT) == 0) {
ddsrt_atomic_fence ();
} else {
ddsrt_mutex_lock (&s->lock);
do {
while ((o = ddsrt_atomic_ld32 (&b->lock)) & LOCKBIT) {
ddsrt_cond_wait (&s->cv, &s->lock);
}
} while (!ddsrt_atomic_cas32 (&b->lock, o, (o - 1) | LOCKBIT));
ddsrt_mutex_unlock (&s->lock);
}
}
static void ddsrt_chh_unlock_bucket (struct ddsrt_chh *rt, uint32_t bidx)
{
struct ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * const b = &bsary->bs[bidx];
struct ddsrt_chh_backing_lock * const s = &rt->backingLocks[bidx % N_BACKING_LOCKS];
uint32_t o, n;
retry:
o = ddsrt_atomic_ld32 (&b->lock);
assert (o & LOCKBIT);
n = o & ~LOCKBIT;
if (!ddsrt_atomic_cas32 (&b->lock, o, n)) {
goto retry;
}
if (n == 0) {
ddsrt_atomic_fence ();
} else {
ddsrt_mutex_lock (&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. */
ddsrt_cond_broadcast (&s->cv);
ddsrt_mutex_unlock (&s->lock);
}
}
static void *ddsrt_chh_lookup_internal (struct ddsrt_chh_bucket_array const * const bsary, ddsrt_hh_equals_fn equals, const uint32_t bucket, const void *template)
{
struct ddsrt_chh_bucket 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 = ddsrt_atomic_ld32 (&bs[bucket].timestamp);
ddsrt_atomic_fence_ldld ();
hopinfo = ddsrt_atomic_ld32 (&bs[bucket].hopinfo);
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
const uint32_t bidx = (bucket + idx) & idxmask;
void *data = ddsrt_atomic_ldvoidp (&bs[bidx].data);
if (ddsrt_chh_data_valid_p (data) && equals (data, template)) {
return data;
}
}
ddsrt_atomic_fence_ldld ();
} while (timestamp != ddsrt_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 = ddsrt_atomic_ldvoidp (&bs[bidx].data);
if (ddsrt_chh_data_valid_p (data) && equals (data, template)) {
return data;
}
}
}
return NULL;
}
#define ddsrt_atomic_rmw32_nonatomic(var_, tmp_, expr_) do { \
ddsrt_atomic_uint32_t *var__ = (var_); \
uint32_t tmp_ = ddsrt_atomic_ld32 (var__); \
ddsrt_atomic_st32 (var__, (expr_)); \
} while (0)
void *ddsrt_chh_lookup (struct ddsrt_chh * __restrict rt, const void * __restrict template)
{
struct ddsrt_chh_bucket_array const * const bsary = ddsrt_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 ddsrt_chh_lookup_internal (bsary, rt->equals, bucket, template);
}
static uint32_t ddsrt_chh_find_closer_free_bucket (struct ddsrt_chh *rt, uint32_t free_bucket, uint32_t *free_distance)
{
struct ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * 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 = ddsrt_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) {
ddsrt_chh_lock_bucket (rt, move_bucket);
if (start_hop_info == ddsrt_atomic_ld32 (&bs[move_bucket].hopinfo))
{
uint32_t new_free_bucket = (move_bucket + move_free_distance) & idxmask;
ddsrt_atomic_rmw32_nonatomic (&bs[move_bucket].hopinfo, x, x | (1u << free_dist));
ddsrt_atomic_stvoidp (&bs[free_bucket].data, ddsrt_atomic_ldvoidp (&bs[new_free_bucket].data));
ddsrt_atomic_rmw32_nonatomic (&bs[move_bucket].timestamp, x, x + 1);
ddsrt_atomic_fence ();
ddsrt_atomic_stvoidp (&bs[new_free_bucket].data, CHH_BUSY);
ddsrt_atomic_rmw32_nonatomic (&bs[move_bucket].hopinfo, x, x & ~(1u << move_free_distance));
*free_distance -= free_dist - move_free_distance;
ddsrt_chh_unlock_bucket (rt, move_bucket);
return new_free_bucket;
}
ddsrt_chh_unlock_bucket (rt, move_bucket);
}
move_bucket = (move_bucket + 1) & idxmask;
}
return NOT_A_BUCKET;
}
static void ddsrt_chh_resize (struct ddsrt_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 ddsrt_chh_bucket_array * const bsary0 = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * const bs0 = bsary0->bs;
struct ddsrt_chh_bucket_array *bsary1;
struct ddsrt_chh_bucket *bs1;
uint32_t i, idxmask0, idxmask1;
assert (bsary0->size > 0);
bsary1 = ddsrt_malloc (offsetof (struct ddsrt_chh_bucket_array, bs) + 2 * bsary0->size * sizeof (*bsary1->bs));
bsary1->size = 2 * bsary0->size;
bs1 = bsary1->bs;
for (i = 0; i < bsary1->size; i++) {
ddsrt_atomic_st32 (&bs1[i].hopinfo, 0);
ddsrt_atomic_st32 (&bs1[i].timestamp, 0);
ddsrt_atomic_st32 (&bs1[i].lock, 0);
ddsrt_atomic_stvoidp (&bs1[i].data, NULL);
}
idxmask0 = bsary0->size - 1;
idxmask1 = bsary1->size - 1;
for (i = 0; i < bsary0->size; i++) {
void *data = ddsrt_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);
ddsrt_atomic_rmw32_nonatomic (&bs1[new_start_bucket].hopinfo, x, x | (1u << dist));
ddsrt_atomic_stvoidp (&bs1[newb].data, data);
}
}
ddsrt_atomic_fence ();
ddsrt_atomic_stvoidp (&rt->buckets, bsary1);
rt->gc_buckets (bsary0);
}
int ddsrt_chh_add (struct ddsrt_chh * __restrict rt, const void * __restrict data)
{
const uint32_t hash = rt->hash (data);
uint32_t size;
ddsrt_rwlock_read (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
{
struct ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * 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;
ddsrt_chh_lock_bucket (rt, start_bucket);
if (ddsrt_chh_lookup_internal (bsary, rt->equals, start_bucket, data)) {
ddsrt_chh_unlock_bucket (rt, start_bucket);
ddsrt_rwlock_unlock (&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 (ddsrt_atomic_ldvoidp (&bs[free_bucket].data) == NULL &&
ddsrt_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));
ddsrt_atomic_rmw32_nonatomic (&bs[start_bucket].hopinfo, x, x | (1u << free_distance));
ddsrt_atomic_fence ();
ddsrt_atomic_stvoidp (&bs[free_bucket].data, (void *) data);
ddsrt_chh_unlock_bucket (rt, start_bucket);
ddsrt_rwlock_unlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 1;
}
free_bucket = ddsrt_chh_find_closer_free_bucket (rt, free_bucket, &free_distance);
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
} while (free_bucket != NOT_A_BUCKET);
}
ddsrt_chh_unlock_bucket (rt, start_bucket);
}
ddsrt_rwlock_unlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
{
int i;
struct ddsrt_chh_bucket_array *bsary1;
for (i = 0; i < N_RESIZE_LOCKS; i++) {
ddsrt_rwlock_write (&rt->resize_locks[i]);
}
/* another thread may have sneaked past and grown the hash table */
bsary1 = ddsrt_atomic_ldvoidp (&rt->buckets);
if (bsary1->size == size) {
ddsrt_chh_resize (rt);
}
for (i = 0; i < N_RESIZE_LOCKS; i++) {
ddsrt_rwlock_unlock (&rt->resize_locks[i]);
}
}
return ddsrt_chh_add (rt, data);
}
int ddsrt_chh_remove (struct ddsrt_chh * __restrict rt, const void * __restrict template)
{
const uint32_t hash = rt->hash (template);
ddsrt_rwlock_read (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
{
struct ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * 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;
ddsrt_chh_lock_bucket (rt, bucket);
hopinfo = ddsrt_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 = ddsrt_atomic_ldvoidp (&bs[bidx].data);
if (ddsrt_chh_data_valid_p (data) && rt->equals (data, template)) {
ddsrt_atomic_stvoidp (&bs[bidx].data, NULL);
ddsrt_atomic_rmw32_nonatomic (&bs[bucket].hopinfo, x, x & ~(1u << idx));
ddsrt_chh_unlock_bucket (rt, bucket);
ddsrt_rwlock_unlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 1;
}
}
}
ddsrt_chh_unlock_bucket (rt, bucket);
}
ddsrt_rwlock_unlock (&rt->resize_locks[hash % N_RESIZE_LOCKS]);
return 0;
}
void ddsrt_chh_enum_unsafe (struct ddsrt_chh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg)
{
struct ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
struct ddsrt_chh_bucket * const bs = bsary->bs;
uint32_t i;
for (i = 0; i < bsary->size; i++) {
void *data = ddsrt_atomic_ldvoidp (&bs[i].data);
if (data && data != CHH_BUSY) {
f (data, f_arg);
}
}
}
void *ddsrt_chh_iter_next (struct ddsrt_chh_iter *it)
{
uint32_t i;
for (i = it->cursor; i < it->size; i++) {
void *data = ddsrt_atomic_ldvoidp (&it->bs[i].data);
if (data && data != CHH_BUSY) {
it->cursor = i+1;
return data;
}
}
return NULL;
}
void *ddsrt_chh_iter_first (struct ddsrt_chh * __restrict rt, struct ddsrt_chh_iter *it)
{
struct ddsrt_chh_bucket_array * const bsary = ddsrt_atomic_ldvoidp (&rt->buckets);
it->bs = bsary->bs;
it->size = bsary->size;
it->cursor = 0;
return ddsrt_chh_iter_next (it);
}
/************* SEQUENTIAL VERSION ***************/
struct ddsrt_hh_bucket {
uint32_t hopinfo;
void *data;
};
struct ddsrt_hh {
uint32_t size; /* power of 2 */
struct ddsrt_hh_bucket *buckets;
ddsrt_hh_hash_fn hash;
ddsrt_hh_equals_fn equals;
};
static void ddsrt_hh_init (struct ddsrt_hh *rt, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_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->buckets = ddsrt_malloc (size * sizeof (*rt->buckets));
for (i = 0; i < size; i++) {
rt->buckets[i].hopinfo = 0;
rt->buckets[i].data = NULL;
}
}
static void ddsrt_hh_fini (struct ddsrt_hh *rt)
{
ddsrt_free (rt->buckets);
}
struct ddsrt_hh *ddsrt_hh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
{
struct ddsrt_hh *hh = ddsrt_malloc (sizeof (*hh));
ddsrt_hh_init (hh, init_size, hash, equals);
return hh;
}
void ddsrt_hh_free (struct ddsrt_hh * __restrict hh)
{
ddsrt_hh_fini (hh);
ddsrt_free (hh);
}
static void *ddsrt_hh_lookup_internal (const struct ddsrt_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 *ddsrt_hh_lookup (const struct ddsrt_hh * __restrict rt, const void * __restrict template)
{
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = rt->size - 1;
const uint32_t bucket = hash & idxmask;
return ddsrt_hh_lookup_internal (rt, bucket, template);
}
static uint32_t ddsrt_hh_find_closer_free_bucket (struct ddsrt_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 ddsrt_hh_resize (struct ddsrt_hh *rt)
{
struct ddsrt_hh_bucket *bs1;
uint32_t i, idxmask0, idxmask1;
bs1 = ddsrt_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;
}
}
ddsrt_free (rt->buckets);
rt->size *= 2;
rt->buckets = bs1;
}
int ddsrt_hh_add (struct ddsrt_hh * __restrict rt, const void * __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 (ddsrt_hh_lookup_internal (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 = ddsrt_hh_find_closer_free_bucket (rt, free_bucket, &free_distance);
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
} while (free_bucket != NOT_A_BUCKET);
}
ddsrt_hh_resize (rt);
return ddsrt_hh_add (rt, data);
}
int ddsrt_hh_remove (struct ddsrt_hh * __restrict rt, const void * __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 ddsrt_hh_enum (struct ddsrt_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 *ddsrt_hh_iter_first (struct ddsrt_hh * __restrict rt, struct ddsrt_hh_iter * __restrict iter)
{
iter->hh = rt;
iter->cursor = 0;
return ddsrt_hh_iter_next (iter);
}
void *ddsrt_hh_iter_next (struct ddsrt_hh_iter * __restrict iter)
{
struct ddsrt_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 ddsrt_ehh_bucket {
uint32_t hopinfo;
uint32_t inuse;
char data[];
};
struct ddsrt_ehh {
uint32_t size; /* power of 2 */
size_t elemsz;
size_t bucketsz;
char *buckets; /* ehhBucket, but embedded data messes up the layout */
ddsrt_hh_hash_fn hash;
ddsrt_hh_equals_fn equals;
};
static void ddsrt_ehh_init (struct ddsrt_ehh *rt, size_t elemsz, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_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 ddsrt_ehh_bucket) + ((elemsz+7) & ~(size_t)7);
rt->buckets = ddsrt_malloc (size * rt->bucketsz);
for (i = 0; i < size; i++) {
struct ddsrt_ehh_bucket *b = (struct ddsrt_ehh_bucket *) (rt->buckets + i * rt->bucketsz);
b->hopinfo = 0;
b->inuse = 0;
}
}
static void ddsrt_ehh_fini (struct ddsrt_ehh *rt)
{
ddsrt_free (rt->buckets);
}
struct ddsrt_ehh *ddsrt_ehh_new (size_t elemsz, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
{
struct ddsrt_ehh *hh = ddsrt_malloc (sizeof (*hh));
ddsrt_ehh_init (hh, elemsz, init_size, hash, equals);
return hh;
}
void ddsrt_ehh_free (struct ddsrt_ehh * __restrict hh)
{
ddsrt_ehh_fini (hh);
ddsrt_free (hh);
}
static void *ddsrt_ehh_lookup_internal (const struct ddsrt_ehh *rt, uint32_t bucket, const void *template)
{
const struct ddsrt_ehh_bucket *b = (const struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_bucket *) (rt->buckets + bucket * rt->bucketsz);
if (b->inuse && rt->equals (b->data, template)) {
return (void *) b->data;
}
}
} while (hopinfo != 0);
return NULL;
}
void *ddsrt_ehh_lookup (const struct ddsrt_ehh * __restrict rt, const void * __restrict template)
{
const uint32_t hash = rt->hash (template);
const uint32_t idxmask = rt->size - 1;
const uint32_t bucket = hash & idxmask;
return ddsrt_ehh_lookup_internal (rt, bucket, template);
}
static uint32_t ddsrt_ehh_find_closer_free_bucket (struct ddsrt_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 ddsrt_ehh_bucket * const mb = (struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_bucket * const fb = (struct ddsrt_ehh_bucket *) (rt->buckets + free_bucket * rt->bucketsz);
struct ddsrt_ehh_bucket * const nfb = (struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_resize (struct ddsrt_ehh *rt)
{
char *bs1;
uint32_t i, idxmask0, idxmask1;
bs1 = ddsrt_malloc (2 * rt->size * rt->bucketsz);
for (i = 0; i < 2 * rt->size; i++) {
struct ddsrt_ehh_bucket *b = (struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_bucket const * const b = (struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_bucket * const nsb = (struct ddsrt_ehh_bucket *) (bs1 + new_start_bucket * rt->bucketsz);
struct ddsrt_ehh_bucket * const nb = (struct ddsrt_ehh_bucket *) (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);
}
}
ddsrt_free (rt->buckets);
rt->size *= 2;
rt->buckets = bs1;
}
int ddsrt_ehh_add (struct ddsrt_ehh * __restrict rt, const void * __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 (ddsrt_ehh_lookup_internal (rt, start_bucket, data)) {
return 0;
}
free_bucket = start_bucket;
for (free_distance = 0; free_distance < HH_ADD_RANGE; free_distance++) {
struct ddsrt_ehh_bucket const * const fb = (struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_bucket * const sb = (struct ddsrt_ehh_bucket *) (rt->buckets + start_bucket * rt->bucketsz);
struct ddsrt_ehh_bucket * const fb = (struct ddsrt_ehh_bucket *) (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 (ddsrt_ehh_lookup_internal (rt, start_bucket, data));
return 1;
}
free_bucket = ddsrt_ehh_find_closer_free_bucket (rt, free_bucket, &free_distance);
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
} while (free_bucket != NOT_A_BUCKET);
}
ddsrt_ehh_resize (rt);
return ddsrt_ehh_add (rt, data);
}
int ddsrt_ehh_remove (struct ddsrt_ehh * __restrict rt, const void * __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 ddsrt_ehh_bucket *sb;
uint32_t idx;
sb = (struct ddsrt_ehh_bucket *) (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 ddsrt_ehh_bucket *b = (struct ddsrt_ehh_bucket *) (rt->buckets + bidx * rt->bucketsz);
if (b->inuse && rt->equals (b->data, template)) {
assert (ddsrt_ehh_lookup_internal(rt, bucket, template));
b->inuse = 0;
sb->hopinfo &= ~(1u << idx);
return 1;
}
}
}
assert (!ddsrt_ehh_lookup_internal(rt, bucket, template));
return 0;
}
void ddsrt_ehh_enum (struct ddsrt_ehh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg)
{
uint32_t i;
for (i = 0; i < rt->size; i++) {
struct ddsrt_ehh_bucket *b = (struct ddsrt_ehh_bucket *) (rt->buckets + i * rt->bucketsz);
if (b->inuse) {
f (b->data, f_arg);
}
}
}
void *ddsrt_ehh_iter_first (struct ddsrt_ehh * __restrict rt, struct ddsrt_ehh_iter * __restrict iter)
{
iter->hh = rt;
iter->cursor = 0;
return ddsrt_ehh_iter_next (iter);
}
void *ddsrt_ehh_iter_next (struct ddsrt_ehh_iter * __restrict iter)
{
struct ddsrt_ehh *rt = iter->hh;
while (iter->cursor < rt->size) {
struct ddsrt_ehh_bucket *b = (struct ddsrt_ehh_bucket *) (rt->buckets + iter->cursor * rt->bucketsz);
iter->cursor++;
if (b->inuse) {
return b->data;
}
}
return NULL;
}

285
src/ddsrt/src/thread_pool.c Normal file
View file

@ -0,0 +1,285 @@
/*
* 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 "dds/ddsrt/io.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/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 ddsrt_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 */
ddsrt_threadattr_t m_attr; /* Thread creation attribute */
ddsrt_cond_t m_cv; /* Thread wait semaphore */
ddsrt_mutex_t m_mutex; /* Pool guard mutex */
};
static uint32_t ddsrt_thread_start_fn (void * arg)
{
ddsi_work_queue_job_t job;
ddsrt_thread_pool pool = (ddsrt_thread_pool) arg;
/* Thread loops, pulling jobs from queue */
ddsrt_mutex_lock (&pool->m_mutex);
while (pool->m_jobs != NULL) {
/* Wait for job */
ddsrt_cond_wait (&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--;
ddsrt_mutex_unlock (&pool->m_mutex);
/* Do job */
(job->m_fn) (job->m_arg);
/* Put job back on free list */
ddsrt_mutex_lock (&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 */
ddsrt_cond_broadcast (&pool->m_cv);
}
ddsrt_mutex_unlock (&pool->m_mutex);
return 0;
}
static dds_retcode_t ddsrt_thread_pool_new_thread (ddsrt_thread_pool pool)
{
static unsigned char pools = 0; /* Pool counter - TODO make atomic */
char name [64];
ddsrt_thread_t id;
dds_retcode_t res;
(void) snprintf (name, sizeof (name), "OSPL-%u-%u", pools++, pool->m_count++);
res = ddsrt_thread_create (&id, name, &pool->m_attr, &ddsrt_thread_start_fn, pool);
if (res == DDS_RETCODE_OK)
{
ddsrt_mutex_lock (&pool->m_mutex);
pool->m_threads++;
pool->m_waiting++;
ddsrt_mutex_unlock (&pool->m_mutex);
}
return res;
}
ddsrt_thread_pool ddsrt_thread_pool_new (uint32_t threads, uint32_t max_threads, uint32_t max_queue, ddsrt_threadattr_t * attr)
{
ddsrt_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 = ddsrt_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;
ddsrt_threadattr_init (&pool->m_attr);
ddsrt_mutex_init (&pool->m_mutex);
ddsrt_cond_init (&pool->m_cv);
if (attr)
{
pool->m_attr = *attr;
}
/* Create initial threads and jobs */
while (threads--)
{
if (ddsrt_thread_pool_new_thread (pool) != DDS_RETCODE_OK)
{
ddsrt_thread_pool_free (pool);
pool = NULL;
break;
}
job = ddsrt_malloc (sizeof (*job));
job->m_next_job = pool->m_free;
pool->m_free = job;
}
return pool;
}
void ddsrt_thread_pool_free (ddsrt_thread_pool pool)
{
ddsi_work_queue_job_t job;
if (pool == NULL)
{
return;
}
ddsrt_mutex_lock (&pool->m_mutex);
/* Delete all pending jobs from queue */
while (pool->m_jobs)
{
job = pool->m_jobs;
pool->m_jobs = job->m_next_job;
ddsrt_free (job);
}
/* Wake all waiting threads */
ddsrt_cond_broadcast (&pool->m_cv);
ddsrt_mutex_unlock (&pool->m_mutex);
/* Wait for threads to complete */
ddsrt_mutex_lock (&pool->m_mutex);
while (pool->m_threads != 0)
ddsrt_cond_wait (&pool->m_cv, &pool->m_mutex);
ddsrt_mutex_unlock (&pool->m_mutex);
/* Delete all free jobs from queue */
while (pool->m_free)
{
job = pool->m_free;
pool->m_free = job->m_next_job;
ddsrt_free (job);
}
ddsrt_cond_destroy (&pool->m_cv);
ddsrt_mutex_destroy (&pool->m_mutex);
ddsrt_free (pool);
}
dds_retcode_t ddsrt_thread_pool_submit (ddsrt_thread_pool pool, void (*fn) (void *arg), void * arg)
{
dds_retcode_t res = DDS_RETCODE_OK;
ddsi_work_queue_job_t job;
ddsrt_mutex_lock (&pool->m_mutex);
if (pool->m_job_max && pool->m_job_count >= pool->m_job_max)
{
/* Maximum number of jobs reached */
res = DDS_RETCODE_TRY_AGAIN;
}
else
{
/* Get or create new job */
if (pool->m_free)
{
job = pool->m_free;
pool->m_free = job->m_next_job;
}
else
{
job = ddsrt_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) ddsrt_thread_pool_new_thread (pool);
}
}
/* Wakeup processing thread */
ddsrt_cond_signal (&pool->m_cv);
}
ddsrt_mutex_unlock (&pool->m_mutex);
return res;
}
void ddsrt_thread_pool_purge (ddsrt_thread_pool pool)
{
uint32_t total;
ddsrt_mutex_lock (&pool->m_mutex);
total = pool->m_threads;
while (pool->m_waiting && (total > pool->m_thread_min))
{
pool->m_waiting--;
total--;
}
ddsrt_cond_broadcast (&pool->m_cv);
ddsrt_mutex_unlock (&pool->m_mutex);
}

722
src/ddsrt/src/xmlparser.c Normal file
View file

@ -0,0 +1,722 @@
/*
* 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 "dds/ddsrt/heap.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/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 ddsrt_xmlp_state {
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 */
int require_eof; /* if false, junk may follow top-level closing tag */
struct ddsrt_xmlp_callbacks cb; /* user-supplied callbacks (or stubs) */
};
static int cb_null_elem_open (void *varg, uintptr_t parentinfo, uintptr_t *eleminfo, const char *name)
{
DDSRT_UNUSED_ARG (varg);
DDSRT_UNUSED_ARG (parentinfo);
DDSRT_UNUSED_ARG (eleminfo);
DDSRT_UNUSED_ARG (name);
return 0;
}
static int cb_null_attr (void *varg, uintptr_t eleminfo, const char *name, const char *value)
{
DDSRT_UNUSED_ARG (varg);
DDSRT_UNUSED_ARG (eleminfo);
DDSRT_UNUSED_ARG (name);
DDSRT_UNUSED_ARG (value);
return 0;
}
static int cb_null_elem_data (void *varg, uintptr_t eleminfo, const char *data)
{
DDSRT_UNUSED_ARG (varg);
DDSRT_UNUSED_ARG (eleminfo);
DDSRT_UNUSED_ARG (data);
return 0;
}
static int cb_null_elem_close (void *varg, uintptr_t eleminfo)
{
DDSRT_UNUSED_ARG (varg);
DDSRT_UNUSED_ARG (eleminfo);
return 0;
}
static void cb_null_error (void *varg, const char *msg, int line)
{
DDSRT_UNUSED_ARG (varg);
DDSRT_UNUSED_ARG (msg);
DDSRT_UNUSED_ARG (line);
}
static void ddsrt_xmlp_new_common (struct ddsrt_xmlp_state *st)
{
st->cbufp = 0;
st->cbufmark = NOMARKER;
st->tpp = 0;
st->tpescp = 0;
st->tpsz = 1024;
st->tp = ddsrt_malloc (st->tpsz);
st->line = 1;
st->prevline = 1;
st->linemark = 0;
st->peektok = 0;
st->peekpayload = NULL;
st->nest = 0;
st->error = 0;
st->require_eof = 1;
}
static void ddsrt_xmlp_new_setCB (struct ddsrt_xmlp_state *st, void *varg, const struct ddsrt_xmlp_callbacks *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 ddsrt_xmlp_state *ddsrt_xmlp_new_file (FILE *fp, void *varg, const struct ddsrt_xmlp_callbacks *cb)
{
struct ddsrt_xmlp_state *st;
st = ddsrt_malloc (sizeof (*st));
st->cbufn = 0;
st->cbufmax = 8192;
st->cbuf = ddsrt_malloc (st->cbufmax);
st->fp = fp;
ddsrt_xmlp_new_common (st);
ddsrt_xmlp_new_setCB (st, varg, cb);
return st;
}
struct ddsrt_xmlp_state *ddsrt_xmlp_new_string (const char *string, void *varg, const struct ddsrt_xmlp_callbacks *cb)
{
struct ddsrt_xmlp_state *st;
st = ddsrt_malloc (sizeof (*st));
st->cbufn = strlen (string);
st->cbufmax = st->cbufn;
st->cbuf = (char *) string;
st->fp = NULL;
ddsrt_xmlp_new_common (st);
ddsrt_xmlp_new_setCB (st, varg, cb);
return st;
}
void ddsrt_xmlp_set_requireEOF (struct ddsrt_xmlp_state *st, int require_eof)
{
st->require_eof = require_eof;
}
size_t ddsrt_xmlp_get_bufpos (const struct ddsrt_xmlp_state *st)
{
return st->cbufp;
}
void ddsrt_xmlp_free (struct ddsrt_xmlp_state *st)
{
if (st->fp != NULL) {
ddsrt_free (st->cbuf);
}
ddsrt_free (st->tp);
ddsrt_free (st);
}
static int make_chars_available (struct ddsrt_xmlp_state *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 = ddsrt_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 ddsrt_xmlp_state *st)
{
assert (st->cbufmark == NOMARKER);
st->cbufmark = st->cbufp;
st->linemark = st->line;
}
static void discard_input_marker (struct ddsrt_xmlp_state *st)
{
assert (st->cbufmark != NOMARKER);
st->cbufmark = NOMARKER;
st->linemark = 0;
}
static void rewind_to_input_marker (struct ddsrt_xmlp_state *st)
{
assert (st->cbufmark != NOMARKER);
st->cbufp = st->cbufmark;
st->line = st->linemark;
discard_input_marker (st);
}
static int next_char (struct ddsrt_xmlp_state *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 ddsrt_xmlp_state *st)
{
if (!make_chars_available (st, 1)) {
return TOK_EOF;
}
return st->cbuf[st->cbufp];
}
static int peek_chars (struct ddsrt_xmlp_state *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;
}
DDSRT_WARNING_MSVC_OFF(4996);
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;
}
DDSRT_WARNING_MSVC_ON(4996);
static void discard_payload (struct ddsrt_xmlp_state *st)
{
st->tpp = 0;
st->tpescp = 0;
}
static int append_to_payload (struct ddsrt_xmlp_state *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 = ddsrt_realloc (st->tp, st->tpsz);
}
return 0;
}
static int save_payload (char **payload, struct ddsrt_xmlp_state *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 = ddsrt_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 = ddsrt_strdup("");
} else {
p = ddsrt_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 ddsrt_xmlp_state *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 ddsrt_xmlp_state *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 ddsrt_xmlp_state *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 ddsrt_xmlp_state *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 ddsrt_xmlp_state *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 ddsrt_xmlp_state *st)
{
st->peektok = 0;
if (st->peekpayload) {
ddsrt_free (st->peekpayload);
st->peekpayload = NULL;
}
}
static int next_token (struct ddsrt_xmlp_state *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) {
ddsrt_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 ddsrt_xmlp_state *st)
{
int tok;
char *payload;
tok = next_token (st, &payload);
st->peektok = tok;
st->peekpayload = payload;
return tok;
}
static int parse_element (struct ddsrt_xmlp_state *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) {
ddsrt_free (content);
PE_LOCAL_ERROR ("expecting string value for attribute", aname);
}
ret = st->cb.attr (st->varg, eleminfo, aname, content);
ddsrt_free (content);
if (ret < 0) {
PE_ERROR2 ("failed in attribute callback", name, aname);
}
ddsrt_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 */
static const char *cdata_magic = "<![CDATA[";
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_magic, 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) && make_chars_available (st, sizeof(cdata_magic) - 1));
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);
ddsrt_free (content);
if (ret < 0) {
PE_ERROR ("failed in data callback", 0);
}
} else {
ddsrt_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:
ddsrt_free (name);
ddsrt_free (aname);
ddsrt_free (ename);
return ret;
#undef PE_LOCAL_ERROR
#undef PE_ERROR
#undef PE_ERROR2
}
int ddsrt_xmlp_parse (struct ddsrt_xmlp_state *st)
{
if (peek_token (st) == TOK_EOF) {
return 0;
} else {
int ret = parse_element (st, 0);
if (ret < 0|| !st->require_eof || next_token (st, NULL) == TOK_EOF ) {
return ret;
} else {
return -1;
}
}
}
int ddsrt_xmlUnescapeInsitu (char *buffer, size_t *n) {
return unescape_insitu (buffer, n);
}