This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository 389-ds-base.
commit a82a2cc6479c298db2b95ed476b91d85d9ee934a
Author: William Brown <firstyear(a)redhat.com>
Date: Wed Feb 22 09:46:18 2017 +1000
Ticket 49139 - Import libsds and nunc-stans for bundling
Fix Description: In order to improve our build, we are importing
our libraries to our source tree. This import includes the datastructure
and nunc-stans library that we use. These are imported from:
ssh://git@pagure.io/nunc-stans.git
commit 4998c860699983814f4b7a2cb7a0a4b12880564a
ssh://git@pagure.io/libsds.git
commit 9afd57c07657bb8ee63f7dabc587189ff94b7f06
https://pagure.io/389-ds-base/issue/49139
Author: wibrown
Review by: mreynolds, vashirov (Thanks!)
---
docs/job-safety.md | 90 +
docs/nunc-stans-job-states.png | Bin 0 -> 11487 bytes
docs/slapi.doxy.in | 2368 ++++++++++++++++++++
ldap/servers/slapd/dse.c | 14 +-
m4/doxygen.m4 | 16 +
m4/event.m4 | 51 +
m4/nunc-stans.m4 | 76 -
src/libsds/README.md | 30 +
src/libsds/external/liblfds710/GNUmakefile | 15 +
.../liblfds710/build/gcc_gnumake_kbuild/Kbuild | 86 +
.../liblfds710/build/msvc_gnumake/liblfds710.def | 75 +
.../liblfds710/build/msvc_gnumake/makefile | 113 +
src/libsds/external/liblfds710/build/wdk_7.1/dirs | 3 +
...river_entry_renamed_to_avoid_compiler_warning.c | 23 +
.../liblfds710/build/wdk_7.1/liblfds710.def | 75 +
.../wdk_7.1/readme_before_win_kernel_build.txt | 32 +
.../runme_before_win_kernel_dynamic_lib_build.bat | 24 +
.../runme_before_win_kernel_static_lib_build.bat | 23 +
.../liblfds710/build/wdk_7.1/sources.dynamic | 66 +
.../liblfds710/build/wdk_7.1/sources.static | 64 +
src/libsds/external/liblfds710/inc/liblfds710.h | 33 +
.../liblfds710/lfds710_btree_addonly_unbalanced.h | 121 +
.../liblfds710/inc/liblfds710/lfds710_freelist.h | 70 +
.../inc/liblfds710/lfds710_hash_addonly.h | 136 ++
.../lfds710_list_addonly_singlylinked_ordered.h | 87 +
.../lfds710_list_addonly_singlylinked_unordered.h | 90 +
.../liblfds710/inc/liblfds710/lfds710_misc.h | 233 ++
.../lfds710_porting_abstraction_layer_compiler.h | 446 ++++
...10_porting_abstraction_layer_operating_system.h | 86 +
.../lfds710_porting_abstraction_layer_processor.h | 459 ++++
.../liblfds710/inc/liblfds710/lfds710_prng.h | 74 +
...ds710_queue_bounded_manyproducer_manyconsumer.h | 66 +
...0_queue_bounded_singleproducer_singleconsumer.h | 59 +
...710_queue_unbounded_manyproducer_manyconsumer.h | 62 +
.../liblfds710/inc/liblfds710/lfds710_ringbuffer.h | 66 +
.../liblfds710/inc/liblfds710/lfds710_stack.h | 58 +
.../obj/lfds710_btree_addonly_unbalanced_cleanup.d | 20 +
.../obj/lfds710_btree_addonly_unbalanced_get.d | 20 +
.../obj/lfds710_btree_addonly_unbalanced_init.d | 20 +
.../obj/lfds710_btree_addonly_unbalanced_insert.d | 20 +
.../obj/lfds710_btree_addonly_unbalanced_query.d | 20 +
.../liblfds710/obj/lfds710_freelist_cleanup.d | 20 +
.../liblfds710/obj/lfds710_freelist_init.d | 20 +
.../external/liblfds710/obj/lfds710_freelist_pop.d | 19 +
.../liblfds710/obj/lfds710_freelist_push.d | 20 +
.../liblfds710/obj/lfds710_freelist_query.d | 20 +
.../liblfds710/obj/lfds710_hash_addonly_cleanup.d | 20 +
.../liblfds710/obj/lfds710_hash_addonly_get.d | 20 +
.../liblfds710/obj/lfds710_hash_addonly_init.d | 20 +
.../liblfds710/obj/lfds710_hash_addonly_insert.d | 20 +
.../liblfds710/obj/lfds710_hash_addonly_iterate.d | 20 +
.../liblfds710/obj/lfds710_hash_addonly_query.d | 20 +
...710_list_addonly_singlylinked_ordered_cleanup.d | 20 +
...lfds710_list_addonly_singlylinked_ordered_get.d | 20 +
...fds710_list_addonly_singlylinked_ordered_init.d | 20 +
...s710_list_addonly_singlylinked_ordered_insert.d | 20 +
...ds710_list_addonly_singlylinked_ordered_query.d | 20 +
...0_list_addonly_singlylinked_unordered_cleanup.d | 20 +
...ds710_list_addonly_singlylinked_unordered_get.d | 20 +
...s710_list_addonly_singlylinked_unordered_init.d | 20 +
...10_list_addonly_singlylinked_unordered_insert.d | 20 +
...710_list_addonly_singlylinked_unordered_query.d | 20 +
.../external/liblfds710/obj/lfds710_misc_globals.d | 19 +
.../obj/lfds710_misc_internal_backoff_init.d | 20 +
.../external/liblfds710/obj/lfds710_misc_query.d | 19 +
.../external/liblfds710/obj/lfds710_prng_init.d | 19 +
...eue_bounded_manyproducer_manyconsumer_cleanup.d | 20 +
...eue_bounded_manyproducer_manyconsumer_dequeue.d | 20 +
...eue_bounded_manyproducer_manyconsumer_enqueue.d | 20 +
..._queue_bounded_manyproducer_manyconsumer_init.d | 20 +
...queue_bounded_manyproducer_manyconsumer_query.d | 20 +
...bounded_singleproducer_singleconsumer_cleanup.d | 20 +
...bounded_singleproducer_singleconsumer_dequeue.d | 20 +
...bounded_singleproducer_singleconsumer_enqueue.d | 20 +
...ue_bounded_singleproducer_singleconsumer_init.d | 20 +
...e_bounded_singleproducer_singleconsumer_query.d | 20 +
...e_unbounded_manyproducer_manyconsumer_cleanup.d | 20 +
...e_unbounded_manyproducer_manyconsumer_dequeue.d | 20 +
...e_unbounded_manyproducer_manyconsumer_enqueue.d | 20 +
...ueue_unbounded_manyproducer_manyconsumer_init.d | 20 +
...eue_unbounded_manyproducer_manyconsumer_query.d | 20 +
.../liblfds710/obj/lfds710_ringbuffer_cleanup.d | 20 +
.../liblfds710/obj/lfds710_ringbuffer_init.d | 20 +
.../liblfds710/obj/lfds710_ringbuffer_query.d | 20 +
.../liblfds710/obj/lfds710_ringbuffer_read.d | 20 +
.../liblfds710/obj/lfds710_ringbuffer_write.d | 20 +
.../liblfds710/obj/lfds710_stack_cleanup.d | 19 +
.../external/liblfds710/obj/lfds710_stack_init.d | 19 +
.../external/liblfds710/obj/lfds710_stack_pop.d | 19 +
.../external/liblfds710/obj/lfds710_stack_push.d | 19 +
.../external/liblfds710/obj/lfds710_stack_query.d | 19 +
.../lfds710_btree_addonly_unbalanced_cleanup.c | 117 +
.../lfds710_btree_addonly_unbalanced_get.c | 482 ++++
.../lfds710_btree_addonly_unbalanced_init.c | 33 +
.../lfds710_btree_addonly_unbalanced_insert.c | 158 ++
.../lfds710_btree_addonly_unbalanced_internal.h | 23 +
.../lfds710_btree_addonly_unbalanced_query.c | 126 ++
.../lfds710_freelist/lfds710_freelist_cleanup.c | 36 +
.../src/lfds710_freelist/lfds710_freelist_init.c | 48 +
.../lfds710_freelist/lfds710_freelist_internal.h | 7 +
.../src/lfds710_freelist/lfds710_freelist_pop.c | 89 +
.../src/lfds710_freelist/lfds710_freelist_push.c | 123 +
.../src/lfds710_freelist/lfds710_freelist_query.c | 152 ++
.../lfds710_hash_addonly_cleanup.c | 63 +
.../lfds710_hash_addonly_get.c | 47 +
.../lfds710_hash_addonly_init.c | 54 +
.../lfds710_hash_addonly_insert.c | 60 +
.../lfds710_hash_addonly_internal.h | 5 +
.../lfds710_hash_addonly_iterate.c | 60 +
.../lfds710_hash_addonly_query.c | 119 +
...710_list_addonly_singlylinked_ordered_cleanup.c | 37 +
...lfds710_list_addonly_singlylinked_ordered_get.c | 29 +
...fds710_list_addonly_singlylinked_ordered_init.c | 38 +
...s710_list_addonly_singlylinked_ordered_insert.c | 136 ++
...10_list_addonly_singlylinked_ordered_internal.h | 5 +
...ds710_list_addonly_singlylinked_ordered_query.c | 128 ++
...0_list_addonly_singlylinked_unordered_cleanup.c | 37 +
...ds710_list_addonly_singlylinked_unordered_get.c | 33 +
...s710_list_addonly_singlylinked_unordered_init.c | 35 +
...10_list_addonly_singlylinked_unordered_insert.c | 193 ++
..._list_addonly_singlylinked_unordered_internal.h | 5 +
...710_list_addonly_singlylinked_unordered_query.c | 128 ++
.../src/lfds710_misc/lfds710_misc_globals.c | 14 +
.../src/lfds710_misc/lfds710_misc_globals.gcno | 1 +
.../src/lfds710_misc/lfds710_misc_internal.h | 5 +
.../lfds710_misc_internal_backoff_init.c | 22 +
.../lfds710_misc_internal_backoff_init.gcno | Bin 0 -> 716 bytes
.../src/lfds710_misc/lfds710_misc_query.c | 38 +
.../src/lfds710_prng/lfds710_prng_init.c | 42 +
.../src/lfds710_prng/lfds710_prng_internal.h | 5 +
...eue_bounded_manyproducer_manyconsumer_cleanup.c | 27 +
...eue_bounded_manyproducer_manyconsumer_dequeue.c | 86 +
...eue_bounded_manyproducer_manyconsumer_enqueue.c | 84 +
..._queue_bounded_manyproducer_manyconsumer_init.c | 42 +
...ue_bounded_manyproducer_manyconsumer_internal.h | 5 +
...queue_bounded_manyproducer_manyconsumer_query.c | 129 ++
...bounded_singleproducer_singleconsumer_cleanup.c | 30 +
...bounded_singleproducer_singleconsumer_dequeue.c | 41 +
...bounded_singleproducer_singleconsumer_enqueue.c | 38 +
...ue_bounded_singleproducer_singleconsumer_init.c | 63 +
...ounded_singleproducer_singleconsumer_internal.h | 5 +
...e_bounded_singleproducer_singleconsumer_query.c | 87 +
...e_unbounded_manyproducer_manyconsumer_cleanup.c | 48 +
...nbounded_manyproducer_manyconsumer_cleanup.gcno | Bin 0 -> 1732 bytes
...e_unbounded_manyproducer_manyconsumer_dequeue.c | 128 ++
...nbounded_manyproducer_manyconsumer_dequeue.gcno | Bin 0 -> 6584 bytes
...e_unbounded_manyproducer_manyconsumer_enqueue.c | 96 +
...nbounded_manyproducer_manyconsumer_enqueue.gcno | Bin 0 -> 5092 bytes
...ueue_unbounded_manyproducer_manyconsumer_init.c | 50 +
...e_unbounded_manyproducer_manyconsumer_init.gcno | Bin 0 -> 2896 bytes
..._unbounded_manyproducer_manyconsumer_internal.h | 14 +
...eue_unbounded_manyproducer_manyconsumer_query.c | 133 ++
..._unbounded_manyproducer_manyconsumer_query.gcno | Bin 0 -> 6588 bytes
.../lfds710_ringbuffer_cleanup.c | 92 +
.../lfds710_ringbuffer/lfds710_ringbuffer_init.c | 42 +
.../lfds710_ringbuffer_internal.h | 5 +
.../lfds710_ringbuffer/lfds710_ringbuffer_query.c | 81 +
.../lfds710_ringbuffer/lfds710_ringbuffer_read.c | 43 +
.../lfds710_ringbuffer/lfds710_ringbuffer_write.c | 77 +
.../src/lfds710_stack/lfds710_stack_cleanup.c | 36 +
.../src/lfds710_stack/lfds710_stack_init.c | 31 +
.../src/lfds710_stack/lfds710_stack_internal.h | 5 +
.../src/lfds710_stack/lfds710_stack_pop.c | 57 +
.../src/lfds710_stack/lfds710_stack_push.c | 47 +
.../src/lfds710_stack/lfds710_stack_query.c | 130 ++
.../external/liblfds710/src/liblfds710_internal.h | 102 +
src/libsds/include/sds.h | 1328 +++++++++++
src/libsds/sds/bpt/bpt.c | 386 ++++
src/libsds/sds/bpt/bpt.h | 91 +
src/libsds/sds/bpt/common.c | 788 +++++++
src/libsds/sds/bpt/list.c | 54 +
src/libsds/sds/bpt/map.c | 361 +++
src/libsds/sds/bpt/search.c | 128 ++
src/libsds/sds/bpt/set.c | 272 +++
src/libsds/sds/bpt/verify.c | 72 +
src/libsds/sds/bpt_cow/atomic.c | 97 +
src/libsds/sds/bpt_cow/bpt_cow.c | 582 +++++
src/libsds/sds/bpt_cow/bpt_cow.h | 47 +
src/libsds/sds/bpt_cow/delete.c | 94 +
src/libsds/sds/bpt_cow/insert.c | 155 ++
src/libsds/sds/bpt_cow/node.c | 252 +++
src/libsds/sds/bpt_cow/search.c | 80 +
src/libsds/sds/bpt_cow/txn.c | 471 ++++
src/libsds/sds/bpt_cow/verify.c | 228 ++
src/libsds/sds/core/crc32c.c | 151 ++
src/libsds/sds/core/utils.c | 124 +
src/libsds/sds/queue/lqueue.c | 238 ++
src/libsds/sds/queue/queue.c | 120 +
src/libsds/sds/queue/queue.h | 13 +
src/libsds/sds/queue/tqueue.c | 64 +
src/libsds/sds/sds_internal.h | 33 +
src/libsds/test/benchmark.c | 591 +++++
src/libsds/test/benchmark.h | 21 +
src/libsds/test/benchmark_par.c | 644 ++++++
src/libsds/test/benchmark_par.h | 73 +
src/libsds/test/benchmark_parwrap.c | 311 +++
src/libsds/test/test_fixtures.c | 201 ++
src/libsds/test/test_sds.c | 22 +
src/libsds/test/test_sds.h | 262 +++
src/libsds/test/test_sds_bpt.c | 983 ++++++++
src/libsds/test/test_sds_cow.c | 613 +++++
src/libsds/test/test_sds_lqueue.c | 210 ++
src/libsds/test/test_sds_queue.c | 171 ++
src/libsds/test/test_sds_set.c | 372 +++
src/libsds/test/test_sds_tqueue.c | 197 ++
src/nunc-stans/README.md | 11 +
src/nunc-stans/include/nunc-stans.h | 883 ++++++++
src/nunc-stans/ns/ns_event_fw.h | 177 ++
src/nunc-stans/ns/ns_event_fw_event.c | 343 +++
src/nunc-stans/ns/ns_event_fw_tevent.c | 332 +++
src/nunc-stans/ns/ns_private.h | 106 +
src/nunc-stans/ns/ns_thrpool.c | 1584 +++++++++++++
src/nunc-stans/ns/ns_tls.c | 291 +++
src/nunc-stans/ns/ns_tls.h | 49 +
src/nunc-stans/test/test_nuncstans.c | 418 ++++
src/nunc-stans/test/test_nuncstans_stress.c | 558 +++++
src/pkgconfig/dirsrv.pc.in | 7 +
src/pkgconfig/libsds.pc.in | 12 +
src/pkgconfig/nunc-stans.pc.in | 11 +
test/libslapd/operation/v3_compat.c | 4 +-
test/libslapd/pblock/v3_compat.c | 3 +
221 files changed, 26674 insertions(+), 83 deletions(-)
diff --git a/docs/job-safety.md b/docs/job-safety.md
new file mode 100644
index 0000000..b122afa
--- /dev/null
+++ b/docs/job-safety.md
@@ -0,0 +1,90 @@
+Nunc Stans Job Safety
+=====================
+
+Nunc Stans 0.2.0 comes with many improvements for job safety. Most consumers of
+this framework will not notice the difference if they are using it
"correctly",
+but in other cases, you may find you have error conditions.
+
+Jobs now flow through a set of states in their lifetime.
+
+States
+------
+
+* WAITING: This represents a job that is idle, and not owned by a worker or event thread.
Any thread can alter this job.
+* NEEDS_DELETE: This represents a job that is marked for deletion. It cannot be accessed
again!
+* DELETED: This represents a job that is deleted. In theory, you can never access a job
in this state.
+* NEEDS_ARM: This is a job that is about to be placed into the event or work queue for
arming, but has not yet been queued.
+* ARMED: This is a job that is currently in the event queue or work queue waiting to be
executed.
+* RUNNING: This is a job that is in the process of executing it's callback right
now.
+
+Diagram
+-------
+
+![Nunc Stans Job States](nunc-stans-job-states.png "Nunc Stans Job States")
+
+WAITING
+-------
+
+All jobs start in the WAITING state. At this point, the job can have two transitions. It
is sent to ns_job_done, and marked as NEEDS_DELETE, or it can be sent to ns_job_rearm, and
marked as NEEDS_ARM. A job that is WAITING can be safely modify with ns_job_set_* and
accessed with ns_job_get_* from any thread.
+
+NEEDS_ARM
+---------
+
+Once a job is in the NEEDS_ARM state, it can not be altered by ns_job_set_*. It can be
read from with ns_job_get_*. It can be sent to ns_job_done (which moves to NEEDS_DELETE),
but generally this is only from within the job callback, with code like the following.
+
+ callback(ns_job_t *job) {
+ ns_job_rearm(job);
+ ns_job_done(job);
+ }
+
+
+NEEDS_ARM in most cases will quickly move to the next state, ARMED
+
+ARMED
+-----
+
+In the ARMED state, this means that the job has been sucessfully queued into the event
*or* work queue. In the ARMED state, the job can be read from with ns_job_get_*, but it
cannot be altered with ns_job_set_*. If a job could be altered while queued, this could
cause issues with the intent of what the job should do (set_data, set_cb, set_done_cb)
etc.
+
+A job that is ARMED and queued can NOT be removed from the queue, or stopped from
running. This is a point of no return!
+
+RUNNING
+-------
+
+In the RUNNING state, the job is in the process of executing the callback that the job
contains. While RUNNING, the thread that is executing the callback may call ns_job_done,
ns_job_rearm, ns_job_get_* and ns_job_set_* upon the job. Note, that calling both
ns_job_done and ns_job_rearm from the callback, as the 'done' is a
'stronger' action we will delete the job even though rearm was also called.
+
+While RUNNING other threads (ie, not the worker thread executing the callback) may only
call ns_job_get_* upon the job. Due to the design of the synchronisation underneath, this
will block until the execution of the callback, so for all intents and purposes by the
time the external thread is able to call ns_job_get_*, the job will have moved to
NEEDS_DELETE, NEEDS_ARM or WAITING.
+
+NEEDS_DELETE
+------------
+
+When you call ns_job_done, this marks the job as NEEDS_DELETE. The deletion actually
occurs at "some later point". When a job is set to NEEDS_DELETE, you may *not*
call any of the ns_job_get_* and ns_job_set_* functions on the job.
+
+DELETED
+-------
+
+This state only exists on the job briefly. This means we are in the process of deleting
the job internally. We execute the ns_job_done_cb at this point, so that the user may
clean up and free any data as required. Only the ns_job_done_cb thread may access the job
at this point.
+
+
+Putting it all together
+-----------------------
+
+This state machine encourages certain types of work flows with jobs. This is because the
current states are opaque to the caller, and are enforced inside of nunc-stans. The most
obviously side effect of a state machine violation is a ASSERT failure with -DDEBUG, or
PR_FAILURE from get()/set(). This encourages certain practices:
+
+* Only single threads should be accessing jobs. This prevents races and sync issues.
+* Data and variables should exist in a single job. Avoid shared (heap) memory locations!
+* Changing jobs should only happen from within the callback, as you can guarantee a
consistent state without needing to spin/block on ns_job_set_*.
+* You may not need mutexes on your data or thread locals, as the job provides the correct
cpu synchronisation guarantees. Consider that each job takes a "root" data node,
then all other allocated variables are referenced there only by the single thread. You can
now dispose of mutexes, as the job will guarantee the synchronisation of this data.
+* Jobs work well if stack variables are used inside the callback functions, rather than
heap.
+
+Some work flows that don't work well here:
+
+* Having threads alter in-flight jobs. This causes race conditions and inconsistencies.
+* Sharing heap data via pointers in jobs. This means you need a mutex on the data, which
causes a serialisation point: Why bother with thread pools if you are just going to
serialise on some data points anyway!
+* Modifying jobs and what they handle. Don't do it! Just ns_job_done on the job, and
create a new one that matches what you want to do.
+* Map reduce: Nunc-Stans doesn't provide a good way to aggregate data on the return,
IE reduce. You may need to provide a queue or some other method to reduce if you were
interested in this.
+
+Examples
+--------
+
+Inside of the nunc-stans project, the tests/cmocka/stress_test.c code is a good example
of a socket server and socket client using nunc-stans that adheres to these principles.
+
diff --git a/docs/nunc-stans-job-states.png b/docs/nunc-stans-job-states.png
new file mode 100644
index 0000000..d91fe32
Binary files /dev/null and b/docs/nunc-stans-job-states.png differ
diff --git a/docs/slapi.doxy.in b/docs/slapi.doxy.in
new file mode 100644
index 0000000..2cc2d5f
--- /dev/null
+++ b/docs/slapi.doxy.in
@@ -0,0 +1,2368 @@
+# Doxyfile 1.8.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (
www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See
http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = @abs_top_builddir@
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to
insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
"class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See
http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+#
http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond
<section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also
http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = src/libsds/include/sds.h \
+ docs/job-safety.md \
+ src/nunc-stans/include/nunc-stans.h
+ # ldap/servers/slapd/slapi-plugin.h \
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is
the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see
http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: NO.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+# HTML_HEADER = docs/doc_header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default
footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the
style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = docs/custom.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+# HTML_EXTRA_FILES = docs/nunc-stans-intro.png \
+# docs/nunc-stans-job-states.png
+HTML_EXTRA_FILES = docs/nunc-stans-job-states.png
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+#
http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 195
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 96
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See
http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see:
http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+#
http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+#
http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+#
http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from
http://www.mathjax.org before deployment.
+# The default value is:
http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH =
http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the
<cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape>
to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor
keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the
filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+#
http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see
http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted,
"=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the
use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+#
http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+#
http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c
index c38448b..2837b16 100644
--- a/ldap/servers/slapd/dse.c
+++ b/ldap/servers/slapd/dse.c
@@ -644,17 +644,18 @@ dse_check_file(char *filename, char *backupname)
PRFileInfo64 prfinfo;
if (PR_GetFileInfo64( filename, &prfinfo ) == PR_SUCCESS) {
- if ( prfinfo.size > 0)
+ if ( prfinfo.size > 0) {
return (1);
- else {
+ } else {
rc = PR_Delete (filename);
}
}
- if (backupname)
+ if (backupname) {
rc = PR_Rename (backupname, filename);
- else
+ } else {
return (0);
+ }
if ( PR_GetFileInfo64( filename, &prfinfo ) == PR_SUCCESS && prfinfo.size
> 0 ) {
slapi_log_err(SLAPI_LOG_INFO, "dse_check_file",
@@ -687,8 +688,9 @@ dse_read_one_file(struct dse *pdse, const char *filename, Slapi_PBlock
*pb,
{
/* check if the "real" file exists and cam be used, if not try tmp as
backup */
rc = dse_check_file((char *)filename, pdse->dse_tmpfile);
- if (!rc)
+ if (!rc) {
rc = dse_check_file((char *)filename, pdse->dse_fileback);
+ }
if ( (rc = PR_GetFileInfo64( filename, &prfinfo )) != PR_SUCCESS )
{
@@ -792,7 +794,7 @@ dse_read_one_file(struct dse *pdse, const char *filename, Slapi_PBlock
*pb,
}
else /* free entry if not used */
{
- slapi_log_err(SLAPI_LOG_ERR,
+ slapi_log_err(SLAPI_LOG_FATAL,
"dse_read_one_file",
"The entry %s in file %s "
"(lineno: %d) is invalid, "
diff --git a/m4/doxygen.m4 b/m4/doxygen.m4
new file mode 100644
index 0000000..0de97d8
--- /dev/null
+++ b/m4/doxygen.m4
@@ -0,0 +1,16 @@
+## BEGIN COPYRIGHT BLOCK
+## Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+## All rights reserved.
+##
+## License: License: GPL (version 3 or any later version).
+## See LICENSE for details.
+## END COPYRIGHT BLOCK
+
+AC_CHECK_PROGS([DOXYGEN], [doxygen])
+if test -z "$DOXYGEN";
+ then AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])
+fi
+
+AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])AM_COND_IF([HAVE_DOXYGEN],
[AC_CONFIG_FILES([docs/slapi.doxy])])
+
+
diff --git a/m4/event.m4 b/m4/event.m4
new file mode 100644
index 0000000..8320459
--- /dev/null
+++ b/m4/event.m4
@@ -0,0 +1,51 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright (C) 2015 Red Hat
+# see files 'COPYING' and 'COPYING.openssl' for use and warranty
+# information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+# Additional permission under GPLv3 section 7:
+#
+# If you modify this Program, or any covered work, by linking or
+# combining it with OpenSSL, or a modified version of OpenSSL licensed
+# under the OpenSSL license
+# (
https://www.openssl.org/source/license.html), the licensors of this
+# Program grant you additional permission to convey the resulting
+# work. Corresponding Source for a non-source form of such a
+# combination shall include the source code for the parts that are
+# licensed under the OpenSSL license as well as that of the covered
+# work.
+# END COPYRIGHT BLOCK
+AC_CHECKING(for EVENT)
+
+# Always use pkgconfig, because we know it's installed properly!
+
+AC_MSG_CHECKING(for event with pkg-config)
+AC_PATH_PROG(PKG_CONFIG, pkg-config)
+if test -n "$PKG_CONFIG"; then
+ if $PKG_CONFIG --exists libevent; then
+ event_inc=`$PKG_CONFIG --cflags libevent`
+ event_lib=`$PKG_CONFIG --libs libevent`
+ AC_MSG_RESULT([using system EVENT])
+ else
+ AC_MSG_ERROR([EVENT not found, check with pkg-config libevent!])
+ fi
+fi
+
+AC_SUBST(event_inc)
+AC_SUBST(event_lib)
+
+
+
diff --git a/m4/nunc-stans.m4 b/m4/nunc-stans.m4
deleted file mode 100644
index 442a0df..0000000
--- a/m4/nunc-stans.m4
+++ /dev/null
@@ -1,76 +0,0 @@
-# BEGIN COPYRIGHT BLOCK
-# License: GPL (version 3 or any later version).
-# See LICENSE for details.
-# END COPYRIGHT BLOCK
-
-AC_CHECKING(for nunc-stans)
-
-dnl - check for --with-nunc-stans
-AC_MSG_CHECKING(for --with-nunc-stans)
-AC_ARG_WITH(nunc-stans, AS_HELP_STRING([--with-nunc-stans@<:@=PATH@:>@],[nunc-stans
directory]),
-[
- if test "$withval" = "yes"; then
- AC_MSG_RESULT(yes)
- elif test "$withval" = "no"; then
- AC_MSG_RESULT(no)
- elif test -d "$withval"; then
- AC_MSG_RESULT([using $withval])
- dnl - check the user provided location
- nunc_stans_lib="-L$withval/lib"
- nunc_stans_libdir="$withval/lib"
- nunc_stans_incdir="$withval/include"
- if ! test -e "$nunc_stans_incdir/nunc-stans/nunc-stans.h" ; then
- AC_MSG_ERROR([$withval include dir not found])
- fi
- nunc_stans_inc="-I$nunc_stans_incdir"
- else
- echo
- AC_MSG_ERROR([$withval not found])
- fi
-],
-AC_MSG_RESULT(no))
-
-# check for --with-nunc-stans-inc
-AC_MSG_CHECKING(for --with-nunc-stans-inc)
-AC_ARG_WITH(nunc-stans-inc, AS_HELP_STRING([--with-nunc-stans-inc=PATH],[nunc-stans
include file directory]),
-[
- if test -e "$withval"/nunc-stans/nunc-stans.h
- then
- AC_MSG_RESULT([using $withval])
- nunc_stans_incdir="$withval"
- nunc_stans_inc="-I$withval"
- else
- echo
- AC_MSG_ERROR([$withval not found])
- fi
-],
-AC_MSG_RESULT(no))
-
-# check for --with-nunc-stans-lib
-AC_MSG_CHECKING(for --with-nunc-stans-lib)
-AC_ARG_WITH(nunc-stans-lib, AS_HELP_STRING([--with-nunc-stans-lib=PATH],[nunc-stans
library directory]),
-[
- if test -d "$withval"
- then
- AC_MSG_RESULT([using $withval])
- nunc_stans_lib="-L$withval"
- nunc_stans_libdir="$withval"
- else
- echo
- AC_MSG_ERROR([$withval not found])
- fi
-],
-AC_MSG_RESULT(no))
-
-if test -z "$nunc_stans_inc" -o -z "$nunc_stans_lib"; then
- AC_PATH_PROG(PKG_CONFIG, pkg-config)
- AC_MSG_CHECKING(for nunc-stans with pkg-config)
- if test -n "$PKG_CONFIG"; then
- if $PKG_CONFIG --exists nunc-stans; then
- nunc_stans_inc=`$PKG_CONFIG --cflags-only-I nunc-stans`
- nunc_stans_lib=`$PKG_CONFIG --libs-only-L nunc-stans`
- AC_MSG_RESULT([using system nunc-stans])
- fi
- fi
-fi
-
diff --git a/src/libsds/README.md b/src/libsds/README.md
new file mode 100644
index 0000000..dc2c1ce
--- /dev/null
+++ b/src/libsds/README.md
@@ -0,0 +1,30 @@
+Libsds {#mainpage}
+======
+
+
+Libsds is the Slapi Data Structures Library. This encapsulates a queue, b+tree
+and set, a copy-on-write b+tree for parallelism.
+
+In the future it will contain a copy-on-write LRU cache as well.
+
+This abstraction exists so that we can reuse this library with Nunc-Stans. The
+queue detects if we are on an x86_64 capable CPU and will build a lock free
+variant if possible.
+
+This library has thorough and complete tests of various complex states to
+assure correct behaviour. It has been tested against NSPR Hashmaps and the
+slapi AVL tree for performance.
+
+Building
+--------
+
+ autoreconf -fiv
+ ./configure [--enable-tests]
+ make
+ [make test]
+ make install
+
+### Author
+
+William Brown <william(a)blackhats.net.au>
+
diff --git a/src/libsds/external/liblfds710/GNUmakefile
b/src/libsds/external/liblfds710/GNUmakefile
new file mode 100644
index 0000000..f2e85ab
--- /dev/null
+++ b/src/libsds/external/liblfds710/GNUmakefile
@@ -0,0 +1,15 @@
+# THIS IS A GENERATED MAKEFILE. PLEASE SEE m4/lfds.m4
+CFLAGS = -O0 -Wall -Wextra -Wunused -fno-omit-frame-pointer -Wstrict-overflow
-fno-strict-aliasing -fsanitize=address -lasan $(AM_CFLAGS) -fPIC -Wno-unknown-pragmas
+lfds_make_target = ar_dbg
+lfds_path = ./
+.PHONY: dvi pdf ps info html check
+dvi pdf ps info html check:
+# This won't work on FreeBSD, because it requires gmake, not make.
+install all:
+ mkdir -p $(lfds_path)/bin/
+ mkdir -p $(lfds_path)/obj/
+ CFLAGS="$(CFLAGS)" make -C $(lfds_path)/build/gcc_gnumake $(lfds_make_target)
+
+clean:
+ make -C $(lfds_path)/build/gcc_gnumake clean
+
diff --git a/src/libsds/external/liblfds710/build/gcc_gnumake_kbuild/Kbuild
b/src/libsds/external/liblfds710/build/gcc_gnumake_kbuild/Kbuild
new file mode 100644
index 0000000..18701f3
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/gcc_gnumake_kbuild/Kbuild
@@ -0,0 +1,86 @@
+lib-y :=
+
+lib-y +=
../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_cleanup.o
+lib-y +=
../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_get.o
+lib-y +=
../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_init.o
+lib-y +=
../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_insert.o
+lib-y +=
../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_query.o
+
+lib-y += ../../src/lfds710_freelist/lfds710_freelist_cleanup.o
+lib-y += ../../src/lfds710_freelist/lfds710_freelist_init.o
+lib-y += ../../src/lfds710_freelist/lfds710_freelist_pop.o
+lib-y += ../../src/lfds710_freelist/lfds710_freelist_push.o
+lib-y += ../../src/lfds710_freelist/lfds710_freelist_query.o
+
+lib-y += ../../src/lfds710_hash_addonly/lfds710_hash_addonly_cleanup.o
+lib-y += ../../src/lfds710_hash_addonly/lfds710_hash_addonly_get.o
+lib-y += ../../src/lfds710_hash_addonly/lfds710_hash_addonly_init.o
+lib-y += ../../src/lfds710_hash_addonly/lfds710_hash_addonly_insert.o
+lib-y += ../../src/lfds710_hash_addonly/lfds710_hash_addonly_iterate.o
+lib-y += ../../src/lfds710_hash_addonly/lfds710_hash_addonly_query.o
+
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_cleanup.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_get.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_init.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_insert.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_query.o
+
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_cleanup.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_get.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_init.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_insert.o
+lib-y +=
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_query.o
+
+lib-y += ../../src/lfds710_misc/lfds710_misc_internal_backoff_init.o
+lib-y += ../../src/lfds710_misc/lfds710_misc_globals.o
+lib-y += ../../src/lfds710_misc/lfds710_misc_query.o
+
+lib-y += ../../src/lfds710_prng/lfds710_prng_init.o
+
+lib-y +=
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.o
+lib-y +=
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.o
+lib-y +=
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.o
+lib-y +=
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_init.o
+lib-y +=
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_query.o
+
+lib-y +=
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.o
+lib-y +=
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.o
+lib-y +=
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.o
+lib-y +=
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_init.o
+lib-y +=
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_query.o
+
+lib-y +=
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.o
+lib-y +=
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.o
+lib-y +=
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.o
+lib-y +=
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.o
+lib-y +=
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.o
+
+lib-y += ../../src/lfds710_ringbuffer/lfds710_ringbuffer_cleanup.o
+lib-y += ../../src/lfds710_ringbuffer/lfds710_ringbuffer_init.o
+lib-y += ../../src/lfds710_ringbuffer/lfds710_ringbuffer_query.o
+lib-y += ../../src/lfds710_ringbuffer/lfds710_ringbuffer_read.o
+lib-y += ../../src/lfds710_ringbuffer/lfds710_ringbuffer_write.o
+
+lib-y += ../../src/lfds710_stack/lfds710_stack_cleanup.o
+lib-y += ../../src/lfds710_stack/lfds710_stack_init.o
+lib-y += ../../src/lfds710_stack/lfds710_stack_pop.o
+lib-y += ../../src/lfds710_stack/lfds710_stack_push.o
+lib-y += ../../src/lfds710_stack/lfds710_stack_query.o
+
+libs-y := ../../bin/
+
+ccflags-y := -I$(src)/../../inc
+ccflags-y += -I$(src)/../../inc/liblfds710
+ccflags-y += -DKERNEL_MODE
+ccflags-y += -DNDEBUG
+ccflags-y += -fno-strict-aliasing
+ccflags-y += -std=gnu89
+ccflags-y += -Wall
+ccflags-y += -Werror
+ccflags-y += -Wno-unknown-pragmas
+ccflags-y += -Wno-unused-but-set-variable
+ccflags-y += -Wno-uninitialized
+
+
+
+
diff --git a/src/libsds/external/liblfds710/build/msvc_gnumake/liblfds710.def
b/src/libsds/external/liblfds710/build/msvc_gnumake/liblfds710.def
new file mode 100644
index 0000000..b5f20d2
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/msvc_gnumake/liblfds710.def
@@ -0,0 +1,75 @@
+EXPORTS
+
+lfds710_btree_au_init_valid_on_current_logical_core =
lfds710_btree_au_init_valid_on_current_logical_core
+lfds710_btree_au_cleanup = lfds710_btree_au_cleanup
+lfds710_btree_au_insert = lfds710_btree_au_insert
+lfds710_btree_au_get_by_key = lfds710_btree_au_get_by_key
+lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position =
lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position
+lfds710_btree_au_get_by_absolute_position = lfds710_btree_au_get_by_absolute_position
+lfds710_btree_au_get_by_relative_position = lfds710_btree_au_get_by_relative_position
+lfds710_btree_au_query = lfds710_btree_au_query
+
+lfds710_freelist_init_valid_on_current_logical_core =
lfds710_freelist_init_valid_on_current_logical_core
+lfds710_freelist_cleanup = lfds710_freelist_cleanup
+lfds710_freelist_push = lfds710_freelist_push
+lfds710_freelist_pop = lfds710_freelist_pop
+lfds710_freelist_query = lfds710_freelist_query
+
+lfds710_hash_a_init_valid_on_current_logical_core =
lfds710_hash_a_init_valid_on_current_logical_core
+lfds710_hash_a_cleanup = lfds710_hash_a_cleanup
+lfds710_hash_a_insert = lfds710_hash_a_insert
+lfds710_hash_a_get_by_key = lfds710_hash_a_get_by_key
+lfds710_hash_a_iterate_init = lfds710_hash_a_iterate_init
+lfds710_hash_a_iterate = lfds710_hash_a_iterate
+lfds710_hash_a_query = lfds710_hash_a_query
+
+lfds710_list_aso_init_valid_on_current_logical_core =
lfds710_list_aso_init_valid_on_current_logical_core
+lfds710_list_aso_cleanup = lfds710_list_aso_cleanup
+lfds710_list_aso_insert = lfds710_list_aso_insert
+lfds710_list_aso_get_by_key = lfds710_list_aso_get_by_key
+lfds710_list_aso_query = lfds710_list_aso_query
+
+lfds710_list_asu_init_valid_on_current_logical_core =
lfds710_list_asu_init_valid_on_current_logical_core
+lfds710_list_asu_cleanup = lfds710_list_asu_cleanup
+lfds710_list_asu_insert_at_position = lfds710_list_asu_insert_at_position
+lfds710_list_asu_insert_at_start = lfds710_list_asu_insert_at_start
+lfds710_list_asu_insert_at_end = lfds710_list_asu_insert_at_end
+lfds710_list_asu_insert_after_element = lfds710_list_asu_insert_after_element
+lfds710_list_asu_get_by_key = lfds710_list_asu_get_by_key
+lfds710_list_asu_query = lfds710_list_asu_query
+
+lfds710_misc_query = lfds710_misc_query
+
+lfds710_prng_init_valid_on_current_logical_core =
lfds710_prng_init_valid_on_current_logical_core
+lfds710_prng_st_init = lfds710_prng_st_init
+
+lfds710_queue_bmm_init_valid_on_current_logical_core =
lfds710_queue_bmm_init_valid_on_current_logical_core
+lfds710_queue_bmm_cleanup = lfds710_queue_bmm_cleanup
+lfds710_queue_bmm_enqueue = lfds710_queue_bmm_enqueue
+lfds710_queue_bmm_dequeue = lfds710_queue_bmm_dequeue
+lfds710_queue_bmm_query = lfds710_queue_bmm_query
+
+lfds710_queue_bss_init_valid_on_current_logical_core =
lfds710_queue_bss_init_valid_on_current_logical_core
+lfds710_queue_bss_cleanup = lfds710_queue_bss_cleanup
+lfds710_queue_bss_enqueue = lfds710_queue_bss_enqueue
+lfds710_queue_bss_dequeue = lfds710_queue_bss_dequeue
+lfds710_queue_bss_query = lfds710_queue_bss_query
+
+lfds710_queue_umm_init_valid_on_current_logical_core =
lfds710_queue_umm_init_valid_on_current_logical_core
+lfds710_queue_umm_cleanup = lfds710_queue_umm_cleanup
+lfds710_queue_umm_enqueue = lfds710_queue_umm_enqueue
+lfds710_queue_umm_dequeue = lfds710_queue_umm_dequeue
+lfds710_queue_umm_query = lfds710_queue_umm_query
+
+lfds710_ringbuffer_init_valid_on_current_logical_core =
lfds710_ringbuffer_init_valid_on_current_logical_core
+lfds710_ringbuffer_cleanup = lfds710_ringbuffer_cleanup
+lfds710_ringbuffer_read = lfds710_ringbuffer_read
+lfds710_ringbuffer_write = lfds710_ringbuffer_write
+lfds710_ringbuffer_query = lfds710_ringbuffer_query
+
+lfds710_stack_init_valid_on_current_logical_core =
lfds710_stack_init_valid_on_current_logical_core
+lfds710_stack_cleanup = lfds710_stack_cleanup
+lfds710_stack_push = lfds710_stack_push
+lfds710_stack_pop = lfds710_stack_pop
+lfds710_stack_query = lfds710_stack_query
+
diff --git a/src/libsds/external/liblfds710/build/msvc_gnumake/makefile
b/src/libsds/external/liblfds710/build/msvc_gnumake/makefile
new file mode 100644
index 0000000..f62b40e
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/msvc_gnumake/makefile
@@ -0,0 +1,113 @@
+##### paths #####
+BINDIR := ..\..\bin
+INCDIR := ..\..\inc
+OBJDIR := ..\..\obj
+SRCDIR := ..\..\src
+
+##### misc #####
+QUIETLY := 1>nul 2>nul
+NULL :=
+SPACE := $(NULL) # TRD : with a trailing space
+
+##### sources, objects and libraries #####
+BINNAME := liblfds710
+LIB_BINARY := $(BINDIR)\$(BINNAME).lib
+DLL_BINARY := $(BINDIR)\$(BINNAME).dll
+SRCDIRS := lfds710_btree_addonly_unbalanced lfds710_freelist lfds710_hash_addonly
lfds710_list_addonly_singlylinked_ordered lfds710_list_addonly_singlylinked_unordered
lfds710_misc lfds710_prng lfds710_queue_bounded_manyproducer_manyconsumer
lfds710_queue_bounded_singleproducer_singleconsumer
lfds710_queue_unbounded_manyproducer_manyconsumer lfds710_ringbuffer lfds710_stack
+SOURCES := lfds710_hash_addonly_cleanup.c lfds710_hash_addonly_get.c
lfds710_hash_addonly_init.c lfds710_hash_addonly_insert.c lfds710_hash_addonly_iterate.c
lfds710_hash_addonly_query.c \
+ lfds710_list_addonly_singlylinked_ordered_cleanup.c
lfds710_list_addonly_singlylinked_ordered_get.c
lfds710_list_addonly_singlylinked_ordered_init.c
lfds710_list_addonly_singlylinked_ordered_insert.c
lfds710_list_addonly_singlylinked_ordered_query.c \
+ lfds710_list_addonly_singlylinked_unordered_cleanup.c
lfds710_list_addonly_singlylinked_unordered_get.c
lfds710_list_addonly_singlylinked_unordered_init.c
lfds710_list_addonly_singlylinked_unordered_insert.c
lfds710_list_addonly_singlylinked_unordered_query.c \
+ lfds710_btree_addonly_unbalanced_cleanup.c
lfds710_btree_addonly_unbalanced_get.c lfds710_btree_addonly_unbalanced_init.c
lfds710_btree_addonly_unbalanced_insert.c lfds710_btree_addonly_unbalanced_query.c \
+ lfds710_freelist_cleanup.c lfds710_freelist_init.c lfds710_freelist_pop.c
lfds710_freelist_push.c lfds710_freelist_query.c \
+ lfds710_misc_internal_backoff_init.c lfds710_misc_globals.c
lfds710_misc_query.c \
+ lfds710_prng_init.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c
lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c
lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c
lfds710_queue_bounded_manyproducer_manyconsumer_init.c
lfds710_queue_bounded_manyproducer_manyconsumer_query.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c
lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c
lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c
lfds710_queue_bounded_singleproducer_singleconsumer_init.c
lfds710_queue_bounded_singleproducer_singleconsumer_query.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c
lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c
lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c
lfds710_queue_unbounded_manyproducer_manyconsumer_init.c
lfds710_queue_unbounded_manyproducer_manyconsumer_query.c \
+ lfds710_ringbuffer_cleanup.c lfds710_ringbuffer_init.c
lfds710_ringbuffer_query.c lfds710_ringbuffer_read.c lfds710_ringbuffer_write.c \
+ lfds710_stack_cleanup.c lfds710_stack_init.c lfds710_stack_pop.c
lfds710_stack_push.c lfds710_stack_query.c
+OBJECTS := $(patsubst %.c,$(OBJDIR)/%.obj,$(notdir $(SOURCES)))
+SYSLIBS := kernel32.lib
+
+##### default paths fix up #####
+INCDIRS := $(patsubst %,%;,$(INCDIR))
+INCLUDE += $(subst $(SPACE),,$(INCDIRS))
+
+##### tools #####
+CC := cl
+CFLAGS_MANDATORY := /c "/Fd$(BINDIR)\$(BINNAME).pdb" /wd 4068
+CFLAGS_OPTIONAL := /DWIN32_LEAN_AND_MEAN /DUNICODE /D_UNICODE /nologo /W4 /WX
+CFLAGS_MANDATORY_DBG := /Od /Gm /Zi /D_DEBUG
+CFLAGS_MANDATORY_REL := /Ox /DNDEBUG
+
+AR := lib
+ARFLAGS :=
+ARFLAGS_MANDATORY := /subsystem:console
+ARFLAGS_OPTIONAL := /nologo /wx /verbose
+
+LD := link
+LDFLAGS_MANDATORY := /def:$(BINNAME).def /dll /nodefaultlib /subsystem:console
+LDFLAGS_OPTIONAL := /nologo /nxcompat /wx
+LDFLAGS_MANDATORY_DBG := /debug "/pdb:$(BINDIR)\$(BINNAME).pdb"
+LDFLAGS_MANDATORY_REL := /incremental:no
+
+##### variants #####
+ifeq ($(MAKECMDGOALS),) # TRD : default to debug lib
+ CFLAGS_MANDATORY += $(CFLAGS_MANDATORY_DBG)
+ LDFLAGS_MANDATORY += $(LDFLAGS_MANDATORY_DBG)
+ CLIB := libcmtd.lib
+endif
+
+ifeq ($(MAKECMDGOALS),libdbg)
+ CFLAGS_MANDATORY += $(CFLAGS_MANDATORY_DBG)
+ LDFLAGS_MANDATORY += $(LDFLAGS_MANDATORY_DBG)
+ CLIB := libcmtd.lib
+endif
+
+ifeq ($(MAKECMDGOALS),librel)
+ CFLAGS_MANDATORY += $(CFLAGS_MANDATORY_REL)
+ LDFLAGS_MANDATORY += $(LDFLAGS_MANDATORY_REL)
+ CLIB := libcmt.lib
+endif
+
+ifeq ($(MAKECMDGOALS),dlldbg)
+ CFLAGS_MANDATORY += $(CFLAGS_MANDATORY_DBG)
+ LDFLAGS_MANDATORY += $(LDFLAGS_MANDATORY_DBG)
+ CLIB := msvcrtd.lib
+endif
+
+ifeq ($(MAKECMDGOALS),dllrel)
+ CFLAGS_MANDATORY += $(CFLAGS_MANDATORY_REL)
+ LDFLAGS_MANDATORY += $(LDFLAGS_MANDATORY_REL)
+ CLIB := msvcrt.lib
+endif
+
+##### search paths #####
+vpath %.c $(patsubst %,$(SRCDIR)/%;,$(SRCDIRS))
+
+##### implicit rules #####
+$(OBJDIR)/%.obj : %.c
+ $(CC) $(CFLAGS_OPTIONAL) $(CFLAGS) $(CFLAGS_MANDATORY) "/Fo$@" $<
+
+##### explicit rules #####
+$(LIB_BINARY) : $(OBJECTS)
+ $(AR) $(ARFLAGS_OPTIONAL) $(ARFLAGS) $(ARFLAGS_MANDATORY) $(OBJECTS) /out:$(LIB_BINARY)
+
+$(DLL_BINARY) : $(OBJECTS)
+ $(LD) $(LDFLAGS_OPTIONAL) $(LDFLAGS) $(LDFLAGS_MANDATORY) $(CLIB) $(SYSLIBS) $(OBJECTS)
/out:$(DLL_BINARY)
+
+##### phony #####
+.PHONY : clean librel libdbg dllrel dlldbg
+
+clean :
+ @erase /Q $(BINDIR)\$(BINNAME).* $(OBJDIR)\*.obj $(QUIETLY)
+
+dllrel : $(DLL_BINARY)
+dlldbg : $(DLL_BINARY)
+
+librel : $(LIB_BINARY)
+libdbg : $(LIB_BINARY)
+
+##### notes #####
+# /wd 4068 : turn off "unknown pragma" warning
+
diff --git a/src/libsds/external/liblfds710/build/wdk_7.1/dirs
b/src/libsds/external/liblfds710/build/wdk_7.1/dirs
new file mode 100644
index 0000000..64e002c
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/wdk_7.1/dirs
@@ -0,0 +1,3 @@
+DIRS = single_dir_for_windows_kernel
+
+
diff --git
a/src/libsds/external/liblfds710/build/wdk_7.1/driver_entry_renamed_to_avoid_compiler_warning.c
b/src/libsds/external/liblfds710/build/wdk_7.1/driver_entry_renamed_to_avoid_compiler_warning.c
new file mode 100644
index 0000000..cddedc7
--- /dev/null
+++
b/src/libsds/external/liblfds710/build/wdk_7.1/driver_entry_renamed_to_avoid_compiler_warning.c
@@ -0,0 +1,23 @@
+#include "liblfds710_internal.h"
+
+
+
+
+
+/****************************************************************************/
+DRIVER_INITIALIZE DriverEntry;
+
+
+
+
+
+/****************************************************************************/
+#pragma warning( disable : 4100 )
+
+NTSTATUS DriverEntry( struct _DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath
)
+{
+ return STATUS_SUCCESS;
+}
+
+#pragma warning( default : 4100 )
+
diff --git a/src/libsds/external/liblfds710/build/wdk_7.1/liblfds710.def
b/src/libsds/external/liblfds710/build/wdk_7.1/liblfds710.def
new file mode 100644
index 0000000..b5f20d2
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/wdk_7.1/liblfds710.def
@@ -0,0 +1,75 @@
+EXPORTS
+
+lfds710_btree_au_init_valid_on_current_logical_core =
lfds710_btree_au_init_valid_on_current_logical_core
+lfds710_btree_au_cleanup = lfds710_btree_au_cleanup
+lfds710_btree_au_insert = lfds710_btree_au_insert
+lfds710_btree_au_get_by_key = lfds710_btree_au_get_by_key
+lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position =
lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position
+lfds710_btree_au_get_by_absolute_position = lfds710_btree_au_get_by_absolute_position
+lfds710_btree_au_get_by_relative_position = lfds710_btree_au_get_by_relative_position
+lfds710_btree_au_query = lfds710_btree_au_query
+
+lfds710_freelist_init_valid_on_current_logical_core =
lfds710_freelist_init_valid_on_current_logical_core
+lfds710_freelist_cleanup = lfds710_freelist_cleanup
+lfds710_freelist_push = lfds710_freelist_push
+lfds710_freelist_pop = lfds710_freelist_pop
+lfds710_freelist_query = lfds710_freelist_query
+
+lfds710_hash_a_init_valid_on_current_logical_core =
lfds710_hash_a_init_valid_on_current_logical_core
+lfds710_hash_a_cleanup = lfds710_hash_a_cleanup
+lfds710_hash_a_insert = lfds710_hash_a_insert
+lfds710_hash_a_get_by_key = lfds710_hash_a_get_by_key
+lfds710_hash_a_iterate_init = lfds710_hash_a_iterate_init
+lfds710_hash_a_iterate = lfds710_hash_a_iterate
+lfds710_hash_a_query = lfds710_hash_a_query
+
+lfds710_list_aso_init_valid_on_current_logical_core =
lfds710_list_aso_init_valid_on_current_logical_core
+lfds710_list_aso_cleanup = lfds710_list_aso_cleanup
+lfds710_list_aso_insert = lfds710_list_aso_insert
+lfds710_list_aso_get_by_key = lfds710_list_aso_get_by_key
+lfds710_list_aso_query = lfds710_list_aso_query
+
+lfds710_list_asu_init_valid_on_current_logical_core =
lfds710_list_asu_init_valid_on_current_logical_core
+lfds710_list_asu_cleanup = lfds710_list_asu_cleanup
+lfds710_list_asu_insert_at_position = lfds710_list_asu_insert_at_position
+lfds710_list_asu_insert_at_start = lfds710_list_asu_insert_at_start
+lfds710_list_asu_insert_at_end = lfds710_list_asu_insert_at_end
+lfds710_list_asu_insert_after_element = lfds710_list_asu_insert_after_element
+lfds710_list_asu_get_by_key = lfds710_list_asu_get_by_key
+lfds710_list_asu_query = lfds710_list_asu_query
+
+lfds710_misc_query = lfds710_misc_query
+
+lfds710_prng_init_valid_on_current_logical_core =
lfds710_prng_init_valid_on_current_logical_core
+lfds710_prng_st_init = lfds710_prng_st_init
+
+lfds710_queue_bmm_init_valid_on_current_logical_core =
lfds710_queue_bmm_init_valid_on_current_logical_core
+lfds710_queue_bmm_cleanup = lfds710_queue_bmm_cleanup
+lfds710_queue_bmm_enqueue = lfds710_queue_bmm_enqueue
+lfds710_queue_bmm_dequeue = lfds710_queue_bmm_dequeue
+lfds710_queue_bmm_query = lfds710_queue_bmm_query
+
+lfds710_queue_bss_init_valid_on_current_logical_core =
lfds710_queue_bss_init_valid_on_current_logical_core
+lfds710_queue_bss_cleanup = lfds710_queue_bss_cleanup
+lfds710_queue_bss_enqueue = lfds710_queue_bss_enqueue
+lfds710_queue_bss_dequeue = lfds710_queue_bss_dequeue
+lfds710_queue_bss_query = lfds710_queue_bss_query
+
+lfds710_queue_umm_init_valid_on_current_logical_core =
lfds710_queue_umm_init_valid_on_current_logical_core
+lfds710_queue_umm_cleanup = lfds710_queue_umm_cleanup
+lfds710_queue_umm_enqueue = lfds710_queue_umm_enqueue
+lfds710_queue_umm_dequeue = lfds710_queue_umm_dequeue
+lfds710_queue_umm_query = lfds710_queue_umm_query
+
+lfds710_ringbuffer_init_valid_on_current_logical_core =
lfds710_ringbuffer_init_valid_on_current_logical_core
+lfds710_ringbuffer_cleanup = lfds710_ringbuffer_cleanup
+lfds710_ringbuffer_read = lfds710_ringbuffer_read
+lfds710_ringbuffer_write = lfds710_ringbuffer_write
+lfds710_ringbuffer_query = lfds710_ringbuffer_query
+
+lfds710_stack_init_valid_on_current_logical_core =
lfds710_stack_init_valid_on_current_logical_core
+lfds710_stack_cleanup = lfds710_stack_cleanup
+lfds710_stack_push = lfds710_stack_push
+lfds710_stack_pop = lfds710_stack_pop
+lfds710_stack_query = lfds710_stack_query
+
diff --git
a/src/libsds/external/liblfds710/build/wdk_7.1/readme_before_win_kernel_build.txt
b/src/libsds/external/liblfds710/build/wdk_7.1/readme_before_win_kernel_build.txt
new file mode 100644
index 0000000..1481c8d
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/wdk_7.1/readme_before_win_kernel_build.txt
@@ -0,0 +1,32 @@
+The Windows kernel build environment is primitive and has a number
+of severe limitations; in particular, all source files must be in
+one directory and it is not possible to choose the output binary type
+(static or dynamic library) from the build command line; rather,
+a string has to be modified in a text file used by the build (!)
+
+To deal with these limitations, it is necessary for a Windows kernel
+build to run a batch file prior to building.
+
+There are two batch files, one for static library builds and the other
+for dynamic library builds.
+
+They are both idempotent; you can run them as often as you like and
+switch between them as often as you want. It's all fine; whenever
+you run one of them, it will take you from whatever state you were
+previously in, into the state you want to be in.
+
+Both batch files copy all the sources file into a single directory,
+"/src/single_dir_for_windows_kernel/".
+
+The static library batch file will then copy "/sources.static" into
+"/src/single_dir_for_windows_kernel/", which will cause a static
+library to be built.
+
+The dynamic library batch file will then copy "/sources.dynamic" into
+"/src/single_dir_for_windows_kernel/", which will cause a dynamic
+library to be built. It will also copy "src/driver_entry.c" into
+"/src/single_dir_for_windows_kernel/", since the linker requires
+the DriverEntry function to exist for dynamic libraries, even
+though it's not used.
+
+
diff --git
a/src/libsds/external/liblfds710/build/wdk_7.1/runme_before_win_kernel_dynamic_lib_build.bat
b/src/libsds/external/liblfds710/build/wdk_7.1/runme_before_win_kernel_dynamic_lib_build.bat
new file mode 100644
index 0000000..3de69b1
--- /dev/null
+++
b/src/libsds/external/liblfds710/build/wdk_7.1/runme_before_win_kernel_dynamic_lib_build.bat
@@ -0,0 +1,24 @@
+@echo off
+rmdir /q /s single_dir_for_windows_kernel
1>nul 2>nul
+mkdir single_dir_for_windows_kernel
1>nul 2>nul
+
+copy /y ..\..\src\lfds710_btree_addonly_unbalanced\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_freelist\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_hash_addonly\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_list_addonly_singlylinked_ordered\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_list_addonly_singlylinked_unordered\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_misc\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_prng\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_queue_bounded_manyproducer_manyconsumer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_queue_bounded_singleproducer_singleconsumer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_queue_unbounded_manyproducer_manyconsumer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_ringbuffer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_stack\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+
+copy /y ..\..\src\liblfds710_internal.h
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y driver_entry_renamed_to_avoid_compiler_warning.c
single_dir_for_windows_kernel\driver_entry.c 1>nul 2>nul
+copy /y sources.dynamic
single_dir_for_windows_kernel\sources 1>nul 2>nul
+
+echo Windows kernel dynamic library build directory structure created.
+echo (Note the effects of this batch file are idempotent).
+
diff --git
a/src/libsds/external/liblfds710/build/wdk_7.1/runme_before_win_kernel_static_lib_build.bat
b/src/libsds/external/liblfds710/build/wdk_7.1/runme_before_win_kernel_static_lib_build.bat
new file mode 100644
index 0000000..aa69343
--- /dev/null
+++
b/src/libsds/external/liblfds710/build/wdk_7.1/runme_before_win_kernel_static_lib_build.bat
@@ -0,0 +1,23 @@
+@echo off
+rmdir /q /s single_dir_for_windows_kernel
1>nul 2>nul
+mkdir single_dir_for_windows_kernel
1>nul 2>nul
+
+copy /y ..\..\src\lfds710_btree_addonly_unbalanced\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_freelist\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_hash_addonly\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_list_addonly_singlylinked_ordered\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_list_addonly_singlylinked_unordered\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_misc\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_prng\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_queue_bounded_manyproducer_manyconsumer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_queue_bounded_singleproducer_singleconsumer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_queue_unbounded_manyproducer_manyconsumer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_ringbuffer\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y ..\..\src\lfds710_stack\*
single_dir_for_windows_kernel\ 1>nul 2>nul
+
+copy /y ..\..\src\liblfds710_internal.h
single_dir_for_windows_kernel\ 1>nul 2>nul
+copy /y sources.static
single_dir_for_windows_kernel\sources 1>nul 2>nul
+
+echo Windows kernel static library build directory structure created.
+echo (Note the effects of this batch file are idempotent).
+
diff --git a/src/libsds/external/liblfds710/build/wdk_7.1/sources.dynamic
b/src/libsds/external/liblfds710/build/wdk_7.1/sources.dynamic
new file mode 100644
index 0000000..8f19574
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/wdk_7.1/sources.dynamic
@@ -0,0 +1,66 @@
+MSC_WARNING_LEVEL = /WX /wd4127 /W4
+DLLDEF = ../liblfds710.def
+TARGETNAME = liblfds710
+TARGETPATH = ../../../bin/
+TARGETTYPE = EXPORT_DRIVER
+UMTYPE = nt
+USER_C_FLAGS = /DKERNEL_MODE /DNDEBUG
+
+INCLUDES = ../../../inc/
+SOURCES = lfds710_btree_addonly_unbalanced_cleanup.c \
+ lfds710_btree_addonly_unbalanced_get.c \
+ lfds710_btree_addonly_unbalanced_init.c \
+ lfds710_btree_addonly_unbalanced_insert.c \
+ lfds710_btree_addonly_unbalanced_query.c \
+ lfds710_freelist_cleanup.c \
+ lfds710_freelist_init.c \
+ lfds710_freelist_pop.c \
+ lfds710_freelist_push.c \
+ lfds710_freelist_query.c \
+ lfds710_hash_addonly_cleanup.c \
+ lfds710_hash_addonly_get.c \
+ lfds710_hash_addonly_init.c \
+ lfds710_hash_addonly_insert.c \
+ lfds710_hash_addonly_iterate.c \
+ lfds710_hash_addonly_query.c \
+ lfds710_list_addonly_singlylinked_ordered_cleanup.c \
+ lfds710_list_addonly_singlylinked_ordered_get.c \
+ lfds710_list_addonly_singlylinked_ordered_init.c \
+ lfds710_list_addonly_singlylinked_ordered_insert.c \
+ lfds710_list_addonly_singlylinked_ordered_query.c \
+ lfds710_list_addonly_singlylinked_unordered_cleanup.c \
+ lfds710_list_addonly_singlylinked_unordered_get.c \
+ lfds710_list_addonly_singlylinked_unordered_init.c \
+ lfds710_list_addonly_singlylinked_unordered_insert.c \
+ lfds710_list_addonly_singlylinked_unordered_query.c \
+ lfds710_misc_globals.c \
+ lfds710_misc_internal_backoff_init.c \
+ lfds710_misc_query.c \
+ lfds710_prng_init.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_init.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_query.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_init.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_query.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_init.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_query.c \
+ lfds710_ringbuffer_cleanup.c \
+ lfds710_ringbuffer_init.c \
+ lfds710_ringbuffer_query.c \
+ lfds710_ringbuffer_read.c \
+ lfds710_ringbuffer_write.c \
+ lfds710_stack_cleanup.c \
+ lfds710_stack_init.c \
+ lfds710_stack_pop.c \
+ lfds710_stack_push.c \
+ lfds710_stack_query.c \
+ driver_entry.c
+
diff --git a/src/libsds/external/liblfds710/build/wdk_7.1/sources.static
b/src/libsds/external/liblfds710/build/wdk_7.1/sources.static
new file mode 100644
index 0000000..c9724d4
--- /dev/null
+++ b/src/libsds/external/liblfds710/build/wdk_7.1/sources.static
@@ -0,0 +1,64 @@
+MSC_WARNING_LEVEL = /WX /wd4127 /W4
+TARGETNAME = liblfds710
+TARGETPATH = ../../../bin/
+TARGETTYPE = DRIVER_LIBRARY
+UMTYPE = nt
+USER_C_FLAGS = /DKERNEL_MODE /DNDEBUG
+
+INCLUDES = ../../../inc/
+SOURCES = lfds710_btree_addonly_unbalanced_cleanup.c \
+ lfds710_btree_addonly_unbalanced_get.c \
+ lfds710_btree_addonly_unbalanced_init.c \
+ lfds710_btree_addonly_unbalanced_insert.c \
+ lfds710_btree_addonly_unbalanced_query.c \
+ lfds710_freelist_cleanup.c \
+ lfds710_freelist_init.c \
+ lfds710_freelist_pop.c \
+ lfds710_freelist_push.c \
+ lfds710_freelist_query.c \
+ lfds710_hash_addonly_cleanup.c \
+ lfds710_hash_addonly_get.c \
+ lfds710_hash_addonly_init.c \
+ lfds710_hash_addonly_insert.c \
+ lfds710_hash_addonly_iterate.c \
+ lfds710_hash_addonly_query.c \
+ lfds710_list_addonly_singlylinked_ordered_cleanup.c \
+ lfds710_list_addonly_singlylinked_ordered_get.c \
+ lfds710_list_addonly_singlylinked_ordered_init.c \
+ lfds710_list_addonly_singlylinked_ordered_insert.c \
+ lfds710_list_addonly_singlylinked_ordered_query.c \
+ lfds710_list_addonly_singlylinked_unordered_cleanup.c \
+ lfds710_list_addonly_singlylinked_unordered_get.c \
+ lfds710_list_addonly_singlylinked_unordered_init.c \
+ lfds710_list_addonly_singlylinked_unordered_insert.c \
+ lfds710_list_addonly_singlylinked_unordered_query.c \
+ lfds710_misc_globals.c \
+ lfds710_misc_internal_backoff_init.c \
+ lfds710_misc_query.c \
+ lfds710_prng_init.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_init.c \
+ lfds710_queue_bounded_manyproducer_manyconsumer_query.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_init.c \
+ lfds710_queue_bounded_singleproducer_singleconsumer_query.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_init.c \
+ lfds710_queue_unbounded_manyproducer_manyconsumer_query.c \
+ lfds710_ringbuffer_cleanup.c \
+ lfds710_ringbuffer_init.c \
+ lfds710_ringbuffer_query.c \
+ lfds710_ringbuffer_read.c \
+ lfds710_ringbuffer_write.c \
+ lfds710_stack_cleanup.c \
+ lfds710_stack_init.c \
+ lfds710_stack_pop.c \
+ lfds710_stack_push.c \
+ lfds710_stack_query.c
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710.h
b/src/libsds/external/liblfds710/inc/liblfds710.h
new file mode 100644
index 0000000..0753f70
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710.h
@@ -0,0 +1,33 @@
+#ifndef LIBLFDS710_H
+
+ /***** defines *****/
+ #define LIBLFDS710_H
+
+ /***** pragmas on *****/
+ // #pragma warning( push )
+ // #pragma warning( disable : 4324 ) // TRD :
4324 disables MSVC warnings for structure alignment padding due to alignment specifiers
+ // #pragma prefast( disable : 28113 28182 28183, "blah" )
+
+ /***** includes *****/
+ #include "liblfds710/lfds710_porting_abstraction_layer_compiler.h"
+ #include "liblfds710/lfds710_porting_abstraction_layer_operating_system.h"
+ #include "liblfds710/lfds710_porting_abstraction_layer_processor.h"
+
+ #include "liblfds710/lfds710_prng.h" //
TRD : misc requires prng
+ #include "liblfds710/lfds710_misc.h" //
TRD : everything after depends on misc
+ // #include "liblfds710/lfds710_btree_addonly_unbalanced.h"
// TRD : hash_addonly depends on btree_addonly_unbalanced
+ // #include "liblfds710/lfds710_freelist.h"
+ // #include "liblfds710/lfds710_hash_addonly.h"
+ // #include "liblfds710/lfds710_list_addonly_singlylinked_ordered.h"
+ // #include "liblfds710/lfds710_list_addonly_singlylinked_unordered.h"
+ // #include "liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h"
+ // #include
"liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h"
+ #include "liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h"
+ // #include "liblfds710/lfds710_ringbuffer.h"
+ // #include "liblfds710/lfds710_stack.h"
+
+ /***** pragmas off *****/
+ // #pragma warning( pop )
+
+#endif
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_btree_addonly_unbalanced.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_btree_addonly_unbalanced.h
new file mode 100644
index 0000000..5984928
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_btree_addonly_unbalanced.h
@@ -0,0 +1,121 @@
+/***** defines *****/
+#define LFDS710_BTREE_AU_GET_KEY_FROM_ELEMENT( btree_au_element ) (
(btree_au_element).key )
+#define LFDS710_BTREE_AU_SET_KEY_IN_ELEMENT( btree_au_element, new_key ) (
(btree_au_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_BTREE_AU_GET_VALUE_FROM_ELEMENT( btree_au_element ) (
LFDS710_MISC_BARRIER_LOAD, (btree_au_element).value )
+#define LFDS710_BTREE_AU_SET_VALUE_IN_ELEMENT( btree_au_element, new_value ) {
LFDS710_PAL_ATOMIC_SET( &(btree_au_element).value, new_value ); }
+#define LFDS710_BTREE_AU_GET_USER_STATE_FROM_STATE( btree_au_state ) (
(btree_au_state).user_state )
+
+/***** enums *****/
+enum lfds710_btree_au_absolute_position
+{
+ LFDS710_BTREE_AU_ABSOLUTE_POSITION_ROOT,
+ LFDS710_BTREE_AU_ABSOLUTE_POSITION_SMALLEST_IN_TREE,
+ LFDS710_BTREE_AU_ABSOLUTE_POSITION_LARGEST_IN_TREE
+};
+
+enum lfds710_btree_au_existing_key
+{
+ LFDS710_BTREE_AU_EXISTING_KEY_OVERWRITE,
+ LFDS710_BTREE_AU_EXISTING_KEY_FAIL
+};
+
+enum lfds710_btree_au_insert_result
+{
+ LFDS710_BTREE_AU_INSERT_RESULT_FAILURE_EXISTING_KEY,
+ LFDS710_BTREE_AU_INSERT_RESULT_SUCCESS_OVERWRITE,
+ LFDS710_BTREE_AU_INSERT_RESULT_SUCCESS
+};
+
+enum lfds710_btree_au_query
+{
+ LFDS710_BTREE_AU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
+ LFDS710_BTREE_AU_QUERY_SINGLETHREADED_VALIDATE
+};
+
+enum lfds710_btree_au_relative_position
+{
+ LFDS710_BTREE_AU_RELATIVE_POSITION_UP,
+ LFDS710_BTREE_AU_RELATIVE_POSITION_LEFT,
+ LFDS710_BTREE_AU_RELATIVE_POSITION_RIGHT,
+ LFDS710_BTREE_AU_RELATIVE_POSITION_SMALLEST_ELEMENT_BELOW_CURRENT_ELEMENT,
+ LFDS710_BTREE_AU_RELATIVE_POSITION_LARGEST_ELEMENT_BELOW_CURRENT_ELEMENT,
+ LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_SMALLER_ELEMENT_IN_ENTIRE_TREE,
+ LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_LARGER_ELEMENT_IN_ENTIRE_TREE
+};
+
+/***** structs *****/
+struct lfds710_btree_au_element
+{
+ /* TRD : we are add-only, so these elements are only written once
+ as such, the write is wholly negligible
+ we are only concerned with getting as many structs in one cache line as we
can
+ */
+
+ struct lfds710_btree_au_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile left,
+ *volatile right,
+ *volatile up;
+
+ void LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile value;
+
+ void
+ *key;
+};
+
+struct lfds710_btree_au_state
+{
+ struct lfds710_btree_au_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile root;
+
+ int
+ (*key_compare_function)( void const *new_key, void const *existing_key );
+
+ enum lfds710_btree_au_existing_key
+ existing_key;
+
+ void
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ insert_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_btree_au_init_valid_on_current_logical_core( struct lfds710_btree_au_state
*baus,
+ int
(*key_compare_function)(void const *new_key, void const *existing_key),
+ enum
lfds710_btree_au_existing_key existing_key,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_btree_au_cleanup( struct lfds710_btree_au_state *baus,
+ void (*element_cleanup_callback)(struct
lfds710_btree_au_state *baus, struct lfds710_btree_au_element *baue) );
+
+enum lfds710_btree_au_insert_result lfds710_btree_au_insert( struct
lfds710_btree_au_state *baus,
+ struct
lfds710_btree_au_element *baue,
+ struct
lfds710_btree_au_element **existing_baue );
+ // TRD : if a link collides with an existing key and existing_baue is non-NULL,
existing_baue is set to the existing element
+
+int lfds710_btree_au_get_by_key( struct lfds710_btree_au_state *baus,
+ int (*key_compare_function)(void const *new_key, void
const *existing_key),
+ void *key,
+ struct lfds710_btree_au_element **baue );
+
+int lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position( struct
lfds710_btree_au_state *baus,
+ struct
lfds710_btree_au_element **baue,
+ enum
lfds710_btree_au_absolute_position absolute_position,
+ enum
lfds710_btree_au_relative_position relative_position );
+ // TRD : if *baue is NULL, we get the element at position, otherwise we move from *baue
according to direction
+
+int lfds710_btree_au_get_by_absolute_position( struct lfds710_btree_au_state *baus,
+ struct lfds710_btree_au_element **baue,
+ enum lfds710_btree_au_absolute_position
absolute_position );
+
+int lfds710_btree_au_get_by_relative_position( struct lfds710_btree_au_element **baue,
+ enum lfds710_btree_au_relative_position
relative_position );
+
+void lfds710_btree_au_query( struct lfds710_btree_au_state *baus,
+ enum lfds710_btree_au_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_freelist.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_freelist.h
new file mode 100644
index 0000000..73c3315
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_freelist.h
@@ -0,0 +1,70 @@
+/***** defines *****/
+#define LFDS710_FREELIST_GET_KEY_FROM_ELEMENT( freelist_element ) (
(freelist_element).key )
+#define LFDS710_FREELIST_SET_KEY_IN_ELEMENT( freelist_element, new_key ) (
(freelist_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_FREELIST_GET_VALUE_FROM_ELEMENT( freelist_element ) (
(freelist_element).value )
+#define LFDS710_FREELIST_SET_VALUE_IN_ELEMENT( freelist_element, new_value ) (
(freelist_element).value = (void *) (lfds710_pal_uint_t) (new_value) )
+#define LFDS710_FREELIST_GET_USER_STATE_FROM_STATE( freelist_state ) (
(freelist_state).user_state )
+
+#define LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS (
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES / sizeof(struct lfds710_freelist_element *) )
+
+/***** enums *****/
+enum lfds710_freelist_query
+{
+ LFDS710_FREELIST_QUERY_SINGLETHREADED_GET_COUNT,
+ LFDS710_FREELIST_QUERY_SINGLETHREADED_VALIDATE,
+ LFDS710_FREELIST_QUERY_GET_ELIMINATION_ARRAY_EXTRA_ELEMENTS_IN_FREELIST_ELEMENTS
+};
+
+/***** structures *****/
+struct lfds710_freelist_element
+{
+ struct lfds710_freelist_element
+ *next;
+
+ void
+ *key,
+ *value;
+};
+
+struct lfds710_freelist_state
+{
+ struct lfds710_freelist_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *volatile top[PAC_SIZE];
+
+ lfds710_pal_uint_t LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ elimination_array_size_in_elements;
+
+ struct lfds710_freelist_element * volatile
+
(*elimination_array)[LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS];
+
+ void
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ pop_backoff,
+ push_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_freelist_init_valid_on_current_logical_core( struct lfds710_freelist_state
*fs,
+ struct lfds710_freelist_element
* volatile
(*elimination_array)[LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS],
+ lfds710_pal_uint_t
elimination_array_size_in_elements,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_freelist_cleanup( struct lfds710_freelist_state *fs,
+ void (*element_cleanup_callback)(struct
lfds710_freelist_state *fs, struct lfds710_freelist_element *fe) );
+
+void lfds710_freelist_push( struct lfds710_freelist_state *fs,
+ struct lfds710_freelist_element *fe,
+ struct lfds710_prng_st_state *psts );
+
+int lfds710_freelist_pop( struct lfds710_freelist_state *fs,
+ struct lfds710_freelist_element **fe,
+ struct lfds710_prng_st_state *psts );
+
+void lfds710_freelist_query( struct lfds710_freelist_state *fs,
+ enum lfds710_freelist_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_hash_addonly.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_hash_addonly.h
new file mode 100644
index 0000000..ea60bfa
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_hash_addonly.h
@@ -0,0 +1,136 @@
+/***** defines *****/
+#define LFDS710_HASH_A_GET_KEY_FROM_ELEMENT( hash_a_element ) (
(hash_a_element).key )
+#define LFDS710_HASH_A_SET_KEY_IN_ELEMENT( hash_a_element, new_key ) (
(hash_a_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_HASH_A_GET_VALUE_FROM_ELEMENT( hash_a_element ) (
LFDS710_MISC_BARRIER_LOAD, (hash_a_element).value )
+#define LFDS710_HASH_A_SET_VALUE_IN_ELEMENT( hash_a_element, new_value ) {
LFDS710_PAL_ATOMIC_SET( &(hash_a_element).value, new_value ); }
+#define LFDS710_HASH_A_GET_USER_STATE_FROM_STATE( hash_a_state ) (
(hash_a_state).user_state )
+
+// TRD : a quality hash function, provided for user convenience - note hash must be
initialized to 0 before the first call by the user
+
+#if( LFDS710_PAL_ALIGN_SINGLE_POINTER == 4 )
+ // TRD : void *data, lfds710_pal_uint_t data_length_in_bytes, lfds710_pal_uint_t hash
+ #define LFDS710_HASH_A_HASH_FUNCTION( data, data_length_in_bytes, hash ) {
\
+
lfds710_pal_uint_t \
+ loop;
\
+
\
+ for( loop =
0 ; loop < (data_length_in_bytes) ; loop++ ) \
+ {
\
+ (hash) +=
*( (char unsigned *) (data) + loop ); \
+ (hash) =
((hash) ^ ((hash) >> 16)) * 0x85ebca6bUL; \
+ (hash) =
((hash) ^ ((hash) >> 13)) * 0xc2b2ae35UL; \
+ (hash) =
(hash ^ (hash >> 16)); \
+ }
\
+ }
+#endif
+
+#if( LFDS710_PAL_ALIGN_SINGLE_POINTER == 8 )
+ // TRD : void *data, lfds710_pal_uint_t data_length_in_bytes, lfds710_pal_uint_t hash
+ #define LFDS710_HASH_A_HASH_FUNCTION( data, data_length_in_bytes, hash ) {
\
+
lfds710_pal_uint_t \
+ loop;
\
+
\
+ for( loop =
0 ; loop < (data_length_in_bytes) ; loop++ ) \
+ {
\
+ (hash) +=
*( (char unsigned *) (data) + loop ); \
+ (hash) =
((hash) ^ ((hash) >> 30)) * 0xBF58476D1CE4E5B9ULL; \
+ (hash) =
((hash) ^ ((hash) >> 27)) * 0x94D049BB133111EBULL; \
+ (hash) =
(hash ^ (hash >> 31)); \
+ }
\
+ }
+#endif
+
+/***** enums *****/
+enum lfds710_hash_a_existing_key
+{
+ LFDS710_HASH_A_EXISTING_KEY_OVERWRITE,
+ LFDS710_HASH_A_EXISTING_KEY_FAIL
+};
+
+enum lfds710_hash_a_insert_result
+{
+ LFDS710_HASH_A_PUT_RESULT_FAILURE_EXISTING_KEY,
+ LFDS710_HASH_A_PUT_RESULT_SUCCESS_OVERWRITE,
+ LFDS710_HASH_A_PUT_RESULT_SUCCESS
+};
+
+enum lfds710_hash_a_query
+{
+ LFDS710_HASH_A_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
+ LFDS710_HASH_A_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structs *****/
+struct lfds710_hash_a_element
+{
+ struct lfds710_btree_au_element
+ baue;
+
+ void
+ *key;
+
+ void LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile value;
+};
+
+struct lfds710_hash_a_iterate
+{
+ struct lfds710_btree_au_element
+ *baue;
+
+ struct lfds710_btree_au_state
+ *baus,
+ *baus_end;
+};
+
+struct lfds710_hash_a_state
+{
+ enum lfds710_hash_a_existing_key
+ existing_key;
+
+ int
+ (*key_compare_function)( void const *new_key, void const *existing_key );
+
+ lfds710_pal_uint_t
+ array_size;
+
+ struct lfds710_btree_au_state
+ *baus_array;
+
+ void
+ (*element_cleanup_callback)( struct lfds710_hash_a_state *has, struct
lfds710_hash_a_element *hae ),
+ (*key_hash_function)( void const *key, lfds710_pal_uint_t *hash ),
+ *user_state;
+};
+
+/***** public prototypes *****/
+void lfds710_hash_a_init_valid_on_current_logical_core( struct lfds710_hash_a_state
*has,
+ struct lfds710_btree_au_state
*baus_array,
+ lfds710_pal_uint_t array_size,
+ int (*key_compare_function)(void
const *new_key, void const *existing_key),
+ void (*key_hash_function)(void
const *key, lfds710_pal_uint_t *hash),
+ enum lfds710_hash_a_existing_key
existing_key,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_hash_a_cleanup( struct lfds710_hash_a_state *has,
+ void (*element_cleanup_function)(struct lfds710_hash_a_state
*has, struct lfds710_hash_a_element *hae) );
+
+enum lfds710_hash_a_insert_result lfds710_hash_a_insert( struct lfds710_hash_a_state
*has,
+ struct lfds710_hash_a_element
*hae,
+ struct lfds710_hash_a_element
**existing_hae );
+ // TRD : if existing_value is not NULL and the key exists, existing_hae is set to the
hash element of the existing key
+
+int lfds710_hash_a_get_by_key( struct lfds710_hash_a_state *has,
+ int (*key_compare_function)(void const *new_key, void
const *existing_key),
+ void (*key_hash_function)(void const *key,
lfds710_pal_uint_t *hash),
+ void *key,
+ struct lfds710_hash_a_element **hae );
+
+void lfds710_hash_a_iterate_init( struct lfds710_hash_a_state *has, struct
lfds710_hash_a_iterate *hai );
+int lfds710_hash_a_iterate( struct lfds710_hash_a_iterate *hai, struct
lfds710_hash_a_element **hae );
+
+void lfds710_hash_a_query( struct lfds710_hash_a_state *has,
+ enum lfds710_hash_a_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
new file mode 100644
index 0000000..8b540a1
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
@@ -0,0 +1,87 @@
+/***** defines *****/
+#define LFDS710_LIST_ASO_GET_START( list_aso_state )
( LFDS710_MISC_BARRIER_LOAD, (list_aso_state).start->next )
+#define LFDS710_LIST_ASO_GET_NEXT( list_aso_element )
( LFDS710_MISC_BARRIER_LOAD, (list_aso_element).next )
+#define LFDS710_LIST_ASO_GET_START_AND_THEN_NEXT( list_aso_state,
pointer_to_list_aso_element ) ( (pointer_to_list_aso_element) == NULL ? (
(pointer_to_list_aso_element) = LFDS710_LIST_ASO_GET_START(list_aso_state) ) : (
(pointer_to_list_aso_element) = LFDS710_LIST_ASO_GET_NEXT(*(pointer_to_list_aso_element))
) )
+#define LFDS710_LIST_ASO_GET_KEY_FROM_ELEMENT( list_aso_element )
( (list_aso_element).key )
+#define LFDS710_LIST_ASO_SET_KEY_IN_ELEMENT( list_aso_element, new_key )
( (list_aso_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_LIST_ASO_GET_VALUE_FROM_ELEMENT( list_aso_element )
( LFDS710_MISC_BARRIER_LOAD, (list_aso_element).value )
+#define LFDS710_LIST_ASO_SET_VALUE_IN_ELEMENT( list_aso_element, new_value )
{ LFDS710_PAL_ATOMIC_SET( &(list_aso_element).value, new_value ); }
+#define LFDS710_LIST_ASO_GET_USER_STATE_FROM_STATE( list_aso_state )
( (list_aso_state).user_state )
+
+/***** enums *****/
+enum lfds710_list_aso_existing_key
+{
+ LFDS710_LIST_ASO_EXISTING_KEY_OVERWRITE,
+ LFDS710_LIST_ASO_EXISTING_KEY_FAIL
+};
+
+enum lfds710_list_aso_insert_result
+{
+ LFDS710_LIST_ASO_INSERT_RESULT_FAILURE_EXISTING_KEY,
+ LFDS710_LIST_ASO_INSERT_RESULT_SUCCESS_OVERWRITE,
+ LFDS710_LIST_ASO_INSERT_RESULT_SUCCESS
+};
+
+enum lfds710_list_aso_query
+{
+ LFDS710_LIST_ASO_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
+ LFDS710_LIST_ASO_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_list_aso_element
+{
+ struct lfds710_list_aso_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile next;
+
+ void LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile value;
+
+ void
+ *key;
+};
+
+struct lfds710_list_aso_state
+{
+ struct lfds710_list_aso_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ dummy_element;
+
+ struct lfds710_list_aso_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *start;
+
+ int
+ (*key_compare_function)( void const *new_key, void const *existing_key );
+
+ enum lfds710_list_aso_existing_key
+ existing_key;
+
+ void
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ insert_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_list_aso_init_valid_on_current_logical_core( struct lfds710_list_aso_state
*lasos,
+ int
(*key_compare_function)(void const *new_key, void const *existing_key),
+ enum
lfds710_list_aso_existing_key existing_key,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_list_aso_cleanup( struct lfds710_list_aso_state *lasos,
+ void (*element_cleanup_callback)(struct
lfds710_list_aso_state *lasos, struct lfds710_list_aso_element *lasoe) );
+
+enum lfds710_list_aso_insert_result lfds710_list_aso_insert( struct
lfds710_list_aso_state *lasos,
+ struct
lfds710_list_aso_element *lasoe,
+ struct
lfds710_list_aso_element **existing_lasoe );
+
+int lfds710_list_aso_get_by_key( struct lfds710_list_aso_state *lasos,
+ void *key,
+ struct lfds710_list_aso_element **lasoe );
+
+void lfds710_list_aso_query( struct lfds710_list_aso_state *lasos,
+ enum lfds710_list_aso_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
new file mode 100644
index 0000000..5bb32c4
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
@@ -0,0 +1,90 @@
+/***** defines *****/
+#define LFDS710_LIST_ASU_GET_START( list_asu_state )
( LFDS710_MISC_BARRIER_LOAD, (list_asu_state).start->next )
+#define LFDS710_LIST_ASU_GET_NEXT( list_asu_element )
( LFDS710_MISC_BARRIER_LOAD, (list_asu_element).next )
+#define LFDS710_LIST_ASU_GET_START_AND_THEN_NEXT( list_asu_state,
pointer_to_list_asu_element ) ( (pointer_to_list_asu_element) == NULL ? (
(pointer_to_list_asu_element) = LFDS710_LIST_ASU_GET_START(list_asu_state) ) : (
(pointer_to_list_asu_element) = LFDS710_LIST_ASU_GET_NEXT(*(pointer_to_list_asu_element))
) )
+#define LFDS710_LIST_ASU_GET_KEY_FROM_ELEMENT( list_asu_element )
( (list_asu_element).key )
+#define LFDS710_LIST_ASU_SET_KEY_IN_ELEMENT( list_asu_element, new_key )
( (list_asu_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_LIST_ASU_GET_VALUE_FROM_ELEMENT( list_asu_element )
( LFDS710_MISC_BARRIER_LOAD, (list_asu_element).value )
+#define LFDS710_LIST_ASU_SET_VALUE_IN_ELEMENT( list_asu_element, new_value )
{ LFDS710_PAL_ATOMIC_SET( &(list_asu_element).value, new_value ); }
+#define LFDS710_LIST_ASU_GET_USER_STATE_FROM_STATE( list_asu_state )
( (list_asu_state).user_state )
+
+/***** enums *****/
+enum lfds710_list_asu_position
+{
+ LFDS710_LIST_ASU_POSITION_START,
+ LFDS710_LIST_ASU_POSITION_END,
+ LFDS710_LIST_ASU_POSITION_AFTER
+};
+
+enum lfds710_list_asu_query
+{
+ LFDS710_LIST_ASU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
+ LFDS710_LIST_ASU_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_list_asu_element
+{
+ struct lfds710_list_asu_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile next;
+
+ void LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *volatile value;
+
+ void
+ *key;
+};
+
+struct lfds710_list_asu_state
+{
+ struct lfds710_list_asu_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ dummy_element;
+
+ struct lfds710_list_asu_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *volatile end;
+
+ struct lfds710_list_asu_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *start;
+
+ void
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ after_backoff,
+ end_backoff,
+ start_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_list_asu_init_valid_on_current_logical_core( struct lfds710_list_asu_state
*lasus,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_list_asu_cleanup( struct lfds710_list_asu_state *lasus,
+ void (*element_cleanup_callback)(struct
lfds710_list_asu_state *lasus, struct lfds710_list_asu_element *lasue) );
+
+void lfds710_list_asu_insert_at_position( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue,
+ struct lfds710_list_asu_element
*lasue_predecessor,
+ enum lfds710_list_asu_position position );
+
+void lfds710_list_asu_insert_at_start( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue );
+
+void lfds710_list_asu_insert_at_end( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue );
+
+void lfds710_list_asu_insert_after_element( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue,
+ struct lfds710_list_asu_element
*lasue_predecessor );
+
+int lfds710_list_asu_get_by_key( struct lfds710_list_asu_state *lasus,
+ int (*key_compare_function)(void const *new_key, void
const *existing_key),
+ void *key,
+ struct lfds710_list_asu_element **lasue );
+
+void lfds710_list_asu_query( struct lfds710_list_asu_state *lasus,
+ enum lfds710_list_asu_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_misc.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_misc.h
new file mode 100644
index 0000000..ea14fb4
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_misc.h
@@ -0,0 +1,233 @@
+/***** defines *****/
+#define LFDS710_MISC_VERSION_STRING "7.1.0"
+#define LFDS710_MISC_VERSION_INTEGER 710
+
+#ifndef NULL
+ #define NULL ( (void *) 0 )
+#endif
+
+#define POINTER 0
+#define COUNTER 1
+#define PAC_SIZE 2
+
+#define LFDS710_MISC_DELIBERATELY_CRASH { char *c = 0; *c = 0; }
+
+#if( !defined LFDS710_PAL_ATOMIC_ADD )
+ #define LFDS710_PAL_NO_ATOMIC_ADD
+ #define LFDS710_MISC_ATOMIC_SUPPORT_ADD 0
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ LFDS710_PAL_ASSERT( !"LFDS710_PAL_ATOMIC_ADD not implemented for this
platform." ); \
+ LFDS710_MISC_DELIBERATELY_CRASH;
\
+ }
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_ADD 1
+#endif
+
+#if( !defined LFDS710_PAL_ATOMIC_CAS )
+ #define LFDS710_PAL_NO_ATOMIC_CAS
+ #define LFDS710_MISC_ATOMIC_SUPPORT_CAS 0
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result ) \
+ {
\
+ LFDS710_PAL_ASSERT( !"LFDS710_PAL_ATOMIC_CAS not implemented for this
platform." ); \
+ (result) = 0;
\
+ LFDS710_MISC_DELIBERATELY_CRASH;
\
+ }
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_CAS 1
+#endif
+
+#if( !defined LFDS710_PAL_ATOMIC_DWCAS )
+ #define LFDS710_PAL_NO_ATOMIC_DWCAS
+ #define LFDS710_MISC_ATOMIC_SUPPORT_DWCAS 0
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result ) \
+ {
\
+ LFDS710_PAL_ASSERT( !"LFDS710_PAL_ATOMIC_DWCAS not implemented for this
platform." ); \
+ (result) = 0;
\
+ LFDS710_MISC_DELIBERATELY_CRASH;
\
+ }
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_DWCAS 1
+#endif
+
+#if( !defined LFDS710_PAL_ATOMIC_EXCHANGE )
+ #define LFDS710_PAL_NO_ATOMIC_EXCHANGE
+ #define LFDS710_MISC_ATOMIC_SUPPORT_EXCHANGE 0
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, new_value, original_value,
value_type ) \
+ {
\
+ LFDS710_PAL_ASSERT( !"LFDS710_PAL_ATOMIC_EXCHANGE not implemented for this
platform." ); \
+ LFDS710_MISC_DELIBERATELY_CRASH;
\
+ }
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_EXCHANGE 1
+#endif
+
+#if( !defined LFDS710_PAL_ATOMIC_SET )
+ #define LFDS710_PAL_NO_ATOMIC_SET
+ #define LFDS710_MISC_ATOMIC_SUPPORT_SET 0
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value )
\
+ {
\
+ LFDS710_PAL_ASSERT( !"LFDS710_PAL_ATOMIC_SET not implemented for this
platform." ); \
+ LFDS710_MISC_DELIBERATELY_CRASH;
\
+ }
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_SET 1
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_COMPILER_LOAD && defined
LFDS710_PAL_BARRIER_PROCESSOR_LOAD )
+ #define LFDS710_MISC_BARRIER_LOAD ( LFDS710_PAL_BARRIER_COMPILER_LOAD,
LFDS710_PAL_BARRIER_PROCESSOR_LOAD, LFDS710_PAL_BARRIER_COMPILER_LOAD )
+#endif
+
+#if( (!defined LFDS710_PAL_BARRIER_COMPILER_LOAD || defined
LFDS710_PAL_COMPILER_BARRIERS_MISSING_PRESUMED_HAVING_A_GOOD_TIME) && defined
LFDS710_PAL_BARRIER_PROCESSOR_LOAD )
+ #define LFDS710_MISC_BARRIER_LOAD LFDS710_PAL_BARRIER_PROCESSOR_LOAD
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_COMPILER_LOAD && !defined
LFDS710_PAL_BARRIER_PROCESSOR_LOAD )
+ #define LFDS710_MISC_BARRIER_LOAD LFDS710_PAL_BARRIER_COMPILER_LOAD
+#endif
+
+#if( !defined LFDS710_PAL_BARRIER_COMPILER_LOAD && !defined
LFDS710_PAL_BARRIER_PROCESSOR_LOAD )
+ #define LFDS710_MISC_BARRIER_LOAD
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_COMPILER_STORE && defined
LFDS710_PAL_BARRIER_PROCESSOR_STORE )
+ #define LFDS710_MISC_BARRIER_STORE ( LFDS710_PAL_BARRIER_COMPILER_STORE,
LFDS710_PAL_BARRIER_PROCESSOR_STORE, LFDS710_PAL_BARRIER_COMPILER_STORE )
+#endif
+
+#if( (!defined LFDS710_PAL_BARRIER_COMPILER_STORE || defined
LFDS710_PAL_COMPILER_BARRIERS_MISSING_PRESUMED_HAVING_A_GOOD_TIME) && defined
LFDS710_PAL_BARRIER_PROCESSOR_STORE )
+ #define LFDS710_MISC_BARRIER_STORE LFDS710_PAL_BARRIER_PROCESSOR_STORE
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_COMPILER_STORE && !defined
LFDS710_PAL_BARRIER_PROCESSOR_STORE )
+ #define LFDS710_MISC_BARRIER_STORE LFDS710_PAL_BARRIER_COMPILER_STORE
+#endif
+
+#if( !defined LFDS710_PAL_BARRIER_COMPILER_STORE && !defined
LFDS710_PAL_BARRIER_PROCESSOR_STORE )
+ #define LFDS710_MISC_BARRIER_STORE
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_COMPILER_FULL && defined
LFDS710_PAL_BARRIER_PROCESSOR_FULL )
+ #define LFDS710_MISC_BARRIER_FULL ( LFDS710_PAL_BARRIER_COMPILER_FULL,
LFDS710_PAL_BARRIER_PROCESSOR_FULL, LFDS710_PAL_BARRIER_COMPILER_FULL )
+#endif
+
+#if( (!defined LFDS710_PAL_BARRIER_COMPILER_FULL || defined
LFDS710_PAL_COMPILER_BARRIERS_MISSING_PRESUMED_HAVING_A_GOOD_TIME) && defined
LFDS710_PAL_BARRIER_PROCESSOR_FULL )
+ #define LFDS710_MISC_BARRIER_FULL LFDS710_PAL_BARRIER_PROCESSOR_FULL
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_COMPILER_FULL && !defined
LFDS710_PAL_BARRIER_PROCESSOR_FULL )
+ #define LFDS710_MISC_BARRIER_FULL LFDS710_PAL_BARRIER_COMPILER_FULL
+#endif
+
+#if( !defined LFDS710_PAL_BARRIER_COMPILER_FULL && !defined
LFDS710_PAL_BARRIER_PROCESSOR_FULL )
+ #define LFDS710_MISC_BARRIER_FULL
+#endif
+
+#if( (defined LFDS710_PAL_BARRIER_COMPILER_LOAD && defined
LFDS710_PAL_BARRIER_COMPILER_STORE && defined LFDS710_PAL_BARRIER_COMPILER_FULL)
|| (defined LFDS710_PAL_COMPILER_BARRIERS_MISSING_PRESUMED_HAVING_A_GOOD_TIME) )
+ #define LFDS710_MISC_ATOMIC_SUPPORT_COMPILER_BARRIERS 1
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_COMPILER_BARRIERS 0
+#endif
+
+#if( defined LFDS710_PAL_BARRIER_PROCESSOR_LOAD && defined
LFDS710_PAL_BARRIER_PROCESSOR_STORE && defined LFDS710_PAL_BARRIER_PROCESSOR_FULL
)
+ #define LFDS710_MISC_ATOMIC_SUPPORT_PROCESSOR_BARRIERS 1
+#else
+ #define LFDS710_MISC_ATOMIC_SUPPORT_PROCESSOR_BARRIERS 0
+#endif
+
+#define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
LFDS710_MISC_BARRIER_LOAD
+#define LFDS710_MISC_FLUSH
{ LFDS710_MISC_BARRIER_STORE; lfds710_misc_force_store(); }
+
+/***** enums *****/
+enum lfds710_misc_cas_strength
+{
+ // TRD : GCC defined values
+ LFDS710_MISC_CAS_STRENGTH_STRONG = 0,
+ LFDS710_MISC_CAS_STRENGTH_WEAK = 1,
+};
+
+enum lfds710_misc_validity
+{
+ LFDS710_MISC_VALIDITY_UNKNOWN,
+ LFDS710_MISC_VALIDITY_VALID,
+ LFDS710_MISC_VALIDITY_INVALID_LOOP,
+ LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS,
+ LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS,
+ LFDS710_MISC_VALIDITY_INVALID_TEST_DATA,
+ LFDS710_MISC_VALIDITY_INVALID_ORDER,
+ LFDS710_MISC_VALIDITY_INVALID_ATOMIC_FAILED,
+ LFDS710_MISC_VALIDITY_INDETERMINATE_NONATOMIC_PASSED,
+};
+
+enum lfds710_misc_flag
+{
+ LFDS710_MISC_FLAG_LOWERED,
+ LFDS710_MISC_FLAG_RAISED
+};
+
+enum lfds710_misc_query
+{
+ LFDS710_MISC_QUERY_GET_BUILD_AND_VERSION_STRING
+};
+
+enum lfds710_misc_data_structure
+{
+ LFDS710_MISC_DATA_STRUCTURE_BTREE_AU,
+ LFDS710_MISC_DATA_STRUCTURE_FREELIST,
+ LFDS710_MISC_DATA_STRUCTURE_HASH_A,
+ LFDS710_MISC_DATA_STRUCTURE_LIST_AOS,
+ LFDS710_MISC_DATA_STRUCTURE_LIST_ASU,
+ LFDS710_MISC_DATA_STRUCTURE_QUEUE_BMM,
+ LFDS710_MISC_DATA_STRUCTURE_QUEUE_BSS,
+ LFDS710_MISC_DATA_STRUCTURE_QUEUE_UMM,
+ LFDS710_MISC_DATA_STRUCTURE_RINGBUFFER,
+ LFDS710_MISC_DATA_STRUCTURE_STACK,
+ LFDS710_MISC_DATA_STRUCTURE_COUNT
+};
+
+/***** struct *****/
+struct lfds710_misc_backoff_state
+{
+ lfds710_pal_uint_t volatile LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ lock;
+
+ lfds710_pal_uint_t
+ backoff_iteration_frequency_counters[2],
+ metric,
+ total_operations;
+};
+
+struct lfds710_misc_globals
+{
+ struct lfds710_prng_state
+ ps;
+};
+
+struct lfds710_misc_validation_info
+{
+ lfds710_pal_uint_t
+ min_elements,
+ max_elements;
+};
+
+/***** externs *****/
+extern struct lfds710_misc_globals
+ lfds710_misc_globals;
+
+/***** public prototypes *****/
+static LFDS710_PAL_INLINE void lfds710_misc_force_store( void );
+
+void lfds710_misc_query( enum lfds710_misc_query query_type, void *query_input, void
*query_output );
+
+/***** public in-line functions *****/
+// #pragma prefast( disable : 28112, "blah" )
+
+static LFDS710_PAL_INLINE void lfds710_misc_force_store()
+{
+ lfds710_pal_uint_t volatile LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ destination;
+
+ LFDS710_PAL_ATOMIC_SET( &destination, 0 );
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
new file mode 100644
index 0000000..674aff9
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
@@ -0,0 +1,446 @@
+/****************************************************************************/
+#if( defined __GNUC__ )
+ // TRD : makes checking GCC versions much tidier
+ #define LFDS710_PAL_GCC_VERSION ( __GNUC__ * 100 + __GNUC_MINOR__ * 10 +
__GNUC_PATCHLEVEL__ )
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined _MSC_VER && _MSC_VER >= 1400 )
+
+ #ifdef LFDS710_PAL_COMPILER
+ #error More than one porting abstraction layer matches the current platform in
lfds710_porting_abstraction_layer_compiler.h
+ #endif
+
+ #define LFDS710_PAL_COMPILER
+
+ #define LFDS710_PAL_COMPILER_STRING "MSVC"
+
+ #define LFDS710_PAL_ALIGN(alignment) __declspec( align(alignment) )
+ #define LFDS710_PAL_INLINE __forceinline
+
+ #define LFDS710_PAL_BARRIER_COMPILER_LOAD _ReadBarrier()
+ #define LFDS710_PAL_BARRIER_COMPILER_STORE _WriteBarrier()
+ #define LFDS710_PAL_BARRIER_COMPILER_FULL _ReadWriteBarrier()
+
+ /* TRD : there are four processors to consider;
+
+ . ARM32 (32 bit, ADD, CAS, DWCAS, EXCHANGE, SET) (defined _M_ARM)
+ . Itanium (64 bit, ADD, CAS, EXCHANGE, SET) (defined _M_IA64)
+ . x64 (64 bit, ADD, CAS, DWCAS, EXCHANGE, SET) (defined _M_X64 || defined
_M_AMD64)
+ . x86 (32 bit, ADD, CAS, DWCAS, EXCHANGE, SET) (defined _M_IX86)
+
+ can't find any indications of 64-bit ARM support yet
+
+ ARM has better intrinsics than the others, as there are no-fence variants
+
+ in theory we also have to deal with 32-bit Windows on a 64-bit platform,
+ and I presume we'd see the compiler properly indicate this in its macros,
+ but this would require that we use 32-bit atomics on the 64-bit platforms,
+ while keeping 64-bit cache line lengths and so on, and this is just so
+ wierd a thing to do these days that it's not supported
+ */
+
+ #if( defined _M_ARM )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD __dmb( _ARM_BARRIER_ISH )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_STORE __dmb( _ARM_BARRIER_ISHST )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_FULL __dmb( _ARM_BARRIER_ISH )
+
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (result) = (result_type) _InterlockedAdd_nf( (int long volatile *)
(pointer_to_target), (int long) (value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result )
\
+ {
\
+ lfds710_pal_uint_t
\
+ original_compare;
\
+
\
+ original_compare = (lfds710_pal_uint_t) *(pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(lfds710_pal_uint_t *) (pointer_to_compare) = (lfds710_pal_uint_t)
_InterlockedCompareExchange_nf( (long volatile *) (pointer_to_destination), (long)
(new_destination), (long) *(pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ result = (char unsigned) ( original_compare == (lfds710_pal_uint_t)
*(pointer_to_compare) );
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result )
\
+ {
\
+ __int64
\
+ original_compare;
\
+
\
+ original_compare = *(__int64 *) (pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(__int64 *) (pointer_to_compare) = _InterlockedCompareExchange64_nf( (__int64
volatile *) (pointer_to_destination), *(__int64 *) (pointer_to_new_destination), *(__int64
*) (pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ (result) = (char unsigned) ( *(__int64 *) (pointer_to_compare) == original_compare
);
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, exchange, exchange_type
) \
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (exchange) = (exchange_type) _InterlockedExchange_nf( (int long volatile *)
(pointer_to_destination), (int long) (exchange) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (void) _InterlockedExchange_nf( (int long volatile *) (pointer_to_destination),
(int long) (new_value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+ #endif
+
+ #if( defined _M_IA64 )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD __mf()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_STORE __mf()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_FULL __mf()
+
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (result) = (result_type) _InterlockedAdd64_acq( (__int64 volatile *)
(pointer_to_target), (__int64) (value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result )
\
+ {
\
+ lfds710_pal_uint_t
\
+ original_compare;
\
+
\
+ original_compare = (lfds710_pal_uint_t) *(pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(lfds710_pal_uint_t *) (pointer_to_compare) = (lfds710_pal_uint_t)
_InterlockedCompareExchange64_acq( (__int64 volatile *) (pointer_to_destination),
(__int64) (new_destination), (__int64) *(pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ result = (char unsigned) ( original_compare == (lfds710_pal_uint_t)
*(pointer_to_compare) );
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, exchange, exchange_type
) \
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (exchange) = (exchange_type) _InterlockedExchange64_acq( (__int64 volatile *)
(pointer_to_destination), (__int64) (exchange) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (void) _InterlockedExchange64_acq( (__int64 volatile *) (pointer_to_destination),
(__int64) (new_value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+ #endif
+
+ #if( defined _M_X64 || defined _M_AMD64 )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD _mm_lfence()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_STORE _mm_sfence()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_FULL _mm_mfence()
+
+ // TRD : no _InterlockedAdd64 for x64 - only the badly named
_InterlockedExchangeAdd64, which is the same as _InterlockedAdd64 but returns the
*original* value (which we must then add to before we return)
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (result) = (result_type) _InterlockedExchangeAdd64( (__int64 volatile *)
(pointer_to_target), (__int64) (value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ result += value;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result )
\
+ {
\
+ lfds710_pal_uint_t
\
+ original_compare;
\
+
\
+ original_compare = (lfds710_pal_uint_t) *(pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(lfds710_pal_uint_t *) (pointer_to_compare) = (lfds710_pal_uint_t)
_InterlockedCompareExchange64( (__int64 volatile *) (pointer_to_destination), (__int64)
(new_destination), (__int64) *(pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ result = (char unsigned) ( original_compare == (lfds710_pal_uint_t)
*(pointer_to_compare) );
\
+ }
+
+ #if( _MSC_VER >= 1500 )
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (result) = (char unsigned) _InterlockedCompareExchange128( (__int64 volatile *)
(pointer_to_destination), (__int64) (pointer_to_new_destination[1]), (__int64)
(pointer_to_new_destination[0]), (__int64 *) (pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+ #endif
+
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, exchange, exchange_type
) \
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (exchange) = (exchange_type) _InterlockedExchange64( (__int64 volatile *)
(pointer_to_destination), (__int64) (exchange) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (void) _InterlockedExchange64( (__int64 volatile *) (pointer_to_destination),
(__int64) (new_value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+ #endif
+
+ #if( defined _M_IX86 )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD lfds710_misc_force_store()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_STORE lfds710_misc_force_store()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_FULL lfds710_misc_force_store()
+
+ // TRD : no _InterlockedAdd for x86 - only the badly named _InterlockedExchangeAdd,
which is the same as _InterlockedAdd but returns the *original* value (which we must then
add to before we return)
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (result) = (result_type) _InterlockedExchangeAdd( (__int64 volatile *)
(pointer_to_target), (__int64) (value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ result += value;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result )
\
+ {
\
+ lfds710_pal_uint_t
\
+ original_compare;
\
+
\
+ /* LFDS710_PAL_ASSERT( (pointer_to_destination) != NULL ); */
\
+ /* LFDS710_PAL_ASSERT( (pointer_to_compare) != NULL ); */
\
+ /* TRD : new_destination can be any value in its range */
\
+ /* TRD : cas_strength can be any value in its range */
\
+ /* TRD : result can be any value in its range */
\
+
\
+ original_compare = (lfds710_pal_uint_t) *(pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(lfds710_pal_uint_t *) (pointer_to_compare) = (lfds710_pal_uint_t)
_InterlockedCompareExchange( (long volatile *) (pointer_to_destination), (long)
(new_destination), (long) *(pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ result = (char unsigned) ( original_compare == (lfds710_pal_uint_t)
*(pointer_to_compare) );
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result )
\
+ {
\
+ __int64
\
+ original_compare;
\
+
\
+ original_compare = *(__int64 *) (pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(__int64 *) (pointer_to_compare) = _InterlockedCompareExchange64( (__int64
volatile *) (pointer_to_destination), *(__int64 *) (pointer_to_new_destination), *(__int64
*) (pointer_to_compare) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ (result) = (char unsigned) ( *(__int64 *) (pointer_to_compare) == original_compare
);
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, exchange, exchange_type
) \
+ {
\
+ /* LFDS710_PAL_ASSERT( (pointer_to_destination) != NULL ); */
\
+ /* LFDS710_PAL_ASSERT( (pointer_to_exchange) != NULL ); */
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (exchange) = (exchange_type) _InterlockedExchange( (int long volatile *)
(pointer_to_destination), (int long) (exchange) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (void) _InterlockedExchange( (int long volatile *) (pointer_to_destination), (int
long) (new_value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+ #endif
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && LFDS710_PAL_GCC_VERSION >= 412 &&
LFDS710_PAL_GCC_VERSION < 473 )
+
+ #ifdef LFDS710_PAL_COMPILER
+ #error More than one porting abstraction layer matches the current platform in
lfds710_porting_abstraction_layer_compiler.h
+ #endif
+
+ #define LFDS710_PAL_COMPILER
+
+ #define LFDS710_PAL_COMPILER_STRING "GCC < 4.7.3"
+
+ #define LFDS710_PAL_ALIGN(alignment) __attribute__( (aligned(alignment)) )
+ #define LFDS710_PAL_INLINE inline
+
+ static LFDS710_PAL_INLINE void lfds710_pal_barrier_compiler( void )
+ {
+ __asm__ __volatile__ ( "" : : : "memory" );
+ }
+
+ #define LFDS710_PAL_BARRIER_COMPILER_LOAD lfds710_pal_barrier_compiler()
+ #define LFDS710_PAL_BARRIER_COMPILER_STORE lfds710_pal_barrier_compiler()
+ #define LFDS710_PAL_BARRIER_COMPILER_FULL lfds710_pal_barrier_compiler()
+
+ #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD __sync_synchronize()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_STORE __sync_synchronize()
+ #define LFDS710_PAL_BARRIER_PROCESSOR_FULL __sync_synchronize()
+
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (result) = (result_type) __sync_add_and_fetch( (lfds710_pal_uint_t *)
(pointer_to_target), (lfds710_pal_uint_t) (value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result ) \
+ {
\
+ lfds710_pal_uint_t
\
+ original_compare;
\
+
\
+ original_compare = (lfds710_pal_uint_t) *(pointer_to_compare);
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ *(pointer_to_compare) = __sync_val_compare_and_swap( pointer_to_destination,
*(pointer_to_compare), new_destination ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ result = (unsigned char) ( original_compare == (lfds710_pal_uint_t)
*(pointer_to_compare) ); \
+ }
+
+ // TRD : ARM and x86 have DWCAS which we can get via GCC intrinsics
+ #if( defined __arm__ || defined __i686__ || defined __i586__ || defined __i486__ )
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result )
\
+ {
\
+ int long long unsigned
\
+ original_destination;
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ original_destination = __sync_val_compare_and_swap( (int long long unsigned
volatile *) (pointer_to_destination), *(int long long unsigned *) (pointer_to_compare),
*(int long long unsigned *) (pointer_to_new_destination) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+
\
+ (result) = (char unsigned) ( original_destination == *(int long long unsigned *)
(pointer_to_compare) );
\
+
\
+ *(int long long unsigned *) (pointer_to_compare) = original_destination;
\
+ }
+ #endif
+
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, exchange, exchange_type )
\
+ {
\
+ /* LFDS710_PAL_ASSERT( (pointer_to_destination) != NULL ); */
\
+ /* TRD : exchange can be any value in its range */
\
+ /* TRD : exchange_type can be any value in its range */
\
+
\
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ (exchange) = (exchange_type) __sync_lock_test_and_set( pointer_to_destination,
(exchange) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL;
\
+ }
+
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value ) \
+ { \
+ LFDS710_PAL_BARRIER_COMPILER_FULL; \
+ (void) __sync_lock_test_and_set( pointer_to_destination, (new_value) ); \
+ LFDS710_PAL_BARRIER_COMPILER_FULL; \
+ }
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && LFDS710_PAL_GCC_VERSION >= 473 )
+
+ #ifdef LFDS710_PAL_COMPILER
+ #error More than one porting abstraction layer matches the current platform in
lfds710_porting_abstraction_layer_compiler.h
+ #endif
+
+ #define LFDS710_PAL_COMPILER
+
+ #define LFDS710_PAL_COMPILER_STRING "GCC >= 4.7.3"
+
+ #define LFDS710_PAL_ALIGN(alignment) __attribute__( (aligned(alignment)) )
+ #define LFDS710_PAL_INLINE inline
+
+ // TRD : GCC >= 4.7.3 compiler barriers are built into the intrinsics
+ #define LFDS710_PAL_COMPILER_BARRIERS_MISSING_PRESUMED_HAVING_A_GOOD_TIME
+
+ #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD __atomic_thread_fence( __ATOMIC_ACQUIRE )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_STORE __atomic_thread_fence( __ATOMIC_RELEASE )
+ #define LFDS710_PAL_BARRIER_PROCESSOR_FULL __atomic_thread_fence( __ATOMIC_ACQ_REL )
+
+ #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type )
\
+ {
\
+ (result) = (result_type) __atomic_add_fetch( (pointer_to_target), (value),
__ATOMIC_RELAXED ); \
+ }
+
+ #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare,
new_destination, cas_strength, result )
\
+ {
\
+ result = (char unsigned) __atomic_compare_exchange_n( pointer_to_destination,
pointer_to_compare, new_destination, cas_strength, __ATOMIC_RELAXED, __ATOMIC_RELAXED );
\
+ }
+
+ // TRD : ARM and x86 have DWCAS which we can get via GCC intrinsics
+ #if( defined __arm__ || defined __i686__ || defined __i586__ || defined __i486__ )
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result )
\
+ {
\
+ (result) = (char unsigned) __atomic_compare_exchange_n( (int long long unsigned
volatile *) (pointer_to_destination), (int long long unsigned *) (pointer_to_compare),
*(int long long unsigned *) (pointer_to_new_destination), (cas_strength),
__ATOMIC_RELAXED, __ATOMIC_RELAXED ); \
+ }
+ #endif
+
+ #if( defined __x86_64__ )
+ /* TRD : On 64 bit platforms, unsigned long long int is 64 bit, so we must manually
use cmpxchg16b,
+ as __sync_val_compare_and_swap() will only emit cmpxchg8b
+ */
+
+ // TRD : lfds710_pal_uint_t volatile (*destination)[2], lfds710_pal_uint_t
(*compare)[2], lfds710_pal_uint_t (*new_destination)[2], enum lfds710_misc_cas_strength
cas_strength, char unsigned result
+
+ #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare,
pointer_to_new_destination, cas_strength, result ) \
+ {
\
+ (result) = 0;
\
+
\
+ __asm__ __volatile__
\
+ (
\
+ "lock;" /* make cmpxchg16b atomic */
\
+ "cmpxchg16b %0;" /* cmpxchg16b sets ZF on success */
\
+ "setz %4;" /* if ZF set, set result to 1 */
\
+
\
+ /* output */
\
+ : "+m" ((pointer_to_destination)[0]), "+m"
((pointer_to_destination)[1]), "+a" ((pointer_to_compare)[0]), "+d"
((pointer_to_compare)[1]), "=q" (result) \
+
\
+ /* input */
\
+ : "b" ((pointer_to_new_destination)[0]), "c"
((pointer_to_new_destination)[1])
\
+
\
+ /* clobbered */
\
+ :
\
+ );
\
+ }
+ #endif
+
+ #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, exchange, exchange_type )
\
+ {
\
+ (exchange) = (exchange_type) __atomic_exchange_n( (pointer_to_destination),
(exchange), __ATOMIC_RELAXED ); \
+ }
+
+ #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, new_value )
\
+ {
\
+ (void) __atomic_exchange_n( (pointer_to_destination), (new_value), __ATOMIC_RELAXED
); \
+ }
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( !defined LFDS710_PAL_COMPILER )
+
+ #error No matching porting abstraction layer in
lfds710_porting_abstraction_layer_compiler.h
+
+#endif
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
new file mode 100644
index 0000000..47ffe78
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
@@ -0,0 +1,86 @@
+/****************************************************************************/
+#if( defined _WIN32 && !defined KERNEL_MODE )
+
+ #ifdef LFDS710_PAL_OPERATING_SYSTEM
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_operating_system.h".
+ #endif
+
+ #define LFDS710_PAL_OPERATING_SYSTEM
+
+ #include <assert.h>
+
+ #define LFDS710_PAL_OS_STRING "Windows"
+ #define LFDS710_PAL_ASSERT( expression ) if( !(expression) )
LFDS710_MISC_DELIBERATELY_CRASH;
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined _WIN32 && defined KERNEL_MODE )
+
+ #ifdef LFDS710_PAL_OPERATING_SYSTEM
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_operating_system.h".
+ #endif
+
+ #define LFDS710_PAL_OPERATING_SYSTEM
+
+ #include <assert.h>
+ #include <wdm.h>
+
+ #define LFDS710_PAL_OS_STRING "Windows"
+ #define LFDS710_PAL_ASSERT( expression ) if( !(expression) )
LFDS710_MISC_DELIBERATELY_CRASH;
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __linux__ && !defined KERNEL_MODE )
+
+ #ifdef LFDS710_PAL_OPERATING_SYSTEM
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_operating_system.h".
+ #endif
+
+ #define LFDS710_PAL_OPERATING_SYSTEM
+
+ #define LFDS710_PAL_OS_STRING "Linux"
+ #define LFDS710_PAL_ASSERT( expression ) if( !(expression) )
LFDS710_MISC_DELIBERATELY_CRASH;
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __linux__ && defined KERNEL_MODE )
+
+ #ifdef LFDS710_PAL_OPERATING_SYSTEM
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_operating_system.h".
+ #endif
+
+ #define LFDS710_PAL_OPERATING_SYSTEM
+
+ #include <linux/module.h>
+
+ #define LFDS710_PAL_OS_STRING "Linux"
+ #define LFDS710_PAL_ASSERT( expression ) BUG_ON( expression )
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( !defined LFDS710_PAL_OPERATING_SYSTEM )
+
+ #error No matching porting abstraction layer in
lfds710_porting_abstraction_layer_operating_system.h
+
+#endif
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
new file mode 100644
index 0000000..567d43c
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
@@ -0,0 +1,459 @@
+/****************************************************************************/
+#if( defined _MSC_VER && defined _M_IX86 )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "x86"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 32
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined _MSC_VER && (defined _M_X64 || defined _M_AMD64) )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "x64"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ // TRD : Intel bring over two cache lines at once, always, unless disabled in BIOS
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 128
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined _MSC_VER && defined _M_IA64 )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "IA64"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 64
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined _MSC_VER && defined _M_ARM )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "ARM (32-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ /* TRD : ARM is LL/SC and uses a reservation granule of 8 to 2048 bytes
+ so the isolation value used here is worst-case - be sure to set
+ this correctly, otherwise structures are painfully large
+
+ the test application has an argument, "-e", which attempts to
+ determine the ERG length
+ */
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 2048
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __arm__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "ARM (32-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ /* TRD : ARM is LL/SC and uses a reservation granule of 8 to 2048 bytes
+ so the isolation value used here is worst-case - be sure to set
+ this correctly, otherwise structures are painfully large
+
+ the test application has an argument, "-e", which attempts to
+ determine the ERG length
+ */
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 2048
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __aarch64__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "ARM (64-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ /* TRD : ARM is LL/SC and uses a reservation granule of 8 to 2048 bytes
+ so the isolation value used here is worst-case - be sure to set
+ this correctly, otherwise structures are painfully large
+
+ the test application has an argument, "-e", which attempts to
+ determine the ERG length
+ */
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 2048
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && (defined __i686__ || defined __i586__ || defined
__i486__) )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "x86"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 32
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __x86_64__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "x64"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 128
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __alpha__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "alpha"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 64
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __ia64__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "IA64"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 64
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __mips__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "MIPS (32-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 32
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __mips64 )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "MIPS (64-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 64
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __ppc__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "POWERPC (32-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ // TRD : this value is not very certain
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 128
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __ppc64__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "POWERPC (64-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ // TRD : this value is not very certain
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 128
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __sparc__ && !defined __sparc_v9__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "SPARC (32-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 32
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __sparc__ && defined __sparc_v9__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long long lfds710_pal_int_t;
+ typedef int long long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "SPARC (64-bit)"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 8
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 16
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 64
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( defined __GNUC__ && defined __m68k__ )
+
+ #ifdef LFDS710_PAL_PROCESSOR
+ #error More than one porting abstraction layer matches the current platform in
"lfds710_porting_abstraction_layer_processor.h".
+ #endif
+
+ #define LFDS710_PAL_PROCESSOR
+
+ typedef int long lfds710_pal_int_t;
+ typedef int long unsigned lfds710_pal_uint_t;
+
+ #define LFDS710_PAL_PROCESSOR_STRING "680x0"
+
+ #define LFDS710_PAL_ALIGN_SINGLE_POINTER 4
+ #define LFDS710_PAL_ALIGN_DOUBLE_POINTER 8
+
+ #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES 32
+
+#endif
+
+
+
+
+
+/****************************************************************************/
+#if( !defined LFDS710_PAL_PROCESSOR )
+
+ #error No matching porting abstraction layer in
"lfds710_porting_abstraction_layer_processor.h".
+
+#endif
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_prng.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_prng.h
new file mode 100644
index 0000000..95e0b27
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_prng.h
@@ -0,0 +1,74 @@
+/***** defines *****/
+#define LFDS710_PRNG_MAX ( (lfds710_pal_uint_t) -1 )
+
+/* TRD : the seed is from an on-line hardware RNG, using atmospheric noise
+ the URL below will generate another 16 random hex digits (e.g. a 64-bit number)
and is
+ the RNG used to generate the number above (0x0a34655d34c092fe)
+
+
http://www.random.org/integers/?num=16&min=0&max=15&col=1&...
+
+ the 32 bit seed is the upper half of the 64 bit seed
+
+ the "SplitMix" PRNG is from from Sebastiano vigna's site, CC0
license,
http://xorshift.di.unimi.it/splitmix64.c
+ the 64-bit constants come directly from the source, the 32-bt constants are in
fact the 32-bit murmurhash3 constants
+*/
+
+#if( LFDS710_PAL_ALIGN_SINGLE_POINTER == 4 )
+ #define LFDS710_PRNG_SEED 0x0a34655dUL
+ #define LFDS710_PRNG_SPLITMIX_MAGIC_RATIO 0x9E3779B9UL
+ #define LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_ONE 16
+ #define LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_TWO 13
+ #define LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_THREE 16
+ #define LFDS710_PRNG_SPLITMIX_MULTIPLY_CONSTANT_ONE 0x85ebca6bUL
+ #define LFDS710_PRNG_SPLITMIX_MULTIPLY_CONSTANT_TWO 0xc2b2ae35UL
+#endif
+
+#if( LFDS710_PAL_ALIGN_SINGLE_POINTER == 8 )
+ #define LFDS710_PRNG_SEED 0x0a34655d34c092feULL
+ #define LFDS710_PRNG_SPLITMIX_MAGIC_RATIO 0x9E3779B97F4A7C15ULL
+ #define LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_ONE 30
+ #define LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_TWO 27
+ #define LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_THREE 31
+ #define LFDS710_PRNG_SPLITMIX_MULTIPLY_CONSTANT_ONE 0xBF58476D1CE4E5B9ULL
+ #define LFDS710_PRNG_SPLITMIX_MULTIPLY_CONSTANT_TWO 0x94D049BB133111EBULL
+#endif
+
+// TRD : struct lfds710_prng_state prng_state, lfds710_pal_uint_t random_value
+#define LFDS710_PRNG_GENERATE( prng_state, random_value )
\
+{
\
+ LFDS710_PAL_ATOMIC_ADD( &(prng_state).entropy, LFDS710_PRNG_SPLITMIX_MAGIC_RATIO,
(random_value), lfds710_pal_uint_t ); \
+ LFDS710_PRNG_ST_MIXING_FUNCTION( random_value );
\
+}
+
+// TRD : struct lfds710_prng_state prng_st_state, lfds710_pal_uint_t random_value
+#define LFDS710_PRNG_ST_GENERATE( prng_st_state, random_value ) \
+{ \
+ (random_value) = ( (prng_st_state).entropy += LFDS710_PRNG_SPLITMIX_MAGIC_RATIO ); \
+ LFDS710_PRNG_ST_MIXING_FUNCTION( random_value ); \
+}
+
+// TRD : lfds710_pal_uint_t random_value
+#define LFDS710_PRNG_ST_MIXING_FUNCTION( random_value )
\
+{
\
+ (random_value) = ((random_value) ^ ((random_value) >>
LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_ONE)) * LFDS710_PRNG_SPLITMIX_MULTIPLY_CONSTANT_ONE;
\
+ (random_value) = ((random_value) ^ ((random_value) >>
LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_TWO)) * LFDS710_PRNG_SPLITMIX_MULTIPLY_CONSTANT_TWO;
\
+ (random_value) = (random_value ^ (random_value >>
LFDS710_PRNG_SPLITMIX_SHIFT_CONSTANT_THREE));
\
+}
+
+/***** structs *****/
+struct lfds710_prng_state
+{
+ lfds710_pal_uint_t volatile LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ entropy;
+};
+
+struct lfds710_prng_st_state
+{
+ lfds710_pal_uint_t
+ entropy;
+};
+
+/***** public prototypes *****/
+void lfds710_prng_init_valid_on_current_logical_core( struct lfds710_prng_state *ps,
lfds710_pal_uint_t seed );
+void lfds710_prng_st_init( struct lfds710_prng_st_state *psts, lfds710_pal_uint_t seed
);
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
new file mode 100644
index 0000000..f5be220
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
@@ -0,0 +1,66 @@
+/***** defines *****/
+#define LFDS710_QUEUE_BMM_GET_USER_STATE_FROM_STATE( queue_bmm_state ) (
(queue_bmm_state).user_state )
+
+/***** enums *****/
+enum lfds710_queue_bmm_query
+{
+ LFDS710_QUEUE_BMM_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
+ LFDS710_QUEUE_BMM_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_queue_bmm_element
+{
+ lfds710_pal_uint_t volatile
+ sequence_number;
+
+ void
+ *volatile key,
+ *volatile value;
+};
+
+struct lfds710_queue_bmm_state
+{
+ lfds710_pal_uint_t
+ number_elements,
+ mask;
+
+ lfds710_pal_uint_t volatile LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ read_index,
+ write_index;
+
+ struct lfds710_queue_bmm_element
+ *element_array;
+
+ void
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ dequeue_backoff,
+ enqueue_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_queue_bmm_init_valid_on_current_logical_core( struct lfds710_queue_bmm_state
*qbmms,
+ struct
lfds710_queue_bmm_element *element_array,
+ lfds710_pal_uint_t
number_elements,
+ void *user_state );
+
+void lfds710_queue_bmm_cleanup( struct lfds710_queue_bmm_state *qbmms,
+ void (*element_cleanup_callback)(struct
lfds710_queue_bmm_state *qbmms,
+ void *key,
+ void *value) );
+
+int lfds710_queue_bmm_enqueue( struct lfds710_queue_bmm_state *qbmms,
+ void *key,
+ void *value );
+
+int lfds710_queue_bmm_dequeue( struct lfds710_queue_bmm_state *qbmms,
+ void **key,
+ void **value );
+
+void lfds710_queue_bmm_query( struct lfds710_queue_bmm_state *qbmms,
+ enum lfds710_queue_bmm_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
new file mode 100644
index 0000000..e0d90ac
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
@@ -0,0 +1,59 @@
+/***** defines *****/
+#define LFDS710_QUEUE_BSS_GET_USER_STATE_FROM_STATE( queue_bss_state ) (
(queue_bss_state).user_state )
+
+/***** enums *****/
+enum lfds710_queue_bss_query
+{
+ LFDS710_QUEUE_BSS_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
+ LFDS710_QUEUE_BSS_QUERY_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_queue_bss_element
+{
+ void
+ *volatile key,
+ *volatile value;
+};
+
+struct lfds710_queue_bss_state
+{
+ lfds710_pal_uint_t
+ number_elements,
+ mask;
+
+ lfds710_pal_uint_t volatile
+ read_index,
+ write_index;
+
+ struct lfds710_queue_bss_element
+ *element_array;
+
+ void
+ *user_state;
+};
+
+/***** public prototypes *****/
+void lfds710_queue_bss_init_valid_on_current_logical_core( struct lfds710_queue_bss_state
*qbsss,
+ struct
lfds710_queue_bss_element *element_array,
+ lfds710_pal_uint_t
number_elements,
+ void *user_state );
+ // TRD : number_elements must be a positive integer power of 2
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_queue_bss_cleanup( struct lfds710_queue_bss_state *qbsss,
+ void (*element_cleanup_callback)(struct
lfds710_queue_bss_state *qbsss, void *key, void *value) );
+
+int lfds710_queue_bss_enqueue( struct lfds710_queue_bss_state *qbsss,
+ void *key,
+ void *value );
+
+int lfds710_queue_bss_dequeue( struct lfds710_queue_bss_state *qbsss,
+ void **key,
+ void **value );
+
+void lfds710_queue_bss_query( struct lfds710_queue_bss_state *qbsss,
+ enum lfds710_queue_bss_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git
a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
new file mode 100644
index 0000000..e7a3613
--- /dev/null
+++
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
@@ -0,0 +1,62 @@
+/***** defines *****/
+#define LFDS710_QUEUE_UMM_GET_KEY_FROM_ELEMENT( queue_umm_element ) (
(queue_umm_element).key )
+#define LFDS710_QUEUE_UMM_SET_KEY_IN_ELEMENT( queue_umm_element, new_key ) (
(queue_umm_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_QUEUE_UMM_GET_VALUE_FROM_ELEMENT( queue_umm_element ) (
(queue_umm_element).value )
+#define LFDS710_QUEUE_UMM_SET_VALUE_IN_ELEMENT( queue_umm_element, new_value ) (
(queue_umm_element).value = (void *) (lfds710_pal_uint_t) (new_value) )
+#define LFDS710_QUEUE_UMM_GET_USER_STATE_FROM_STATE( queue_umm_state ) (
(queue_umm_state).user_state )
+
+/***** enums *****/
+enum lfds710_queue_umm_query
+{
+ LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_GET_COUNT,
+ LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_queue_umm_element
+{
+ struct lfds710_queue_umm_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *volatile next[PAC_SIZE];
+
+ void
+ *key,
+ *value;
+};
+
+struct lfds710_queue_umm_state
+{
+ struct lfds710_queue_umm_element
LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *volatile enqueue[PAC_SIZE],
+ *volatile dequeue[PAC_SIZE];
+
+ lfds710_pal_uint_t volatile LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ aba_counter;
+
+ void LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ dequeue_backoff,
+ enqueue_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_queue_umm_init_valid_on_current_logical_core( struct lfds710_queue_umm_state
*qumms,
+ struct
lfds710_queue_umm_element *qumme_dummy,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_queue_umm_cleanup( struct lfds710_queue_umm_state *qumms,
+ void (*element_cleanup_callback)(struct
lfds710_queue_umm_state *qumms, struct lfds710_queue_umm_element *qumme, enum
lfds710_misc_flag dummy_element_flag) );
+
+void lfds710_queue_umm_enqueue( struct lfds710_queue_umm_state *qumms,
+ struct lfds710_queue_umm_element *qumme );
+
+int lfds710_queue_umm_dequeue( struct lfds710_queue_umm_state *qumms,
+ struct lfds710_queue_umm_element **qumme );
+
+void lfds710_queue_umm_query( struct lfds710_queue_umm_state *qumms,
+ enum lfds710_queue_umm_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_ringbuffer.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_ringbuffer.h
new file mode 100644
index 0000000..60bf0ca
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_ringbuffer.h
@@ -0,0 +1,66 @@
+/***** enums *****/
+#define LFDS710_RINGBUFFER_GET_USER_STATE_FROM_STATE( ringbuffer_state ) (
(ringbuffer_state).user_state )
+
+/***** enums *****/
+enum lfds710_ringbuffer_query
+{
+ LFDS710_RINGBUFFER_QUERY_SINGLETHREADED_GET_COUNT,
+ LFDS710_RINGBUFFER_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_ringbuffer_element
+{
+ struct lfds710_freelist_element
+ fe;
+
+ struct lfds710_queue_umm_element
+ qumme;
+
+ struct lfds710_queue_umm_element
+ *qumme_use; // TRD : hack; we need a new queue with no dummy element
+
+ void
+ *key,
+ *value;
+};
+
+struct lfds710_ringbuffer_state
+{
+ struct lfds710_freelist_state
+ fs;
+
+ struct lfds710_queue_umm_state
+ qumms;
+
+ void
+ (*element_cleanup_callback)( struct lfds710_ringbuffer_state *rs, void *key, void
*value, enum lfds710_misc_flag unread_flag ),
+ *user_state;
+};
+
+/***** public prototypes *****/
+void lfds710_ringbuffer_init_valid_on_current_logical_core( struct
lfds710_ringbuffer_state *rs,
+ struct
lfds710_ringbuffer_element *re_array_inc_dummy,
+ lfds710_pal_uint_t
number_elements_inc_dummy,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_ringbuffer_cleanup( struct lfds710_ringbuffer_state *rs,
+ void (*element_cleanup_callback)(struct
lfds710_ringbuffer_state *rs, void *key, void *value, enum lfds710_misc_flag unread_flag)
);
+
+int lfds710_ringbuffer_read( struct lfds710_ringbuffer_state *rs,
+ void **key,
+ void **value );
+
+void lfds710_ringbuffer_write( struct lfds710_ringbuffer_state *rs,
+ void *key,
+ void *value,
+ enum lfds710_misc_flag *overwrite_occurred_flag,
+ void **overwritten_key,
+ void **overwritten_value );
+
+void lfds710_ringbuffer_query( struct lfds710_ringbuffer_state *rs,
+ enum lfds710_ringbuffer_query query_type,
+ void *query_input,
+ void *query_output );
+
diff --git a/src/libsds/external/liblfds710/inc/liblfds710/lfds710_stack.h
b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_stack.h
new file mode 100644
index 0000000..e14aa02
--- /dev/null
+++ b/src/libsds/external/liblfds710/inc/liblfds710/lfds710_stack.h
@@ -0,0 +1,58 @@
+/***** defines *****/
+#define LFDS710_STACK_GET_KEY_FROM_ELEMENT( stack_element ) (
(stack_element).key )
+#define LFDS710_STACK_SET_KEY_IN_ELEMENT( stack_element, new_key ) (
(stack_element).key = (void *) (lfds710_pal_uint_t) (new_key) )
+#define LFDS710_STACK_GET_VALUE_FROM_ELEMENT( stack_element ) (
(stack_element).value )
+#define LFDS710_STACK_SET_VALUE_IN_ELEMENT( stack_element, new_value ) (
(stack_element).value = (void *) (lfds710_pal_uint_t) (new_value) )
+#define LFDS710_STACK_GET_USER_STATE_FROM_STATE( stack_state ) (
(stack_state).user_state )
+
+/***** enums *****/
+enum lfds710_stack_query
+{
+ LFDS710_STACK_QUERY_SINGLETHREADED_GET_COUNT,
+ LFDS710_STACK_QUERY_SINGLETHREADED_VALIDATE
+};
+
+/***** structures *****/
+struct lfds710_stack_element
+{
+ struct lfds710_stack_element
+ *next;
+
+ void
+ *key,
+ *value;
+};
+
+struct lfds710_stack_state
+{
+ struct lfds710_stack_element LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *volatile top[PAC_SIZE];
+
+ void LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
+ *user_state;
+
+ struct lfds710_misc_backoff_state
+ pop_backoff,
+ push_backoff;
+};
+
+/***** public prototypes *****/
+void lfds710_stack_init_valid_on_current_logical_core( struct lfds710_stack_state *ss,
+ void *user_state );
+ // TRD : used in conjunction with the #define
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE
+
+void lfds710_stack_cleanup( struct lfds710_stack_state *ss,
+ void (*element_cleanup_callback)(struct lfds710_stack_state
*ss, struct lfds710_stack_element *se) );
+
+void lfds710_stack_push( struct lfds710_stack_state *ss,
+ struct lfds710_stack_element *se );
+
+int lfds710_stack_pop( struct lfds710_stack_state *ss,
+ struct lfds710_stack_element **se );
+
+void lfds710_stack_query( struct lfds710_stack_state *ss,
+ enum lfds710_stack_query query_type,
+ void *query_input,
+ void *query_output );
+
+
diff --git a/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_cleanup.d
new file mode 100644
index 0000000..73bc44e
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_btree_addonly_unbalanced_cleanup.o: \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_cleanup.c \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../liblfds710_internal.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_misc.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_get.d
b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_get.d
new file mode 100644
index 0000000..413e253
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_get.d
@@ -0,0 +1,20 @@
+lfds710_btree_addonly_unbalanced_get.o: \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_get.c \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../liblfds710_internal.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_misc.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_init.d
b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_init.d
new file mode 100644
index 0000000..22055b3
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_init.d
@@ -0,0 +1,20 @@
+lfds710_btree_addonly_unbalanced_init.o: \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_init.c \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../liblfds710_internal.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_misc.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_insert.d
b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_insert.d
new file mode 100644
index 0000000..29988bd
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_insert.d
@@ -0,0 +1,20 @@
+lfds710_btree_addonly_unbalanced_insert.o: \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_insert.c \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../liblfds710_internal.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_misc.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_query.d
b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_query.d
new file mode 100644
index 0000000..b4699c4
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_btree_addonly_unbalanced_query.d
@@ -0,0 +1,20 @@
+lfds710_btree_addonly_unbalanced_query.o: \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_query.c \
+ ../../src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../liblfds710_internal.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_misc.h \
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_btree_addonly_unbalanced/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_freelist_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_freelist_cleanup.d
new file mode 100644
index 0000000..d616e63
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_freelist_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_freelist_cleanup.o: \
+ ../../src/lfds710_freelist/lfds710_freelist_cleanup.c \
+ ../../src/lfds710_freelist/lfds710_freelist_internal.h \
+ ../../src/lfds710_freelist/../liblfds710_internal.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_freelist_init.d
b/src/libsds/external/liblfds710/obj/lfds710_freelist_init.d
new file mode 100644
index 0000000..884f314
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_freelist_init.d
@@ -0,0 +1,20 @@
+lfds710_freelist_init.o: \
+ ../../src/lfds710_freelist/lfds710_freelist_init.c \
+ ../../src/lfds710_freelist/lfds710_freelist_internal.h \
+ ../../src/lfds710_freelist/../liblfds710_internal.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_freelist_pop.d
b/src/libsds/external/liblfds710/obj/lfds710_freelist_pop.d
new file mode 100644
index 0000000..1097118
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_freelist_pop.d
@@ -0,0 +1,19 @@
+lfds710_freelist_pop.o: ../../src/lfds710_freelist/lfds710_freelist_pop.c \
+ ../../src/lfds710_freelist/lfds710_freelist_internal.h \
+ ../../src/lfds710_freelist/../liblfds710_internal.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_freelist_push.d
b/src/libsds/external/liblfds710/obj/lfds710_freelist_push.d
new file mode 100644
index 0000000..25e58f6
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_freelist_push.d
@@ -0,0 +1,20 @@
+lfds710_freelist_push.o: \
+ ../../src/lfds710_freelist/lfds710_freelist_push.c \
+ ../../src/lfds710_freelist/lfds710_freelist_internal.h \
+ ../../src/lfds710_freelist/../liblfds710_internal.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_freelist_query.d
b/src/libsds/external/liblfds710/obj/lfds710_freelist_query.d
new file mode 100644
index 0000000..e354cf1
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_freelist_query.d
@@ -0,0 +1,20 @@
+lfds710_freelist_query.o: \
+ ../../src/lfds710_freelist/lfds710_freelist_query.c \
+ ../../src/lfds710_freelist/lfds710_freelist_internal.h \
+ ../../src/lfds710_freelist/../liblfds710_internal.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_freelist/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_freelist/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_cleanup.d
new file mode 100644
index 0000000..872c84e
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_hash_addonly_cleanup.o: \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_cleanup.c \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h \
+ ../../src/lfds710_hash_addonly/../liblfds710_internal.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_get.d
b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_get.d
new file mode 100644
index 0000000..1cff207
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_get.d
@@ -0,0 +1,20 @@
+lfds710_hash_addonly_get.o: \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_get.c \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h \
+ ../../src/lfds710_hash_addonly/../liblfds710_internal.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_init.d
b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_init.d
new file mode 100644
index 0000000..6ff002b
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_init.d
@@ -0,0 +1,20 @@
+lfds710_hash_addonly_init.o: \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_init.c \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h \
+ ../../src/lfds710_hash_addonly/../liblfds710_internal.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_insert.d
b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_insert.d
new file mode 100644
index 0000000..b82a3bf
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_insert.d
@@ -0,0 +1,20 @@
+lfds710_hash_addonly_insert.o: \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_insert.c \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h \
+ ../../src/lfds710_hash_addonly/../liblfds710_internal.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_iterate.d
b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_iterate.d
new file mode 100644
index 0000000..175b985
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_iterate.d
@@ -0,0 +1,20 @@
+lfds710_hash_addonly_iterate.o: \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_iterate.c \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h \
+ ../../src/lfds710_hash_addonly/../liblfds710_internal.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_query.d
b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_query.d
new file mode 100644
index 0000000..5957a6b
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_hash_addonly_query.d
@@ -0,0 +1,20 @@
+lfds710_hash_addonly_query.o: \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_query.c \
+ ../../src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h \
+ ../../src/lfds710_hash_addonly/../liblfds710_internal.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_hash_addonly/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_cleanup.d
new file mode 100644
index 0000000..d6ac11b
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_ordered_cleanup.o: \
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_cleanup.c
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_prng.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_get.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_get.d
new file mode 100644
index 0000000..c109f13
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_get.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_ordered_get.o: \
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_get.c
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_prng.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_init.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_init.d
new file mode 100644
index 0000000..d75e828
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_init.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_ordered_init.o: \
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_init.c
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_prng.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_insert.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_insert.d
new file mode 100644
index 0000000..ce63de3
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_insert.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_ordered_insert.o: \
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_insert.c
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_prng.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_query.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_query.d
new file mode 100644
index 0000000..17bd36e
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_ordered_query.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_ordered_query.o: \
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_query.c
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_prng.h
\
+ ../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_ordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_cleanup.d
new file mode 100644
index 0000000..aab3a37
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_unordered_cleanup.o: \
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_cleanup.c
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_get.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_get.d
new file mode 100644
index 0000000..5c72317
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_get.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_unordered_get.o: \
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_get.c
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_init.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_init.d
new file mode 100644
index 0000000..dd604f1
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_init.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_unordered_init.o: \
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_init.c
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_insert.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_insert.d
new file mode 100644
index 0000000..081c279
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_insert.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_unordered_insert.o: \
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_insert.c
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_query.d
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_query.d
new file mode 100644
index 0000000..9258b66
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_list_addonly_singlylinked_unordered_query.d
@@ -0,0 +1,20 @@
+lfds710_list_addonly_singlylinked_unordered_query.o: \
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_query.c
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
\
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../liblfds710_internal.h \
+ ../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710.h \
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_list_addonly_singlylinked_unordered/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_misc_globals.d
b/src/libsds/external/liblfds710/obj/lfds710_misc_globals.d
new file mode 100644
index 0000000..188d18f
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_misc_globals.d
@@ -0,0 +1,19 @@
+lfds710_misc_globals.o: ../../src/lfds710_misc/lfds710_misc_globals.c \
+ ../../src/lfds710_misc/lfds710_misc_internal.h \
+ ../../src/lfds710_misc/../liblfds710_internal.h \
+ ../../src/lfds710_misc/../../inc/liblfds710.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_misc_internal_backoff_init.d
b/src/libsds/external/liblfds710/obj/lfds710_misc_internal_backoff_init.d
new file mode 100644
index 0000000..be961a1
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_misc_internal_backoff_init.d
@@ -0,0 +1,20 @@
+lfds710_misc_internal_backoff_init.o: \
+ ../../src/lfds710_misc/lfds710_misc_internal_backoff_init.c \
+ ../../src/lfds710_misc/lfds710_misc_internal.h \
+ ../../src/lfds710_misc/../liblfds710_internal.h \
+ ../../src/lfds710_misc/../../inc/liblfds710.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_misc_query.d
b/src/libsds/external/liblfds710/obj/lfds710_misc_query.d
new file mode 100644
index 0000000..4d372bd
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_misc_query.d
@@ -0,0 +1,19 @@
+lfds710_misc_query.o: ../../src/lfds710_misc/lfds710_misc_query.c \
+ ../../src/lfds710_misc/lfds710_misc_internal.h \
+ ../../src/lfds710_misc/../liblfds710_internal.h \
+ ../../src/lfds710_misc/../../inc/liblfds710.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_misc/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_misc/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_prng_init.d
b/src/libsds/external/liblfds710/obj/lfds710_prng_init.d
new file mode 100644
index 0000000..99c42c4
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_prng_init.d
@@ -0,0 +1,19 @@
+lfds710_prng_init.o: ../../src/lfds710_prng/lfds710_prng_init.c \
+ ../../src/lfds710_prng/lfds710_prng_internal.h \
+ ../../src/lfds710_prng/../liblfds710_internal.h \
+ ../../src/lfds710_prng/../../inc/liblfds710.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_prng/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_prng/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_prng/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_prng/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_prng/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_prng/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_prng/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.d
new file mode 100644
index 0000000..39e8d30
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.o: \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.d
new file mode 100644
index 0000000..f743cb8
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.o: \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.d
new file mode 100644
index 0000000..3405419
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.o: \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_init.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_init.d
new file mode 100644
index 0000000..511c2d6
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_init.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_manyproducer_manyconsumer_init.o: \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_init.c
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_query.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_query.d
new file mode 100644
index 0000000..3fc050b
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_manyproducer_manyconsumer_query.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_manyproducer_manyconsumer_query.o: \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_query.c
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.d
new file mode 100644
index 0000000..9053b95
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.o: \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../liblfds710_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.d
new file mode 100644
index 0000000..ab97e34
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.o: \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../liblfds710_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.d
new file mode 100644
index 0000000..d4f6b26
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.o: \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../liblfds710_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_init.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_init.d
new file mode 100644
index 0000000..ca605a7
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_init.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_singleproducer_singleconsumer_init.o: \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_init.c
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../liblfds710_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_query.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_query.d
new file mode 100644
index 0000000..61ac923
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_bounded_singleproducer_singleconsumer_query.d
@@ -0,0 +1,20 @@
+lfds710_queue_bounded_singleproducer_singleconsumer_query.o: \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_query.c
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../liblfds710_internal.h
\
+ ../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_bounded_singleproducer_singleconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.d
new file mode 100644
index 0000000..9160ee7
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.o: \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.d
new file mode 100644
index 0000000..75185f3
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.d
@@ -0,0 +1,20 @@
+lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.o: \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.d
new file mode 100644
index 0000000..b072507
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.d
@@ -0,0 +1,20 @@
+lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.o: \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_init.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_init.d
new file mode 100644
index 0000000..9311254
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_init.d
@@ -0,0 +1,20 @@
+lfds710_queue_unbounded_manyproducer_manyconsumer_init.o: \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.c
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_query.d
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_query.d
new file mode 100644
index 0000000..3fb9768
--- /dev/null
+++
b/src/libsds/external/liblfds710/obj/lfds710_queue_unbounded_manyproducer_manyconsumer_query.d
@@ -0,0 +1,20 @@
+lfds710_queue_unbounded_manyproducer_manyconsumer_query.o: \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.c
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
\
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../liblfds710_internal.h \
+ ../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710.h \
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_prng.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_misc.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_freelist.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_hash_addonly.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_ringbuffer.h
\
+
../../src/lfds710_queue_unbounded_manyproducer_manyconsumer/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_cleanup.d
new file mode 100644
index 0000000..a36027c
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_cleanup.d
@@ -0,0 +1,20 @@
+lfds710_ringbuffer_cleanup.o: \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_cleanup.c \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h \
+ ../../src/lfds710_ringbuffer/../liblfds710_internal.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_init.d
b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_init.d
new file mode 100644
index 0000000..c2c2b36
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_init.d
@@ -0,0 +1,20 @@
+lfds710_ringbuffer_init.o: \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_init.c \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h \
+ ../../src/lfds710_ringbuffer/../liblfds710_internal.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_query.d
b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_query.d
new file mode 100644
index 0000000..4bd0658
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_query.d
@@ -0,0 +1,20 @@
+lfds710_ringbuffer_query.o: \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_query.c \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h \
+ ../../src/lfds710_ringbuffer/../liblfds710_internal.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_read.d
b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_read.d
new file mode 100644
index 0000000..b188a45
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_read.d
@@ -0,0 +1,20 @@
+lfds710_ringbuffer_read.o: \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_read.c \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h \
+ ../../src/lfds710_ringbuffer/../liblfds710_internal.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_write.d
b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_write.d
new file mode 100644
index 0000000..0504bb4
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_ringbuffer_write.d
@@ -0,0 +1,20 @@
+lfds710_ringbuffer_write.o: \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_write.c \
+ ../../src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h \
+ ../../src/lfds710_ringbuffer/../liblfds710_internal.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_hash_addonly.h \
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_ringbuffer/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_stack_cleanup.d
b/src/libsds/external/liblfds710/obj/lfds710_stack_cleanup.d
new file mode 100644
index 0000000..72e9772
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_stack_cleanup.d
@@ -0,0 +1,19 @@
+lfds710_stack_cleanup.o: ../../src/lfds710_stack/lfds710_stack_cleanup.c \
+ ../../src/lfds710_stack/lfds710_stack_internal.h \
+ ../../src/lfds710_stack/../liblfds710_internal.h \
+ ../../src/lfds710_stack/../../inc/liblfds710.h \
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_stack_init.d
b/src/libsds/external/liblfds710/obj/lfds710_stack_init.d
new file mode 100644
index 0000000..7714af2
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_stack_init.d
@@ -0,0 +1,19 @@
+lfds710_stack_init.o: ../../src/lfds710_stack/lfds710_stack_init.c \
+ ../../src/lfds710_stack/lfds710_stack_internal.h \
+ ../../src/lfds710_stack/../liblfds710_internal.h \
+ ../../src/lfds710_stack/../../inc/liblfds710.h \
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_stack_pop.d
b/src/libsds/external/liblfds710/obj/lfds710_stack_pop.d
new file mode 100644
index 0000000..61d6d40
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_stack_pop.d
@@ -0,0 +1,19 @@
+lfds710_stack_pop.o: ../../src/lfds710_stack/lfds710_stack_pop.c \
+ ../../src/lfds710_stack/lfds710_stack_internal.h \
+ ../../src/lfds710_stack/../liblfds710_internal.h \
+ ../../src/lfds710_stack/../../inc/liblfds710.h \
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_stack_push.d
b/src/libsds/external/liblfds710/obj/lfds710_stack_push.d
new file mode 100644
index 0000000..9580c5b
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_stack_push.d
@@ -0,0 +1,19 @@
+lfds710_stack_push.o: ../../src/lfds710_stack/lfds710_stack_push.c \
+ ../../src/lfds710_stack/lfds710_stack_internal.h \
+ ../../src/lfds710_stack/../liblfds710_internal.h \
+ ../../src/lfds710_stack/../../inc/liblfds710.h \
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_stack.h
diff --git a/src/libsds/external/liblfds710/obj/lfds710_stack_query.d
b/src/libsds/external/liblfds710/obj/lfds710_stack_query.d
new file mode 100644
index 0000000..e9a929f
--- /dev/null
+++ b/src/libsds/external/liblfds710/obj/lfds710_stack_query.d
@@ -0,0 +1,19 @@
+lfds710_stack_query.o: ../../src/lfds710_stack/lfds710_stack_query.c \
+ ../../src/lfds710_stack/lfds710_stack_internal.h \
+ ../../src/lfds710_stack/../liblfds710_internal.h \
+ ../../src/lfds710_stack/../../inc/liblfds710.h \
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_compiler.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_operating_system.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_porting_abstraction_layer_processor.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_prng.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_misc.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_btree_addonly_unbalanced.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_freelist.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_hash_addonly.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_ordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_list_addonly_singlylinked_unordered.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_manyproducer_manyconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_bounded_singleproducer_singleconsumer.h
\
+
../../src/lfds710_stack/../../inc/liblfds710/lfds710_queue_unbounded_manyproducer_manyconsumer.h
\
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_ringbuffer.h \
+ ../../src/lfds710_stack/../../inc/liblfds710/lfds710_stack.h
diff --git
a/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_cleanup.c
new file mode 100644
index 0000000..ec00e7b
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_cleanup.c
@@ -0,0 +1,117 @@
+/***** includes *****/
+#include "lfds710_btree_addonly_unbalanced_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_btree_au_cleanup( struct lfds710_btree_au_state *baus,
+ void (*element_cleanup_callback)(struct
lfds710_btree_au_state *baus, struct lfds710_btree_au_element *baue) )
+{
+ enum lfds710_btree_au_delete_action
+ delete_action = LFDS710_BTREE_AU_DELETE_SELF; // TRD : to remove compiler warning
+
+ struct lfds710_btree_au_element
+ *baue;
+
+ struct lfds710_btree_au_element
+ *temp;
+
+ LFDS710_PAL_ASSERT( baus != NULL );
+ // TRD : element_delete_function can be NULL
+
+ /* TRD : we're not lock-free now, so delete at will
+ but be iterative, so can be used in kernels (where there's little stack)
+ and be performant, since the user may be
+ creating/destroying many of these trees
+ also remember the user may be deallocating user data
+ so we cannot visit an element twice
+
+ we start at the root and iterate till we go to NULL
+ if the element has zero children, we delete it and move up to its parent
+ if the element has one child, we delete it, move its child into its place, and
continue from its child
+ if the element has two children, we move left
+
+ the purpose of this is to minimize walking around the tree
+ to prevent visiting an element twice
+ while also minimizing code complexity
+ */
+
+ if( element_cleanup_callback == NULL )
+ return;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ lfds710_btree_au_get_by_absolute_position( baus, &baue,
LFDS710_BTREE_AU_ABSOLUTE_POSITION_ROOT );
+
+ while( baue != NULL )
+ {
+ if( baue->left == NULL and baue->right == NULL )
+ delete_action = LFDS710_BTREE_AU_DELETE_SELF;
+
+ if( baue->left != NULL and baue->right == NULL )
+ delete_action = LFDS710_BTREE_AU_DELETE_SELF_REPLACE_WITH_LEFT_CHILD;
+
+ if( baue->left == NULL and baue->right != NULL )
+ delete_action = LFDS710_BTREE_AU_DELETE_SELF_REPLACE_WITH_RIGHT_CHILD;
+
+ if( baue->left != NULL and baue->right != NULL )
+ delete_action = LFDS710_BTREE_AU_DELETE_MOVE_LEFT;
+
+ switch( delete_action )
+ {
+ case LFDS710_BTREE_AU_DELETE_SELF:
+ // TRD : if we have a parent (we could be root) set his point to us to NULL
+ if( baue->up != NULL )
+ {
+ if( baue->up->left == baue )
+ baue->up->left = NULL;
+ if( baue->up->right == baue )
+ baue->up->right = NULL;
+ }
+
+ temp = baue;
+ lfds710_btree_au_get_by_relative_position( &baue,
LFDS710_BTREE_AU_RELATIVE_POSITION_UP );
+ element_cleanup_callback( baus, temp );
+ break;
+
+ case LFDS710_BTREE_AU_DELETE_SELF_REPLACE_WITH_LEFT_CHILD:
+ baue->left->up = baue->up;
+ if( baue->up != NULL )
+ {
+ if( baue->up->left == baue )
+ baue->up->left = baue->left;
+ if( baue->up->right == baue )
+ baue->up->right = baue->left;
+ }
+
+ temp = baue;
+ lfds710_btree_au_get_by_relative_position( &baue,
LFDS710_BTREE_AU_RELATIVE_POSITION_LEFT );
+ element_cleanup_callback( baus, temp );
+ break;
+
+ case LFDS710_BTREE_AU_DELETE_SELF_REPLACE_WITH_RIGHT_CHILD:
+ baue->right->up = baue->up;
+ if( baue->up != NULL )
+ {
+ if( baue->up->left == baue )
+ baue->up->left = baue->right;
+ if( baue->up->right == baue )
+ baue->up->right = baue->right;
+ }
+
+ temp = baue;
+ lfds710_btree_au_get_by_relative_position( &baue,
LFDS710_BTREE_AU_RELATIVE_POSITION_RIGHT );
+ element_cleanup_callback( baus, temp );
+ break;
+
+ case LFDS710_BTREE_AU_DELETE_MOVE_LEFT:
+ lfds710_btree_au_get_by_relative_position( &baue,
LFDS710_BTREE_AU_RELATIVE_POSITION_LEFT );
+ break;
+ }
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_get.c
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_get.c
new file mode 100644
index 0000000..51e0600
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_get.c
@@ -0,0 +1,482 @@
+/***** includes *****/
+#include "lfds710_btree_addonly_unbalanced_internal.h"
+
+/***** private prototypes *****/
+static void
lfds710_btree_au_internal_inorder_walk_from_largest_get_next_smallest_element( struct
lfds710_btree_au_element **baue );
+static void
lfds710_btree_au_internal_inorder_walk_from_smallest_get_next_largest_element( struct
lfds710_btree_au_element **baue );
+
+
+
+
+
+/****************************************************************************/
+int lfds710_btree_au_get_by_key( struct lfds710_btree_au_state *baus,
+ int (*key_compare_function)(void const *new_key, void
const *existing_key),
+ void *key,
+ struct lfds710_btree_au_element **baue )
+{
+ int
+ compare_result = !0,
+ rv = 1;
+
+ LFDS710_PAL_ASSERT( baus != NULL );
+ // TRD : key_compare_function can be NULL
+ // TRD : key can be NULL
+ LFDS710_PAL_ASSERT( baue != NULL );
+
+ if( key_compare_function == NULL )
+ key_compare_function = baus->key_compare_function;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ *baue = baus->root;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ while( *baue != NULL and compare_result != 0 )
+ {
+ compare_result = key_compare_function( key, (*baue)->key );
+
+ if( compare_result < 0 )
+ {
+ *baue = (*baue)->left;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+
+ if( compare_result > 0 )
+ {
+ *baue = (*baue)->right;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+
+ if( *baue == NULL )
+ rv = 0;
+
+ return rv;
+}
+
+
+
+
+
+/****************************************************************************/
+int lfds710_btree_au_get_by_absolute_position( struct lfds710_btree_au_state *baus,
+ struct lfds710_btree_au_element **baue,
+ enum lfds710_btree_au_absolute_position
absolute_position )
+{
+ int
+ rv = 1;
+
+ LFDS710_PAL_ASSERT( baus != NULL );
+ LFDS710_PAL_ASSERT( baue != NULL );
+ // TRD : absolute_position can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ *baue = baus->root;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( absolute_position )
+ {
+ case LFDS710_BTREE_AU_ABSOLUTE_POSITION_ROOT:
+ break;
+
+ case LFDS710_BTREE_AU_ABSOLUTE_POSITION_LARGEST_IN_TREE:
+ if( *baue != NULL )
+ while( (*baue)->right != NULL )
+ {
+ *baue = (*baue)->right;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ break;
+
+ case LFDS710_BTREE_AU_ABSOLUTE_POSITION_SMALLEST_IN_TREE:
+ if( *baue != NULL )
+ while( (*baue)->left != NULL )
+ {
+ *baue = (*baue)->left;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ break;
+ }
+
+ if( *baue == NULL )
+ rv = 0;
+
+ return rv;
+}
+
+
+
+
+
+/****************************************************************************/
+int lfds710_btree_au_get_by_relative_position( struct lfds710_btree_au_element **baue,
+ enum lfds710_btree_au_relative_position
relative_position )
+{
+ int
+ rv = 1;
+
+ LFDS710_PAL_ASSERT( baue != NULL );
+ // TRD : relative_position can baue any value in its range
+
+ if( *baue == NULL )
+ return 0;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( relative_position )
+ {
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_UP:
+ *baue = (*baue)->up;
+ // TRD : no load barrier - up already existed, so is known to be safely propagated
+ break;
+
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_LEFT:
+ *baue = (*baue)->left;
+ LFDS710_MISC_BARRIER_LOAD;
+ break;
+
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_RIGHT:
+ *baue = (*baue)->right;
+ LFDS710_MISC_BARRIER_LOAD;
+ break;
+
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_SMALLEST_ELEMENT_BELOW_CURRENT_ELEMENT:
+ *baue = (*baue)->left;
+ if( *baue != NULL )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ while( (*baue)->right != NULL )
+ {
+ *baue = (*baue)->right;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+ break;
+
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_LARGEST_ELEMENT_BELOW_CURRENT_ELEMENT:
+ *baue = (*baue)->right;
+ if( *baue != NULL )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ while( (*baue)->left != NULL )
+ {
+ *baue = (*baue)->left;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+ break;
+
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_SMALLER_ELEMENT_IN_ENTIRE_TREE:
+ lfds710_btree_au_internal_inorder_walk_from_largest_get_next_smallest_element( baue
);
+ break;
+
+ case LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_LARGER_ELEMENT_IN_ENTIRE_TREE:
+ lfds710_btree_au_internal_inorder_walk_from_smallest_get_next_largest_element( baue
);
+ break;
+ }
+
+ if( *baue == NULL )
+ rv = 0;
+
+ return rv;
+}
+
+
+
+
+
+/****************************************************************************/
+static void
lfds710_btree_au_internal_inorder_walk_from_largest_get_next_smallest_element( struct
lfds710_btree_au_element **baue )
+{
+ enum lfds710_btree_au_move
+ action = LFDS710_BTREE_AU_MOVE_INVALID;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED,
+ load_finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ struct lfds710_btree_au_element
+ *left = NULL,
+ *right = NULL,
+ *up = NULL,
+ *up_left = NULL,
+ *up_right = NULL;
+
+ LFDS710_PAL_ASSERT( baue != NULL );
+
+ /* TRD : from any given element, the next smallest element is;
+ 1. if we have a left, it's the largest element on the right branch of our
left child
+ 2. if we don't have a left, and we're on the right of our parent, then
it's our parent
+ 3. if we don't have a left, and we're on the left of our parent or we
have no parent,
+ iterative up the tree until we find the first child who is on the right of
its parent; then it's the parent
+ */
+
+ /* TRD : we need to ensure the variables we use to decide our action are
self-consistent
+ to do this, we make local copies of them all
+ then, if they are all not NULL, we can know they cannot change and we can
continue
+ if however any of them are NULL, they could have changed while we were
reading
+ and so our variables could be non-self-consistent
+ to check for this, we issue another processor read barrier
+ and then compare our local variables with the values in the tree
+ if they all match, then we know our variable set is self-consistent
+ (even though it may now be wrong - but we will discover this when we try the
atomic operation)
+ */
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ while( load_finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ left = (*baue)->left;
+ right = (*baue)->right;
+ up = (*baue)->up;
+ if( up != NULL )
+ {
+ up_left = (*baue)->up->left;
+ up_right = (*baue)->up->right;
+ }
+
+ // TRD : optimization - if all already not NULL, given we're add-only, they
won't change
+ if( left != NULL and right != NULL and (up == NULL or (up != NULL and up_left != NULL
and up_right != NULL)) )
+ break;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( left == (*baue)->left and right == (*baue)->right and (up == NULL or (up !=
NULL and up == (*baue)->up and up_left == (*baue)->up->left and up_right ==
(*baue)->up->right)) )
+ load_finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( left != NULL )
+ action = LFDS710_BTREE_AU_MOVE_LARGEST_FROM_LEFT_CHILD;
+
+ if( left == NULL and up != NULL and up_right == *baue )
+ action = LFDS710_BTREE_AU_MOVE_GET_PARENT;
+
+ if( (left == NULL and up == NULL) or (up != NULL and up_left == *baue and left == NULL)
)
+ action = LFDS710_BTREE_AU_MOVE_MOVE_UP_TREE;
+
+ switch( action )
+ {
+ case LFDS710_BTREE_AU_MOVE_INVALID:
+ case LFDS710_BTREE_AU_MOVE_SMALLEST_FROM_RIGHT_CHILD:
+ // TRD : eliminates a compiler warning
+ break;
+
+ case LFDS710_BTREE_AU_MOVE_LARGEST_FROM_LEFT_CHILD:
+ *baue = left;
+ if( *baue != NULL )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ while( (*baue)->right != NULL )
+ {
+ *baue = (*baue)->right;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+ break;
+
+ case LFDS710_BTREE_AU_MOVE_GET_PARENT:
+ *baue = up;
+ break;
+
+ case LFDS710_BTREE_AU_MOVE_MOVE_UP_TREE:
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ load_finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ while( load_finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ up = (*baue)->up;
+ if( up != NULL )
+ up_left = (*baue)->up->left;
+
+ // TRD : optimization - if all already not NULL, given we're add-only, they
won't change
+ if( up == NULL or (up != NULL and up_left != NULL) )
+ break;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( up == (*baue)->up and up_left == (*baue)->up->left )
+ load_finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( *baue != NULL and up != NULL and *baue == up_left )
+ *baue = up;
+ else
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ *baue = up;
+
+ /*
+
+ while( *baue != NULL and (*baue)->up != NULL and *baue ==
(*baue)->up->left )
+ *baue = (*baue)->up;
+
+ *baue = (*baue)->up;
+
+ */
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void
lfds710_btree_au_internal_inorder_walk_from_smallest_get_next_largest_element( struct
lfds710_btree_au_element **baue )
+{
+ enum lfds710_btree_au_move
+ action = LFDS710_BTREE_AU_MOVE_INVALID;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED,
+ load_finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ struct lfds710_btree_au_element
+ *left = NULL,
+ *right = NULL,
+ *up = NULL,
+ *up_left = NULL,
+ *up_right = NULL;
+
+ LFDS710_PAL_ASSERT( baue != NULL );
+
+ /* TRD : from any given element, the next largest element is;
+ 1. if we have a right, it's the smallest element on the left branch of our
right child
+ 2. if we don't have a right, and we're on the left of our parent, then
it's our parent
+ 3. if we don't have a right, and we're on the right of our parent or
we have no parent,
+ iterate up the tree until we find the first child who is on the left of its
parent; then it's the parent
+ */
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ while( load_finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ left = (*baue)->left;
+ right = (*baue)->right;
+ up = (*baue)->up;
+ if( up != NULL )
+ {
+ up_left = (*baue)->up->left;
+ up_right = (*baue)->up->right;
+ }
+
+ // TRD : optimization - if all already not NULL, given we're add-only, they
won't change
+ if( left != NULL and right != NULL and (up == NULL or (up != NULL and up_left != NULL
and up_right != NULL)) )
+ break;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( left == (*baue)->left and right == (*baue)->right and (up == NULL or (up !=
NULL and up == (*baue)->up and up_left == (*baue)->up->left and up_right ==
(*baue)->up->right)) )
+ load_finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( right != NULL )
+ action = LFDS710_BTREE_AU_MOVE_SMALLEST_FROM_RIGHT_CHILD;
+
+ if( right == NULL and up != NULL and up_left == *baue )
+ action = LFDS710_BTREE_AU_MOVE_GET_PARENT;
+
+ if( (right == NULL and up == NULL) or (up != NULL and up_right == *baue and right ==
NULL) )
+ action = LFDS710_BTREE_AU_MOVE_MOVE_UP_TREE;
+
+ switch( action )
+ {
+ case LFDS710_BTREE_AU_MOVE_INVALID:
+ case LFDS710_BTREE_AU_MOVE_LARGEST_FROM_LEFT_CHILD:
+ // TRD : remove compiler warning
+ break;
+
+ case LFDS710_BTREE_AU_MOVE_SMALLEST_FROM_RIGHT_CHILD:
+ *baue = right;
+ if( *baue != NULL )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ while( (*baue)->left != NULL )
+ {
+ *baue = (*baue)->left;
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+ break;
+
+ case LFDS710_BTREE_AU_MOVE_GET_PARENT:
+ *baue = up;
+ break;
+
+ case LFDS710_BTREE_AU_MOVE_MOVE_UP_TREE:
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ load_finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ while( load_finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ up = (*baue)->up;
+ if( up != NULL )
+ up_right = (*baue)->up->right;
+
+ // TRD : optimization - if all already not NULL, given we're add-only, they
won't change
+ if( up == NULL or (up != NULL and up_right != NULL) )
+ break;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( up == (*baue)->up and up_right == (*baue)->up->right )
+ load_finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( *baue != NULL and up != NULL and *baue == up_right )
+ *baue = up;
+ else
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ *baue = up;
+
+ /*
+
+ while( *baue != NULL and (*baue)->up != NULL and *baue ==
(*baue)->up->right )
+ *baue = (*baue)->up;
+
+ *baue = (*baue)->up;
+
+ */
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+int lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position( struct
lfds710_btree_au_state *baus,
+ struct
lfds710_btree_au_element **baue,
+ enum
lfds710_btree_au_absolute_position absolute_position,
+ enum
lfds710_btree_au_relative_position relative_position )
+{
+ int
+ rv;
+
+ LFDS710_PAL_ASSERT( baus != NULL );
+ LFDS710_PAL_ASSERT( baue != NULL );
+ // TRD: absolute_position can be any value in its range
+ // TRD: relative_position can be any value in its range
+
+ if( *baue == NULL )
+ rv = lfds710_btree_au_get_by_absolute_position( baus, baue, absolute_position );
+ else
+ rv = lfds710_btree_au_get_by_relative_position( baue, relative_position );
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_init.c
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_init.c
new file mode 100644
index 0000000..17da718
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_init.c
@@ -0,0 +1,33 @@
+/***** includes *****/
+#include "lfds710_btree_addonly_unbalanced_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_btree_au_init_valid_on_current_logical_core( struct lfds710_btree_au_state
*baus,
+ int
(*key_compare_function)(void const *new_key, void const *existing_key),
+ enum
lfds710_btree_au_existing_key existing_key,
+ void *user_state )
+{
+ LFDS710_PAL_ASSERT( baus != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &baus->root %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( key_compare_function != NULL );
+ // TRD : existing_key can be any value in its range
+ // TRD : user_state can be NULL
+
+ baus->root = NULL;
+ baus->key_compare_function = key_compare_function;
+ baus->existing_key = existing_key;
+ baus->user_state = user_state;
+
+ lfds710_misc_internal_backoff_init( &baus->insert_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_insert.c
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_insert.c
new file mode 100644
index 0000000..48c5098
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_insert.c
@@ -0,0 +1,158 @@
+/***** includes *****/
+#include "lfds710_btree_addonly_unbalanced_internal.h"
+
+
+
+
+
+/****************************************************************************/
+enum lfds710_btree_au_insert_result lfds710_btree_au_insert( struct
lfds710_btree_au_state *baus,
+ struct
lfds710_btree_au_element *baue,
+ struct
lfds710_btree_au_element **existing_baue )
+{
+ char unsigned
+ result = 0;
+
+ int
+ compare_result = 0;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_btree_au_element
+ *compare = NULL,
+ *volatile baue_next = NULL,
+ *volatile baue_parent = NULL,
+ *volatile baue_temp;
+
+ LFDS710_PAL_ASSERT( baus != NULL );
+ LFDS710_PAL_ASSERT( baue != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &baue->left %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &baue->right %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &baue->up %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &baue->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ // TRD : existing_baue can be NULL
+
+ /* TRD : we follow a normal search for the insert node and which side to insert
+
+ the difference is that insertion may fail because someone else inserts
+ there before we do
+
+ in this case, we resume searching for the insert node from the node
+ we were attempting to insert upon
+
+ (if we attempted to insert the root node and this failed, i.e. we thought
+ the tree was empty but then it wasn't, then we start searching from the
+ new root)
+ */
+
+ baue->up = baue->left = baue->right = NULL;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ baue_temp = baus->root;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ while( result == 0 )
+ {
+ // TRD : first we find where to insert
+ while( baue_temp != NULL )
+ {
+ compare_result = baus->key_compare_function( baue->key, baue_temp->key );
+
+ if( compare_result == 0 )
+ {
+ if( existing_baue != NULL )
+ *existing_baue = baue_temp;
+
+ switch( baus->existing_key )
+ {
+ case LFDS710_BTREE_AU_EXISTING_KEY_OVERWRITE:
+ LFDS710_BTREE_AU_SET_VALUE_IN_ELEMENT( *baue_temp, baue->value );
+ return LFDS710_BTREE_AU_INSERT_RESULT_SUCCESS_OVERWRITE;
+ break;
+
+ case LFDS710_BTREE_AU_EXISTING_KEY_FAIL:
+ return LFDS710_BTREE_AU_INSERT_RESULT_FAILURE_EXISTING_KEY;
+ break;
+ }
+ }
+
+ if( compare_result < 0 )
+ baue_next = baue_temp->left;
+
+ if( compare_result > 0 )
+ baue_next = baue_temp->right;
+
+ baue_parent = baue_temp;
+ baue_temp = baue_next;
+ if( baue_temp != NULL )
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+
+ /* TRD : second, we actually insert
+
+ at this point baue_temp has come to NULL
+ and baue_parent is the element to insert at
+ and result of the last compare indicates
+ the direction of insertion
+
+ it may be that another tree has already inserted an element with
+ the same key as ourselves, or other elements which mean our position
+ is now wrong
+
+ in this case, it is either inserted in the position we're trying
+ to insert in now, in which case our insert will fail
+
+ or, similarly, other elements will have come in where we are,
+ and our insert will fail
+ */
+
+ if( baue_parent == NULL )
+ {
+ compare = NULL;
+ baue->up = baus->root;
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &baus->root, &compare, baue,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ baue_temp = baus->root;
+ }
+
+ if( baue_parent != NULL )
+ {
+ if( compare_result <= 0 )
+ {
+ compare = NULL;
+ baue->up = baue_parent;
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &baue_parent->left, &compare, baue,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+ }
+
+ if( compare_result > 0 )
+ {
+ compare = NULL;
+ baue->up = baue_parent;
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &baue_parent->right, &compare, baue,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+ }
+
+ // TRD : if the insert fails, then resume searching at the insert node
+ if( result == 0 )
+ baue_temp = baue_parent;
+ }
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( baus->insert_backoff, backoff_iteration );
+ }
+
+ LFDS710_BACKOFF_AUTOTUNE( baus->insert_backoff, backoff_iteration );
+
+ // TRD : if we get to here, we added (not failed or overwrite on exist) a new element
+ if( existing_baue != NULL )
+ *existing_baue = NULL;
+
+ return LFDS710_BTREE_AU_INSERT_RESULT_SUCCESS;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
new file mode 100644
index 0000000..c4badeb
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_internal.h
@@ -0,0 +1,23 @@
+/***** the library-wide header file *****/
+#include "../liblfds710_internal.h"
+
+/***** enums *****/
+enum lfds710_btree_au_move
+{
+ LFDS710_BTREE_AU_MOVE_INVALID,
+ LFDS710_BTREE_AU_MOVE_SMALLEST_FROM_RIGHT_CHILD,
+ LFDS710_BTREE_AU_MOVE_LARGEST_FROM_LEFT_CHILD,
+ LFDS710_BTREE_AU_MOVE_GET_PARENT,
+ LFDS710_BTREE_AU_MOVE_MOVE_UP_TREE
+};
+
+enum lfds710_btree_au_delete_action
+{
+ LFDS710_BTREE_AU_DELETE_SELF,
+ LFDS710_BTREE_AU_DELETE_SELF_REPLACE_WITH_LEFT_CHILD,
+ LFDS710_BTREE_AU_DELETE_SELF_REPLACE_WITH_RIGHT_CHILD,
+ LFDS710_BTREE_AU_DELETE_MOVE_LEFT
+};
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_query.c
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_query.c
new file mode 100644
index 0000000..1401732
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_btree_addonly_unbalanced/lfds710_btree_addonly_unbalanced_query.c
@@ -0,0 +1,126 @@
+/***** includes *****/
+#include "lfds710_btree_addonly_unbalanced_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_btree_au_internal_validate( struct lfds710_btree_au_state *abs,
struct lfds710_misc_validation_info *vi, enum lfds710_misc_validity
*lfds710_btree_au_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_btree_au_query( struct lfds710_btree_au_state *baus,
+ enum lfds710_btree_au_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( baus != NULL );
+ // TRD : query_type can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( query_type )
+ {
+ case LFDS710_BTREE_AU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT:
+ {
+ struct lfds710_btree_au_element
+ *baue = NULL;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ while(
lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position(baus, &baue,
LFDS710_BTREE_AU_ABSOLUTE_POSITION_SMALLEST_IN_TREE,
LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_LARGER_ELEMENT_IN_ENTIRE_TREE) )
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ }
+ break;
+
+ case LFDS710_BTREE_AU_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_btree_au_internal_validate( baus, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_btree_au_internal_validate( struct lfds710_btree_au_state *baus,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_btree_au_validity )
+{
+ lfds710_pal_uint_t
+ number_elements_from_query_tree = 0,
+ number_elements_from_walk = 0;
+
+ struct lfds710_btree_au_element
+ *baue = NULL,
+ *baue_prev = NULL;
+
+ LFDS710_PAL_ASSERT( baus!= NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_btree_au_validity != NULL );
+
+ *lfds710_btree_au_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ /* TRD : validation is performed by;
+
+ performing an in-order walk
+ we should see every element is larger than the preceeding element
+ we count elements as we go along (visited elements, that is)
+ and check our tally equals the expected count
+ */
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ while( lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position(baus,
&baue, LFDS710_BTREE_AU_ABSOLUTE_POSITION_SMALLEST_IN_TREE,
LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_LARGER_ELEMENT_IN_ENTIRE_TREE) )
+ {
+ // TRD : baue_prev should always be smaller than or equal to baue
+ if( baue_prev != NULL )
+ if( baus->key_compare_function(baue_prev->key, baue->key) > 0 )
+ {
+ *lfds710_btree_au_validity = LFDS710_MISC_VALIDITY_INVALID_ORDER;
+ return;
+ }
+
+ baue_prev = baue;
+ number_elements_from_walk++;
+ }
+
+ if( *lfds710_btree_au_validity == LFDS710_MISC_VALIDITY_VALID )
+ {
+ lfds710_btree_au_query( (struct lfds710_btree_au_state *) baus,
LFDS710_BTREE_AU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL,
&number_elements_from_query_tree );
+
+ if( number_elements_from_walk > number_elements_from_query_tree )
+ *lfds710_btree_au_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+
+ if( number_elements_from_walk < number_elements_from_query_tree )
+ *lfds710_btree_au_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+ }
+
+ /* TRD : now check for expected number of elements
+ vi can be NULL, in which case we do not check
+ we know we don't have a loop from our earlier check
+ */
+
+ if( *lfds710_btree_au_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_btree_au_query( (struct lfds710_btree_au_state *) baus,
LFDS710_BTREE_AU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL,
&number_elements_from_query_tree );
+
+ if( number_elements_from_query_tree < vi->min_elements )
+ *lfds710_btree_au_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements_from_query_tree > vi->max_elements )
+ *lfds710_btree_au_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_cleanup.c
new file mode 100644
index 0000000..07fb5ca
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_cleanup.c
@@ -0,0 +1,36 @@
+/***** includes *****/
+#include "lfds710_freelist_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_freelist_cleanup( struct lfds710_freelist_state *fs,
+ void (*element_cleanup_callback)(struct
lfds710_freelist_state *fs, struct lfds710_freelist_element *fe) )
+{
+ struct lfds710_freelist_element
+ *fe,
+ *fe_temp;
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( element_cleanup_callback != NULL )
+ {
+ fe = fs->top[POINTER];
+
+ while( fe != NULL )
+ {
+ fe_temp = fe;
+ fe = fe->next;
+
+ element_cleanup_callback( fs, fe_temp );
+ }
+ }
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_init.c
b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_init.c
new file mode 100644
index 0000000..fad177a
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_init.c
@@ -0,0 +1,48 @@
+/***** includes *****/
+#include "lfds710_freelist_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_freelist_init_valid_on_current_logical_core( struct lfds710_freelist_state
*fs,
+ struct lfds710_freelist_element
* volatile
(*elimination_array)[LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS],
+ lfds710_pal_uint_t
elimination_array_size_in_elements,
+ void *user_state )
+{
+ lfds710_pal_uint_t
+ loop,
+ subloop;
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) fs->top %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &fs->elimination_array_size_in_elements
% LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ // TRD : elimination_array can be NULL
+ LFDS710_PAL_ASSERT( (elimination_array == NULL) or
+ ( (elimination_array != NULL) and (lfds710_pal_uint_t)
elimination_array % LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 ) );
+ LFDS710_PAL_ASSERT( (elimination_array == NULL and elimination_array_size_in_elements
== 0) or
+ (elimination_array != NULL and elimination_array_size_in_elements
>= 2 and (elimination_array_size_in_elements &
(elimination_array_size_in_elements-1)) == 0) );
+ // TRD : user_state can be NULL
+
+ fs->top[POINTER] = NULL;
+ fs->top[COUNTER] = 0;
+
+ fs->elimination_array = elimination_array;
+ fs->elimination_array_size_in_elements = elimination_array_size_in_elements;
+ fs->user_state = user_state;
+
+ for( loop = 0 ; loop < elimination_array_size_in_elements ; loop++ )
+ for( subloop = 0 ; subloop <
LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS ; subloop++ )
+ fs->elimination_array[loop][subloop] = NULL;
+
+ lfds710_misc_internal_backoff_init( &fs->pop_backoff );
+ lfds710_misc_internal_backoff_init( &fs->push_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_internal.h
b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_internal.h
new file mode 100644
index 0000000..ee0bf97
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_internal.h
@@ -0,0 +1,7 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+void lfds710_freelist_internal_push_without_ea( struct lfds710_freelist_state *fs,
+ struct lfds710_freelist_element *fe );
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_pop.c
b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_pop.c
new file mode 100644
index 0000000..a40253b
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_pop.c
@@ -0,0 +1,89 @@
+/***** includes *****/
+#include "lfds710_freelist_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_freelist_pop( struct lfds710_freelist_state *fs,
+ struct lfds710_freelist_element **fe,
+ struct lfds710_prng_st_state *psts )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE,
+ elimination_array_index,
+ loop,
+ random_value;
+
+ struct lfds710_freelist_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *new_top[PAC_SIZE],
+ *volatile original_top[PAC_SIZE];
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ LFDS710_PAL_ASSERT( fe != NULL );
+ // TRD : psts can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( fs->elimination_array_size_in_elements > 0 )
+ {
+ if( psts != NULL )
+ {
+ LFDS710_PRNG_ST_GENERATE( *psts, random_value );
+ elimination_array_index = ( random_value &
(fs->elimination_array_size_in_elements-1) );
+ }
+ else
+ {
+ elimination_array_index = (lfds710_pal_uint_t) fe;
+ LFDS710_PRNG_ST_MIXING_FUNCTION( elimination_array_index );
+ elimination_array_index = ( elimination_array_index &
(fs->elimination_array_size_in_elements-1) );
+ }
+
+ // TRD : full scan of one cache line, max pointers per cache line
+
+ *fe = NULL;
+
+ for( loop = 0 ; loop <
LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS ; loop++ )
+ if( fs->elimination_array[elimination_array_index][loop] != NULL )
+ {
+ LFDS710_PAL_ATOMIC_EXCHANGE(
&fs->elimination_array[elimination_array_index][loop], *fe, struct
lfds710_freelist_element * );
+ if( *fe != NULL )
+ return 1;
+ }
+ }
+
+ original_top[COUNTER] = fs->top[COUNTER];
+ original_top[POINTER] = fs->top[POINTER];
+
+ do
+ {
+ if( original_top[POINTER] == NULL )
+ {
+ *fe = NULL;
+ return 0;
+ }
+
+ new_top[COUNTER] = original_top[COUNTER] + 1;
+ new_top[POINTER] = original_top[POINTER]->next;
+
+ LFDS710_PAL_ATOMIC_DWCAS( fs->top, original_top, new_top,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ {
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( fs->pop_backoff, backoff_iteration );
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+ while( result == 0 );
+
+ *fe = original_top[POINTER];
+
+ LFDS710_BACKOFF_AUTOTUNE( fs->pop_backoff, backoff_iteration );
+
+ return 1;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_push.c
b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_push.c
new file mode 100644
index 0000000..c9cfca9
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_push.c
@@ -0,0 +1,123 @@
+/***** includes *****/
+#include "lfds710_freelist_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_freelist_push( struct lfds710_freelist_state *fs,
+ struct lfds710_freelist_element *fe,
+ struct lfds710_prng_st_state *psts )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE,
+ elimination_array_index,
+ loop,
+ random_value;
+
+ struct lfds710_freelist_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *new_top[PAC_SIZE],
+ *volatile original_top[PAC_SIZE];
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ LFDS710_PAL_ASSERT( fe != NULL );
+ // TRD : psts can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( fs->elimination_array_size_in_elements > 0 )
+ {
+ if( psts != NULL )
+ {
+ LFDS710_PRNG_ST_GENERATE( *psts, random_value );
+ elimination_array_index = ( random_value &
(fs->elimination_array_size_in_elements-1) );
+ }
+ else
+ {
+ elimination_array_index = (lfds710_pal_uint_t) fe;
+ LFDS710_PRNG_ST_MIXING_FUNCTION( elimination_array_index );
+ elimination_array_index = ( elimination_array_index &
(fs->elimination_array_size_in_elements-1) );
+ }
+
+ // TRD : full scan of one cache line, max pointers per cache line
+
+ for( loop = 0 ; loop <
LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS ; loop++ )
+ if( fs->elimination_array[elimination_array_index][loop] == NULL )
+ {
+ LFDS710_PAL_ATOMIC_EXCHANGE(
&fs->elimination_array[elimination_array_index][loop], fe, struct
lfds710_freelist_element * );
+ if( fe == NULL )
+ return;
+ }
+ }
+
+ new_top[POINTER] = fe;
+
+ original_top[COUNTER] = fs->top[COUNTER];
+ original_top[POINTER] = fs->top[POINTER];
+
+ do
+ {
+ fe->next = original_top[POINTER];
+ LFDS710_MISC_BARRIER_STORE;
+
+ new_top[COUNTER] = original_top[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( fs->top, original_top, new_top,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( fs->push_backoff, backoff_iteration );
+ }
+ while( result == 0 );
+
+ LFDS710_BACKOFF_AUTOTUNE( fs->push_backoff, backoff_iteration );
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+void lfds710_freelist_internal_push_without_ea( struct lfds710_freelist_state *fs,
+ struct lfds710_freelist_element *fe )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_freelist_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *new_top[PAC_SIZE],
+ *volatile original_top[PAC_SIZE];
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ LFDS710_PAL_ASSERT( fe != NULL );
+
+ new_top[POINTER] = fe;
+
+ original_top[COUNTER] = fs->top[COUNTER];
+ original_top[POINTER] = fs->top[POINTER];
+
+ do
+ {
+ fe->next = original_top[POINTER];
+ LFDS710_MISC_BARRIER_STORE;
+
+ new_top[COUNTER] = original_top[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( fs->top, original_top, new_top,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( fs->push_backoff, backoff_iteration );
+ }
+ while( result == 0 );
+
+ LFDS710_BACKOFF_AUTOTUNE( fs->push_backoff, backoff_iteration );
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_query.c
b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_query.c
new file mode 100644
index 0000000..ffb7bb4
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_freelist/lfds710_freelist_query.c
@@ -0,0 +1,152 @@
+/***** includes *****/
+#include "lfds710_freelist_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_freelist_internal_freelist_validate( struct lfds710_freelist_state
*fs,
+ struct
lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_freelist_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_freelist_query( struct lfds710_freelist_state *fs,
+ enum lfds710_freelist_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ struct lfds710_freelist_element
+ *fe;
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ // TRD : query_type can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( query_type )
+ {
+ case LFDS710_FREELIST_QUERY_SINGLETHREADED_GET_COUNT:
+ {
+ lfds710_pal_uint_t
+ loop,
+ subloop;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ // TRD : count the elements in the elimination array
+ for( loop = 0 ; loop < fs->elimination_array_size_in_elements ; loop++ )
+ for( subloop = 0 ; subloop <
LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS ; subloop++ )
+ if( fs->elimination_array[loop][subloop] != NULL )
+ ( *(lfds710_pal_uint_t *) query_output )++;
+
+ // TRD : count the elements on the freelist
+ fe = (struct lfds710_freelist_element *) fs->top[POINTER];
+
+ while( fe != NULL )
+ {
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ fe = (struct lfds710_freelist_element *) fe->next;
+ }
+ }
+ break;
+
+ case LFDS710_FREELIST_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_freelist_internal_freelist_validate( fs, (struct
lfds710_misc_validation_info *) query_input, (enum lfds710_misc_validity *) query_output
);
+ break;
+
+ case
LFDS710_FREELIST_QUERY_GET_ELIMINATION_ARRAY_EXTRA_ELEMENTS_IN_FREELIST_ELEMENTS:
+ {
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ ( *(lfds710_pal_uint_t *) query_output ) =
(fs->elimination_array_size_in_elements-1) *
LFDS710_FREELIST_ELIMINATION_ARRAY_ELEMENT_SIZE_IN_FREELIST_ELEMENTS;
+ }
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_freelist_internal_freelist_validate( struct lfds710_freelist_state
*fs,
+ struct
lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_freelist_validity )
+{
+ lfds710_pal_uint_t
+ number_elements = 0;
+
+ struct lfds710_freelist_element
+ *fe_slow,
+ *fe_fast;
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_freelist_validity != NULL );
+
+ *lfds710_freelist_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ fe_slow = fe_fast = (struct lfds710_freelist_element *) fs->top[POINTER];
+
+ /* TRD : first, check for a loop
+ we have two pointers
+ both of which start at the top of the freelist
+ we enter a loop
+ and on each iteration
+ we advance one pointer by one element
+ and the other by two
+
+ we exit the loop when both pointers are NULL
+ (have reached the end of the freelist)
+
+ or
+
+ if we fast pointer 'sees' the slow pointer
+ which means we have a loop
+ */
+
+ if( fe_slow != NULL )
+ do
+ {
+ fe_slow = fe_slow->next;
+
+ if( fe_fast != NULL )
+ fe_fast = fe_fast->next;
+
+ if( fe_fast != NULL )
+ fe_fast = fe_fast->next;
+ }
+ while( fe_slow != NULL and fe_fast != fe_slow );
+
+ if( fe_fast != NULL and fe_slow != NULL and fe_fast == fe_slow )
+ *lfds710_freelist_validity = LFDS710_MISC_VALIDITY_INVALID_LOOP;
+
+ /* TRD : now check for expected number of elements
+ vi can be NULL, in which case we do not check
+ we know we don't have a loop from our earlier check
+ */
+
+ if( *lfds710_freelist_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_freelist_query( fs, LFDS710_FREELIST_QUERY_SINGLETHREADED_GET_COUNT, NULL,
(void *) &number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_freelist_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_freelist_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_cleanup.c
new file mode 100644
index 0000000..4d39bd5
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_cleanup.c
@@ -0,0 +1,63 @@
+/***** includes *****/
+#include "lfds710_hash_addonly_internal.h"
+
+/***** private prototypes*****/
+static void btree_au_element_cleanup_function( struct lfds710_btree_au_state *baus,
+ struct lfds710_btree_au_element *baue );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_hash_a_cleanup( struct lfds710_hash_a_state *has,
+ void (*element_cleanup_callback)(struct lfds710_hash_a_state
*has, struct lfds710_hash_a_element *hae) )
+{
+ lfds710_pal_uint_t
+ loop;
+
+ LFDS710_PAL_ASSERT( has != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ if( element_cleanup_callback == NULL )
+ return;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ has->element_cleanup_callback = element_cleanup_callback;
+
+ for( loop = 0 ; loop < has->array_size ; loop++ )
+ lfds710_btree_au_cleanup( has->baus_array+loop, btree_au_element_cleanup_function
);
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+#pragma warning( disable : 4100 )
+
+static void btree_au_element_cleanup_function( struct lfds710_btree_au_state *baus,
+ struct lfds710_btree_au_element *baue )
+{
+ struct lfds710_hash_a_state
+ *has;
+
+ struct lfds710_hash_a_element
+ *hae;
+
+ LFDS710_PAL_ASSERT( baus != NULL );
+ LFDS710_PAL_ASSERT( baue != NULL );
+
+ hae = (struct lfds710_hash_a_element *) LFDS710_BTREE_AU_GET_VALUE_FROM_ELEMENT( *baue
);
+ has = (struct lfds710_hash_a_state *) LFDS710_BTREE_AU_GET_USER_STATE_FROM_STATE( *baus
);
+
+ has->element_cleanup_callback( has, hae );
+
+ return;
+}
+
+#pragma warning( default : 4100 )
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_get.c
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_get.c
new file mode 100644
index 0000000..1b3bc87
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_get.c
@@ -0,0 +1,47 @@
+/***** includes *****/
+#include "lfds710_hash_addonly_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_hash_a_get_by_key( struct lfds710_hash_a_state *has,
+ int (*key_compare_function)(void const *new_key, void
const *existing_key),
+ void (*key_hash_function)(void const *key,
lfds710_pal_uint_t *hash),
+ void *key,
+ struct lfds710_hash_a_element **hae )
+{
+ int
+ rv;
+
+ lfds710_pal_uint_t
+ hash = 0;
+
+ struct lfds710_btree_au_element
+ *baue;
+
+ LFDS710_PAL_ASSERT( has != NULL );
+ // TRD : key_compare_function can be NULL
+ // TRD : key_hash_function can be NULL
+ // TRD : key can be NULL
+ LFDS710_PAL_ASSERT( hae != NULL );
+
+ if( key_compare_function == NULL )
+ key_compare_function = has->key_compare_function;
+
+ if( key_hash_function == NULL )
+ key_hash_function = has->key_hash_function;
+
+ key_hash_function( key, &hash );
+
+ rv = lfds710_btree_au_get_by_key( has->baus_array + (hash % has->array_size),
key_compare_function, key, &baue );
+
+ if( rv == 1 )
+ *hae = LFDS710_BTREE_AU_GET_VALUE_FROM_ELEMENT( *baue );
+ else
+ *hae = NULL;
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_init.c
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_init.c
new file mode 100644
index 0000000..3a8bf04
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_init.c
@@ -0,0 +1,54 @@
+/***** includes *****/
+#include "lfds710_hash_addonly_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_hash_a_init_valid_on_current_logical_core( struct lfds710_hash_a_state
*has,
+ struct lfds710_btree_au_state
*baus_array,
+ lfds710_pal_uint_t array_size,
+ int (*key_compare_function)(void
const *new_key, void const *existing_key),
+ void (*key_hash_function)(void
const *key, lfds710_pal_uint_t *hash),
+ enum lfds710_hash_a_existing_key
existing_key,
+ void *user_state )
+{
+ enum lfds710_btree_au_existing_key
+ btree_au_existing_key = LFDS710_BTREE_AU_EXISTING_KEY_OVERWRITE; // TRD : for
compiler warning
+
+ lfds710_pal_uint_t
+ loop;
+
+ LFDS710_PAL_ASSERT( has != NULL );
+ LFDS710_PAL_ASSERT( baus_array != NULL );
+ LFDS710_PAL_ASSERT( array_size > 0 );
+ LFDS710_PAL_ASSERT( key_compare_function != NULL );
+ LFDS710_PAL_ASSERT( key_hash_function != NULL );
+ // TRD : existing_key can be any value in its range
+ // TRD : user_state can be NULL
+
+ has->array_size = array_size;
+ has->key_compare_function = key_compare_function;
+ has->key_hash_function = key_hash_function;
+ has->existing_key = existing_key;
+ has->baus_array = baus_array;
+ has->user_state = user_state;
+
+ if( has->existing_key == LFDS710_HASH_A_EXISTING_KEY_OVERWRITE )
+ btree_au_existing_key = LFDS710_BTREE_AU_EXISTING_KEY_OVERWRITE;
+
+ if( has->existing_key == LFDS710_HASH_A_EXISTING_KEY_FAIL )
+ btree_au_existing_key = LFDS710_BTREE_AU_EXISTING_KEY_FAIL;
+
+ // TRD : since the addonly_hash atomic counts, if that flag is set, the
btree_addonly_unbalanceds don't have to
+ for( loop = 0 ; loop < array_size ; loop++ )
+ lfds710_btree_au_init_valid_on_current_logical_core( has->baus_array+loop,
key_compare_function, btree_au_existing_key, user_state );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_insert.c
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_insert.c
new file mode 100644
index 0000000..04f65d5
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_insert.c
@@ -0,0 +1,60 @@
+/***** includes *****/
+#include "lfds710_hash_addonly_internal.h"
+
+
+
+
+
+/****************************************************************************/
+enum lfds710_hash_a_insert_result lfds710_hash_a_insert( struct lfds710_hash_a_state
*has,
+ struct lfds710_hash_a_element
*hae,
+ struct lfds710_hash_a_element
**existing_hae )
+{
+ enum lfds710_hash_a_insert_result
+ apr = LFDS710_HASH_A_PUT_RESULT_SUCCESS;
+
+ enum lfds710_btree_au_insert_result
+ alr;
+
+ lfds710_pal_uint_t
+ hash = 0;
+
+ struct lfds710_btree_au_element
+ *existing_baue;
+
+ LFDS710_PAL_ASSERT( has != NULL );
+ LFDS710_PAL_ASSERT( hae != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &hae->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ // TRD : existing_hae can be NULL
+
+ // TRD : alignment checks
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &hae->baue %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+
+ has->key_hash_function( hae->key, &hash );
+
+ LFDS710_BTREE_AU_SET_KEY_IN_ELEMENT( hae->baue, hae->key );
+ LFDS710_BTREE_AU_SET_VALUE_IN_ELEMENT( hae->baue, hae );
+
+ alr = lfds710_btree_au_insert( has->baus_array + (hash % has->array_size),
&hae->baue, &existing_baue );
+
+ switch( alr )
+ {
+ case LFDS710_BTREE_AU_INSERT_RESULT_FAILURE_EXISTING_KEY:
+ if( existing_hae != NULL )
+ *existing_hae = LFDS710_BTREE_AU_GET_VALUE_FROM_ELEMENT( *existing_baue );
+
+ apr = LFDS710_HASH_A_PUT_RESULT_FAILURE_EXISTING_KEY;
+ break;
+
+ case LFDS710_BTREE_AU_INSERT_RESULT_SUCCESS_OVERWRITE:
+ apr = LFDS710_HASH_A_PUT_RESULT_SUCCESS_OVERWRITE;
+ break;
+
+ case LFDS710_BTREE_AU_INSERT_RESULT_SUCCESS:
+ apr = LFDS710_HASH_A_PUT_RESULT_SUCCESS;
+ break;
+ }
+
+ return apr;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_iterate.c
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_iterate.c
new file mode 100644
index 0000000..0a861f6
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_iterate.c
@@ -0,0 +1,60 @@
+/***** includes *****/
+#include "lfds710_hash_addonly_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_hash_a_iterate_init( struct lfds710_hash_a_state *has,
+ struct lfds710_hash_a_iterate *hai )
+{
+ LFDS710_PAL_ASSERT( has != NULL );
+ LFDS710_PAL_ASSERT( hai != NULL );
+
+ hai->baus = has->baus_array;
+ hai->baus_end = has->baus_array + has->array_size;
+ hai->baue = NULL;
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+int lfds710_hash_a_iterate( struct lfds710_hash_a_iterate *hai,
+ struct lfds710_hash_a_element **hae )
+{
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ int
+ rv = 0;
+
+ LFDS710_PAL_ASSERT( hai != NULL );
+ LFDS710_PAL_ASSERT( hae != NULL );
+
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ lfds710_btree_au_get_by_absolute_position_and_then_by_relative_position(
hai->baus, &hai->baue, LFDS710_BTREE_AU_ABSOLUTE_POSITION_SMALLEST_IN_TREE,
LFDS710_BTREE_AU_RELATIVE_POSITION_NEXT_LARGER_ELEMENT_IN_ENTIRE_TREE );
+
+ if( hai->baue != NULL )
+ {
+ *hae = LFDS710_BTREE_AU_GET_VALUE_FROM_ELEMENT( *hai->baue );
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ rv = 1;
+ }
+
+ if( hai->baue == NULL )
+ if( ++hai->baus == hai->baus_end )
+ {
+ *hae = NULL;
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+ }
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_query.c
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_query.c
new file mode 100644
index 0000000..66e5897
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_hash_addonly/lfds710_hash_addonly_query.c
@@ -0,0 +1,119 @@
+/***** includes *****/
+#include "lfds710_hash_addonly_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_hash_a_internal_validate( struct lfds710_hash_a_state *has,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_hash_a_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_hash_a_query( struct lfds710_hash_a_state *has,
+ enum lfds710_hash_a_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( has != NULL );
+ // TRD : query_type can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( query_type )
+ {
+ case LFDS710_HASH_A_QUERY_GET_POTENTIALLY_INACCURATE_COUNT:
+ {
+ struct lfds710_hash_a_iterate
+ ai;
+
+ struct lfds710_hash_a_element
+ *hae;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ lfds710_hash_a_iterate_init( has, &ai );
+
+ while( lfds710_hash_a_iterate(&ai, &hae) )
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ }
+ break;
+
+ case LFDS710_HASH_A_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD: query_input can be any value in its range
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_hash_a_internal_validate( has, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_hash_a_internal_validate( struct lfds710_hash_a_state *has,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_hash_a_validity )
+{
+ lfds710_pal_uint_t
+ lfds710_hash_a_total_number_elements = 0,
+ lfds710_btree_au_total_number_elements = 0,
+ number_elements;
+
+ lfds710_pal_uint_t
+ loop;
+
+ LFDS710_PAL_ASSERT( has!= NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_hash_a_validity != NULL );
+
+ /* TRD : validate every btree_addonly_unbalanced in the addonly_hash
+ sum elements in each btree_addonly_unbalanced
+ check matches expected element counts (if vi is provided)
+ */
+
+ *lfds710_hash_a_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ for( loop = 0 ; *lfds710_hash_a_validity == LFDS710_MISC_VALIDITY_VALID and loop <
has->array_size ; loop++ )
+ lfds710_btree_au_query( has->baus_array+loop,
LFDS710_BTREE_AU_QUERY_SINGLETHREADED_VALIDATE, NULL, (void *) lfds710_hash_a_validity );
+
+ if( *lfds710_hash_a_validity == LFDS710_MISC_VALIDITY_VALID )
+ {
+ for( loop = 0 ; loop < has->array_size ; loop++ )
+ {
+ lfds710_btree_au_query( has->baus_array+loop,
LFDS710_BTREE_AU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL, (void *)
&number_elements );
+ lfds710_btree_au_total_number_elements += number_elements;
+ }
+
+ // TRD : first, check btree_addonly_unbalanced total vs the addonly_hash total
+ lfds710_hash_a_query( has, LFDS710_HASH_A_QUERY_GET_POTENTIALLY_INACCURATE_COUNT,
NULL, &lfds710_hash_a_total_number_elements );
+
+ // TRD : the btree_addonly_unbalanceds are assumed to speak the truth
+ if( lfds710_hash_a_total_number_elements < lfds710_btree_au_total_number_elements
)
+ *lfds710_hash_a_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+
+ if( lfds710_hash_a_total_number_elements > lfds710_btree_au_total_number_elements
)
+ *lfds710_hash_a_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ // TRD : second, if we're still valid and vi is provided, check the
btree_addonly_unbalanced total against vi
+ if( *lfds710_hash_a_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ if( lfds710_btree_au_total_number_elements < vi->min_elements )
+ *lfds710_hash_a_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( lfds710_btree_au_total_number_elements > vi->max_elements )
+ *lfds710_hash_a_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_cleanup.c
new file mode 100644
index 0000000..9a5cad7
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_cleanup.c
@@ -0,0 +1,37 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_ordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_aso_cleanup( struct lfds710_list_aso_state *lasos,
+ void (*element_cleanup_callback)(struct
lfds710_list_aso_state *lasos, struct lfds710_list_aso_element *lasoe) )
+{
+ struct lfds710_list_aso_element
+ *lasoe,
+ *temp;
+
+ LFDS710_PAL_ASSERT( lasos != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( element_cleanup_callback == NULL )
+ return;
+
+ lasoe = LFDS710_LIST_ASO_GET_START( *lasos );
+
+ while( lasoe != NULL )
+ {
+ temp = lasoe;
+
+ lasoe = LFDS710_LIST_ASO_GET_NEXT( *lasoe );
+
+ element_cleanup_callback( lasos, temp );
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_get.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_get.c
new file mode 100644
index 0000000..2f87131
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_get.c
@@ -0,0 +1,29 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_ordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_list_aso_get_by_key( struct lfds710_list_aso_state *lasos,
+ void *key,
+ struct lfds710_list_aso_element **lasoe )
+{
+ int
+ cr = !0,
+ rv = 1;
+
+ LFDS710_PAL_ASSERT( lasos != NULL );
+ // TRD : key can be NULL
+ LFDS710_PAL_ASSERT( lasoe != NULL );
+
+ while( cr != 0 and LFDS710_LIST_ASO_GET_START_AND_THEN_NEXT(*lasos, *lasoe) )
+ cr = lasos->key_compare_function( key, (*lasoe)->key );
+
+ if( *lasoe == NULL )
+ rv = 0;
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_init.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_init.c
new file mode 100644
index 0000000..4eba59a
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_init.c
@@ -0,0 +1,38 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_ordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_aso_init_valid_on_current_logical_core( struct lfds710_list_aso_state
*lasos,
+ int
(*key_compare_function)(void const *new_key, void const *existing_key),
+ enum
lfds710_list_aso_existing_key existing_key,
+ void *user_state )
+{
+ LFDS710_PAL_ASSERT( lasos != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasos->dummy_element %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasos->start %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( key_compare_function != NULL );
+ // TRD : existing_key can be any value in its range
+ // TRD : user_state can be NULL
+
+ // TRD : dummy start element - makes code easier when you can always use ->next
+ lasos->start = &lasos->dummy_element;
+
+ lasos->start->next = NULL;
+ lasos->start->value = NULL;
+ lasos->key_compare_function = key_compare_function;
+ lasos->existing_key = existing_key;
+ lasos->user_state = user_state;
+
+ lfds710_misc_internal_backoff_init( &lasos->insert_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_insert.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_insert.c
new file mode 100644
index 0000000..9b8c2e7
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_insert.c
@@ -0,0 +1,136 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_ordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+enum lfds710_list_aso_insert_result lfds710_list_aso_insert( struct
lfds710_list_aso_state *lasos,
+ struct
lfds710_list_aso_element *lasoe,
+ struct
lfds710_list_aso_element **existing_lasoe )
+{
+ char unsigned
+ result;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ int
+ compare_result = 0;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_list_aso_element
+ *volatile lasoe_temp = NULL,
+ *volatile lasoe_trailing;
+
+ LFDS710_PAL_ASSERT( lasos != NULL );
+ LFDS710_PAL_ASSERT( lasoe != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasoe->next %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasoe->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ // TRD : existing_lasoe can be NULL
+
+ /* TRD : imagine a list, sorted small to large
+
+ we arrive at an element
+ we obtain its next pointer
+ we check we are greater than the current element and smaller than the next
element
+ this means we have found the correct location to insert
+ we try to CAS ourselves in; in the meantime,
+ someone else has *aready* swapped in an element which is smaller than we are
+
+ e.g.
+
+ the list is { 1, 10 } and we are the value 5
+
+ we arrive at 1; we check the next element and see it is 10
+ so we are larger than the current element and smaller than the next
+ we are in the correct location to insert and we go to insert...
+
+ in the meantime, someone else with the value 3 comes along
+ he too finds this is the correct location and inserts before we do
+ the list is now { 1, 3, 10 } and we are trying to insert now after
+ 1 and before 3!
+
+ our insert CAS fails, because the next pointer of 1 has changed aready;
+ but we see we are in the wrong location - we need to move forward an
+ element
+ */
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ /* TRD : we need to begin with the leading dummy element
+ as the element to be inserted
+ may be smaller than all elements in the list
+ */
+
+ lasoe_trailing = lasos->start;
+ lasoe_temp = lasos->start->next;
+
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ if( lasoe_temp == NULL )
+ compare_result = -1;
+
+ if( lasoe_temp != NULL )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ compare_result = lasos->key_compare_function( lasoe->key, lasoe_temp->key
);
+ }
+
+ if( compare_result == 0 )
+ {
+ if( existing_lasoe != NULL )
+ *existing_lasoe = lasoe_temp;
+
+ switch( lasos->existing_key )
+ {
+ case LFDS710_LIST_ASO_EXISTING_KEY_OVERWRITE:
+ LFDS710_LIST_ASO_SET_VALUE_IN_ELEMENT( *lasoe_temp, lasoe->value );
+ return LFDS710_LIST_ASO_INSERT_RESULT_SUCCESS_OVERWRITE;
+ break;
+
+ case LFDS710_LIST_ASO_EXISTING_KEY_FAIL:
+ return LFDS710_LIST_ASO_INSERT_RESULT_FAILURE_EXISTING_KEY;
+ break;
+ }
+
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( compare_result < 0 )
+ {
+ lasoe->next = lasoe_temp;
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &lasoe_trailing->next, (struct
lfds710_list_aso_element **) &lasoe->next, lasoe, LFDS710_MISC_CAS_STRENGTH_WEAK,
result );
+
+ if( result == 1 )
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ else
+ {
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( lasos->insert_backoff, backoff_iteration
);
+ // TRD : if we fail to link, someone else has linked and so we need to
redetermine our position is correct
+ lasoe_temp = lasoe_trailing->next;
+ }
+ }
+
+ if( compare_result > 0 )
+ {
+ // TRD : move trailing along by one element
+ lasoe_trailing = lasoe_trailing->next;
+
+ /* TRD : set temp as the element after trailing
+ if the new element we're linking is larger than all elements in the
list,
+ lasoe_temp will now go to NULL and we'll link at the end
+ */
+ lasoe_temp = lasoe_trailing->next;
+ }
+ }
+
+ LFDS710_BACKOFF_AUTOTUNE( lasos->insert_backoff, backoff_iteration );
+
+ return LFDS710_LIST_ASO_INSERT_RESULT_SUCCESS;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_query.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_query.c
new file mode 100644
index 0000000..b982654
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_ordered/lfds710_list_addonly_singlylinked_ordered_query.c
@@ -0,0 +1,128 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_ordered_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_list_aso_internal_validate( struct lfds710_list_aso_state *lasos,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_list_aso_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_aso_query( struct lfds710_list_aso_state *lasos,
+ enum lfds710_list_aso_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( lasos != NULL );
+ // TRD : query_type can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( query_type )
+ {
+ case LFDS710_LIST_ASO_QUERY_GET_POTENTIALLY_INACCURATE_COUNT:
+ {
+ struct lfds710_list_aso_element
+ *lasoe = NULL;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ while( LFDS710_LIST_ASO_GET_START_AND_THEN_NEXT(*lasos, lasoe) )
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ }
+ break;
+
+ case LFDS710_LIST_ASO_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_list_aso_internal_validate( lasos, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_list_aso_internal_validate( struct lfds710_list_aso_state *lasos,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_list_aso_validity )
+{
+ lfds710_pal_uint_t
+ number_elements = 0;
+
+ struct lfds710_list_aso_element
+ *lasoe_fast,
+ *lasoe_slow;
+
+ LFDS710_PAL_ASSERT( lasos!= NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_list_aso_validity != NULL );
+
+ *lfds710_list_aso_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ lasoe_slow = lasoe_fast = lasos->start->next;
+
+ /* TRD : first, check for a loop
+ we have two pointers
+ both of which start at the start of the list
+ we enter a loop
+ and on each iteration
+ we advance one pointer by one element
+ and the other by two
+
+ we exit the loop when both pointers are NULL
+ (have reached the end of the queue)
+
+ or
+
+ if we fast pointer 'sees' the slow pointer
+ which means we have a loop
+ */
+
+ if( lasoe_slow != NULL )
+ do
+ {
+ lasoe_slow = lasoe_slow->next;
+
+ if( lasoe_fast != NULL )
+ lasoe_fast = lasoe_fast->next;
+
+ if( lasoe_fast != NULL )
+ lasoe_fast = lasoe_fast->next;
+ }
+ while( lasoe_slow != NULL and lasoe_fast != lasoe_slow );
+
+ if( lasoe_fast != NULL and lasoe_slow != NULL and lasoe_fast == lasoe_slow )
+ *lfds710_list_aso_validity = LFDS710_MISC_VALIDITY_INVALID_LOOP;
+
+ /* TRD : now check for expected number of elements
+ vi can be NULL, in which case we do not check
+ we know we don't have a loop from our earlier check
+ */
+
+ if( *lfds710_list_aso_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_list_aso_query( lasos,
LFDS710_LIST_ASO_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL, &number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_list_aso_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_list_aso_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_cleanup.c
new file mode 100644
index 0000000..78969dc
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_cleanup.c
@@ -0,0 +1,37 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_unordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_asu_cleanup( struct lfds710_list_asu_state *lasus,
+ void (*element_cleanup_callback)(struct
lfds710_list_asu_state *lasus, struct lfds710_list_asu_element *lasue) )
+{
+ struct lfds710_list_asu_element
+ *lasue,
+ *temp;
+
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( element_cleanup_callback == NULL )
+ return;
+
+ lasue = LFDS710_LIST_ASU_GET_START( *lasus );
+
+ while( lasue != NULL )
+ {
+ temp = lasue;
+
+ lasue = LFDS710_LIST_ASU_GET_NEXT( *lasue );
+
+ element_cleanup_callback( lasus, temp );
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_get.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_get.c
new file mode 100644
index 0000000..f36483c
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_get.c
@@ -0,0 +1,33 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_unordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_list_asu_get_by_key( struct lfds710_list_asu_state *lasus,
+ int (*key_compare_function)(void const *new_key, void
const *existing_key),
+ void *key,
+ struct lfds710_list_asu_element **lasue )
+{
+ int
+ cr = !0,
+ rv = 1;
+
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ LFDS710_PAL_ASSERT( key_compare_function != NULL );
+ // TRD : key can be NULL
+ LFDS710_PAL_ASSERT( lasue != NULL );
+
+ *lasue = NULL;
+
+ while( cr != 0 and LFDS710_LIST_ASU_GET_START_AND_THEN_NEXT(*lasus, *lasue) )
+ cr = key_compare_function( key, (*lasue)->key );
+
+ if( *lasue == NULL )
+ rv = 0;
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_init.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_init.c
new file mode 100644
index 0000000..cce71db
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_init.c
@@ -0,0 +1,35 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_unordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_asu_init_valid_on_current_logical_core( struct lfds710_list_asu_state
*lasus,
+ void *user_state )
+{
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasus->dummy_element %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasus->end %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasus->start %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ // TRD : user_state can be NULL
+
+ // TRD : dummy start element - makes code easier when you can always use ->next
+ lasus->start = lasus->end = &lasus->dummy_element;
+
+ lasus->start->next = NULL;
+ lasus->start->value = NULL;
+ lasus->user_state = user_state;
+
+ lfds710_misc_internal_backoff_init( &lasus->after_backoff );
+ lfds710_misc_internal_backoff_init( &lasus->start_backoff );
+ lfds710_misc_internal_backoff_init( &lasus->end_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_insert.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_insert.c
new file mode 100644
index 0000000..ba42d1f
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_insert.c
@@ -0,0 +1,193 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_unordered_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_asu_insert_at_position( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue,
+ struct lfds710_list_asu_element
*lasue_predecessor,
+ enum lfds710_list_asu_position position )
+{
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ LFDS710_PAL_ASSERT( lasue != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->next %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ // TRD : lasue_predecessor asserted in the switch
+ // TRD : position can be any value in its range
+
+ switch( position )
+ {
+ case LFDS710_LIST_ASU_POSITION_START:
+ lfds710_list_asu_insert_at_start( lasus, lasue );
+ break;
+
+ case LFDS710_LIST_ASU_POSITION_END:
+ lfds710_list_asu_insert_at_end( lasus, lasue );
+ break;
+
+ case LFDS710_LIST_ASU_POSITION_AFTER:
+ lfds710_list_asu_insert_after_element( lasus, lasue, lasue_predecessor );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_asu_insert_at_start( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ LFDS710_PAL_ASSERT( lasue != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->next %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ lasue->next = lasus->start->next;
+
+ do
+ {
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &lasus->start->next, (struct
lfds710_list_asu_element **) &lasue->next, lasue, LFDS710_MISC_CAS_STRENGTH_WEAK,
result );
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( lasus->start_backoff, backoff_iteration );
+ }
+ while( result == 0 );
+
+ LFDS710_BACKOFF_AUTOTUNE( lasus->start_backoff, backoff_iteration );
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_asu_insert_at_end( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue )
+{
+ char unsigned
+ result;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_list_asu_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_SINGLE_POINTER)
+ *compare;
+
+ struct lfds710_list_asu_element
+ *volatile lasue_next,
+ *volatile lasue_end;
+
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ LFDS710_PAL_ASSERT( lasue != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->next %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+
+ /* TRD : begin by assuming end is correctly pointing to the final element
+ try to link (comparing for next being NULL)
+ if we fail, move down list till we find last element
+ and retry
+ when successful, update end to ourselves
+
+ note there's a leading dummy element
+ so lasus->end always points to an element
+ */
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ lasue->next = NULL;
+ lasue_end = lasus->end;
+
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ compare = NULL;
+
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &lasue_end->next, &compare, lasue,
LFDS710_MISC_CAS_STRENGTH_STRONG, result );
+
+ if( result == 1 )
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ else
+ {
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( lasus->end_backoff, backoff_iteration );
+
+ lasue_end = compare;
+ lasue_next = LFDS710_LIST_ASU_GET_NEXT( *lasue_end );
+
+ while( lasue_next != NULL )
+ {
+ lasue_end = lasue_next;
+ lasue_next = LFDS710_LIST_ASU_GET_NEXT( *lasue_end );
+ }
+ }
+ }
+
+ lasus->end = lasue;
+
+ LFDS710_BACKOFF_AUTOTUNE( lasus->end_backoff, backoff_iteration );
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+#pragma warning( disable : 4100 )
+
+void lfds710_list_asu_insert_after_element( struct lfds710_list_asu_state *lasus,
+ struct lfds710_list_asu_element *lasue,
+ struct lfds710_list_asu_element
*lasue_predecessor )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ LFDS710_PAL_ASSERT( lasue != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->next %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &lasue->value %
LFDS710_PAL_ALIGN_SINGLE_POINTER == 0 );
+ LFDS710_PAL_ASSERT( lasue_predecessor != NULL );
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ lasue->next = lasue_predecessor->next;
+
+ do
+ {
+ LFDS710_MISC_BARRIER_STORE;
+ LFDS710_PAL_ATOMIC_CAS( &lasue_predecessor->next, (struct
lfds710_list_asu_element **) &lasue->next, lasue, LFDS710_MISC_CAS_STRENGTH_WEAK,
result );
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( lasus->after_backoff, backoff_iteration );
+ }
+ while( result == 0 );
+
+ LFDS710_BACKOFF_AUTOTUNE( lasus->after_backoff, backoff_iteration );
+
+ return;
+}
+
+#pragma warning( default : 4100 )
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_query.c
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_query.c
new file mode 100644
index 0000000..2277422
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_list_addonly_singlylinked_unordered/lfds710_list_addonly_singlylinked_unordered_query.c
@@ -0,0 +1,128 @@
+/***** includes *****/
+#include "lfds710_list_addonly_singlylinked_unordered_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_list_asu_internal_validate( struct lfds710_list_asu_state *lasus,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_list_asu_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_list_asu_query( struct lfds710_list_asu_state *lasus,
+ enum lfds710_list_asu_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( lasus != NULL );
+ // TRD : query_type can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( query_type )
+ {
+ case LFDS710_LIST_ASU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT:
+ {
+ struct lfds710_list_asu_element
+ *lasue = NULL;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ while( LFDS710_LIST_ASU_GET_START_AND_THEN_NEXT(*lasus, lasue) )
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ }
+ break;
+
+ case LFDS710_LIST_ASU_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_list_asu_internal_validate( lasus, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_list_asu_internal_validate( struct lfds710_list_asu_state *lasus,
+ struct lfds710_misc_validation_info *vi,
+ enum lfds710_misc_validity
*lfds710_list_asu_validity )
+{
+ lfds710_pal_uint_t
+ number_elements = 0;
+
+ struct lfds710_list_asu_element
+ *lasue_fast,
+ *lasue_slow;
+
+ LFDS710_PAL_ASSERT( lasus!= NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_list_asu_validity != NULL );
+
+ *lfds710_list_asu_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ lasue_slow = lasue_fast = lasus->start->next;
+
+ /* TRD : first, check for a loop
+ we have two pointers
+ both of which start at the start of the list
+ we enter a loop
+ and on each iteration
+ we advance one pointer by one element
+ and the other by two
+
+ we exit the loop when both pointers are NULL
+ (have reached the end of the queue)
+
+ or
+
+ if we fast pointer 'sees' the slow pointer
+ which means we have a loop
+ */
+
+ if( lasue_slow != NULL )
+ do
+ {
+ lasue_slow = lasue_slow->next;
+
+ if( lasue_fast != NULL )
+ lasue_fast = lasue_fast->next;
+
+ if( lasue_fast != NULL )
+ lasue_fast = lasue_fast->next;
+ }
+ while( lasue_slow != NULL and lasue_fast != lasue_slow );
+
+ if( lasue_fast != NULL and lasue_slow != NULL and lasue_fast == lasue_slow )
+ *lfds710_list_asu_validity = LFDS710_MISC_VALIDITY_INVALID_LOOP;
+
+ /* TRD : now check for expected number of elements
+ vi can be NULL, in which case we do not check
+ we know we don't have a loop from our earlier check
+ */
+
+ if( *lfds710_list_asu_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_list_asu_query( lasus,
LFDS710_LIST_ASU_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL, &number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_list_asu_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_list_asu_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_globals.c
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_globals.c
new file mode 100644
index 0000000..cc80cf7
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_globals.c
@@ -0,0 +1,14 @@
+/***** includes *****/
+#include "lfds710_misc_internal.h"
+
+
+
+
+
+/****************************************************************************/
+struct lfds710_misc_globals
+ lfds710_misc_globals =
+ {
+ { LFDS710_PRNG_SEED }
+ };
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_globals.gcno
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_globals.gcno
new file mode 100644
index 0000000..1ae87fe
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_globals.gcno
@@ -0,0 +1 @@
+oncgR206otB
\ No newline at end of file
diff --git a/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal.h
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal.h
new file mode 100644
index 0000000..6b06f56
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+void lfds710_misc_prng_internal_big_slow_high_quality_init( int long long unsigned seed
);
diff --git
a/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal_backoff_init.c
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal_backoff_init.c
new file mode 100644
index 0000000..7a2256c
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal_backoff_init.c
@@ -0,0 +1,22 @@
+/***** includes *****/
+#include "lfds710_misc_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_misc_internal_backoff_init( struct lfds710_misc_backoff_state *bs )
+{
+ LFDS710_PAL_ASSERT( bs != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &bs->lock %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+
+ bs->lock = LFDS710_MISC_FLAG_LOWERED;
+ bs->backoff_iteration_frequency_counters[0] = 0;
+ bs->backoff_iteration_frequency_counters[1] = 0;
+ bs->metric = 1;
+ bs->total_operations = 0;
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal_backoff_init.gcno
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal_backoff_init.gcno
new file mode 100644
index 0000000..03faf04
Binary files /dev/null and
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_internal_backoff_init.gcno
differ
diff --git a/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_query.c
b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_query.c
new file mode 100644
index 0000000..8009de8
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_misc/lfds710_misc_query.c
@@ -0,0 +1,38 @@
+/***** includes *****/
+#include "lfds710_misc_internal.h"
+
+
+
+
+
+/****************************************************************************/
+#pragma warning( disable : 4100 )
+
+void lfds710_misc_query( enum lfds710_misc_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ // TRD : query type can be any value in its range
+ // TRD : query_input can be NULL in some cases
+ // TRD : query_outputput can be NULL in some cases
+
+ switch( query_type )
+ {
+ case LFDS710_MISC_QUERY_GET_BUILD_AND_VERSION_STRING:
+ {
+ static char const
+ * const build_and_version_string = "liblfds "
LFDS710_MISC_VERSION_STRING " (" BUILD_TYPE_STRING ", "
LFDS710_PAL_OS_STRING ", " MODE_TYPE_STRING ", "
LFDS710_PAL_PROCESSOR_STRING ", " LFDS710_PAL_COMPILER_STRING ")";
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(char const **) query_output = build_and_version_string;
+ }
+ break;
+ }
+
+ return;
+}
+
+#pragma warning( default : 4100 )
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_prng/lfds710_prng_init.c
b/src/libsds/external/liblfds710/src/lfds710_prng/lfds710_prng_init.c
new file mode 100644
index 0000000..aa6ea42
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_prng/lfds710_prng_init.c
@@ -0,0 +1,42 @@
+/***** includes *****/
+#include "lfds710_prng_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_prng_init_valid_on_current_logical_core( struct lfds710_prng_state *ps,
lfds710_pal_uint_t seed )
+{
+ LFDS710_PAL_ASSERT( ps != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &ps->entropy %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ // TRD : seed can be any value in its range (unlike for the mixing function)
+
+ LFDS710_PRNG_ST_MIXING_FUNCTION( seed );
+
+ ps->entropy = seed;
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+void lfds710_prng_st_init( struct lfds710_prng_st_state *psts, lfds710_pal_uint_t seed )
+{
+ LFDS710_PAL_ASSERT( psts != NULL );
+ LFDS710_PAL_ASSERT( seed != 0 );
+
+ LFDS710_PRNG_ST_MIXING_FUNCTION( seed );
+
+ psts->entropy = seed;
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_prng/lfds710_prng_internal.h
b/src/libsds/external/liblfds710/src/lfds710_prng/lfds710_prng_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_prng/lfds710_prng_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c
new file mode 100644
index 0000000..ff9cea1
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_cleanup.c
@@ -0,0 +1,27 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_bmm_cleanup( struct lfds710_queue_bmm_state *qbmms,
+ void (*element_cleanup_callback)(struct
lfds710_queue_bmm_state *qbmms, void *key, void *value) )
+{
+ void
+ *key,
+ *value;
+
+ LFDS710_PAL_ASSERT( qbmms != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( element_cleanup_callback != NULL )
+ while( lfds710_queue_bmm_dequeue(qbmms,&key,&value) )
+ element_cleanup_callback( qbmms, key, value );
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c
new file mode 100644
index 0000000..1e9486e
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_dequeue.c
@@ -0,0 +1,86 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_queue_bmm_dequeue( struct lfds710_queue_bmm_state *qbmms,
+ void **key,
+ void **value )
+{
+ char unsigned
+ result;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ int
+ rv = 1;
+
+ lfds710_pal_uint_t
+ read_index,
+ sequence_number;
+
+ lfds710_pal_int_t
+ difference;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_queue_bmm_element
+ *qbmme = NULL;
+
+ LFDS710_PAL_ASSERT( qbmms != NULL );
+ // TRD : key can be NULL
+ // TRD : value can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ read_index = qbmms->read_index;
+
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ qbmme = &qbmms->element_array[ read_index & qbmms->mask ];
+ LFDS710_MISC_BARRIER_LOAD;
+ sequence_number = qbmme->sequence_number;
+ difference = (lfds710_pal_int_t) sequence_number - (lfds710_pal_int_t) (read_index +
1);
+
+ if( difference == 0 )
+ {
+ LFDS710_PAL_ATOMIC_CAS( &qbmms->read_index, &read_index, read_index + 1,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( qbmms->dequeue_backoff, backoff_iteration
);
+ if( result == 1 )
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( difference < 0 )
+ {
+ rv = 0;
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( difference > 0 )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ read_index = qbmms->read_index;
+ }
+ }
+
+ if( rv == 1 )
+ {
+ if( key != NULL )
+ *key = qbmme->key;
+ if( value != NULL )
+ *value = qbmme->value;
+ LFDS710_MISC_BARRIER_STORE;
+ qbmme->sequence_number = read_index + qbmms->mask + 1;
+ }
+
+ LFDS710_BACKOFF_AUTOTUNE( qbmms->dequeue_backoff, backoff_iteration );
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c
new file mode 100644
index 0000000..d666c8d
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_enqueue.c
@@ -0,0 +1,84 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_queue_bmm_enqueue( struct lfds710_queue_bmm_state *qbmms,
+ void *key,
+ void *value )
+{
+ char unsigned
+ result;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ int
+ rv = 1;
+
+ lfds710_pal_uint_t
+ sequence_number,
+ write_index;
+
+ lfds710_pal_int_t
+ difference;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_queue_bmm_element
+ *qbmme = NULL;
+
+ LFDS710_PAL_ASSERT( qbmms != NULL );
+ // TRD : key can be NULL
+ // TRD : value can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ write_index = qbmms->write_index;
+
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED )
+ {
+ qbmme = &qbmms->element_array[ write_index & qbmms->mask ];
+ LFDS710_MISC_BARRIER_LOAD;
+ sequence_number = qbmme->sequence_number;
+ difference = (lfds710_pal_int_t) sequence_number - (lfds710_pal_int_t) write_index;
+
+ if( difference == 0 )
+ {
+ LFDS710_PAL_ATOMIC_CAS( &qbmms->write_index, &write_index, write_index +
1, LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( qbmms->enqueue_backoff, backoff_iteration
);
+ if( result == 1 )
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( difference < 0 )
+ {
+ rv = 0;
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+
+ if( difference > 0 )
+ {
+ LFDS710_MISC_BARRIER_LOAD;
+ write_index = qbmms->write_index;
+ }
+ }
+
+ if( rv == 1 )
+ {
+ qbmme->key = key;
+ qbmme->value = value;
+ LFDS710_MISC_BARRIER_STORE;
+ qbmme->sequence_number = write_index + 1;
+ }
+
+ LFDS710_BACKOFF_AUTOTUNE( qbmms->enqueue_backoff, backoff_iteration );
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_init.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_init.c
new file mode 100644
index 0000000..76743e5
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_init.c
@@ -0,0 +1,42 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_bmm_init_valid_on_current_logical_core( struct lfds710_queue_bmm_state
*qbmms,
+ struct
lfds710_queue_bmm_element *element_array,
+ lfds710_pal_uint_t
number_elements,
+ void *user_state )
+{
+ lfds710_pal_uint_t
+ loop;
+
+ LFDS710_PAL_ASSERT( qbmms != NULL );
+ LFDS710_PAL_ASSERT( element_array != NULL );
+ LFDS710_PAL_ASSERT( number_elements >= 2 );
+ LFDS710_PAL_ASSERT( ( number_elements & (number_elements-1) ) == 0 ); // TRD :
number_elements must be a positive integer power of 2
+ // TRD : user_state can be NULL
+
+ qbmms->number_elements = number_elements;
+ qbmms->mask = qbmms->number_elements - 1;
+ qbmms->read_index = 0;
+ qbmms->write_index = 0;
+ qbmms->element_array = element_array;
+ qbmms->user_state = user_state;
+
+ for( loop = 0 ; loop < qbmms->number_elements ; loop++ )
+ qbmms->element_array[loop].sequence_number = loop;
+
+ lfds710_misc_internal_backoff_init( &qbmms->dequeue_backoff );
+ lfds710_misc_internal_backoff_init( &qbmms->enqueue_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_query.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_query.c
new file mode 100644
index 0000000..792a44b
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_manyproducer_manyconsumer/lfds710_queue_bounded_manyproducer_manyconsumer_query.c
@@ -0,0 +1,129 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_manyproducer_manyconsumer_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_queue_bmm_internal_validate( struct lfds710_queue_bmm_state *qbmms,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_bmm_query( struct lfds710_queue_bmm_state *qbmms,
+ enum lfds710_queue_bmm_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( qbmms != NULL );
+ // TRD : query_type can be any value in its range
+
+ switch( query_type )
+ {
+ case LFDS710_QUEUE_BMM_QUERY_GET_POTENTIALLY_INACCURATE_COUNT:
+ {
+ lfds710_pal_uint_t
+ local_read_index,
+ local_write_index;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ local_read_index = qbmms->read_index;
+ local_write_index = qbmms->write_index;
+
+ *(lfds710_pal_uint_t *) query_output = +( local_write_index - local_read_index );
+
+ if( local_read_index > local_write_index )
+ *(lfds710_pal_uint_t *) query_output = ((lfds710_pal_uint_t) -1) -
*(lfds710_pal_uint_t *) query_output;
+ }
+ break;
+
+ case LFDS710_QUEUE_BMM_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_queue_bmm_internal_validate( qbmms, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_queue_bmm_internal_validate( struct lfds710_queue_bmm_state *qbmms,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_validity )
+{
+ lfds710_pal_uint_t
+ expected_sequence_number,
+ loop,
+ number_elements,
+ sequence_number;
+
+ LFDS710_PAL_ASSERT( qbmms != NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_validity != NULL );
+
+ *lfds710_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ /* TRD : starting from the read_index, we should find number_elements of incrementing
sequence numbers
+ we then perform a second scan from the write_index onwards, which should have
(max elements in queue - number_elements) incrementing sequence numbers
+ */
+
+ lfds710_queue_bmm_query( qbmms,
LFDS710_QUEUE_BMM_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL, (void *)
&number_elements );
+
+ expected_sequence_number = qbmms->element_array[ qbmms->read_index &
qbmms->mask ].sequence_number;
+
+ for( loop = 0 ; loop < number_elements ; loop++ )
+ {
+ sequence_number = qbmms->element_array[ (qbmms->read_index + loop) &
qbmms->mask ].sequence_number;
+
+ if( sequence_number != expected_sequence_number )
+ *lfds710_validity = LFDS710_MISC_VALIDITY_INVALID_ORDER;
+
+ if( sequence_number == expected_sequence_number )
+ expected_sequence_number = sequence_number + 1;
+ }
+
+ // TRD : now the write_index onwards
+
+ expected_sequence_number = qbmms->element_array[ qbmms->write_index &
qbmms->mask ].sequence_number;
+
+ for( loop = 0 ; loop < qbmms->number_elements - number_elements ; loop++ )
+ {
+ sequence_number = qbmms->element_array[ (qbmms->write_index + loop) &
qbmms->mask ].sequence_number;
+
+ if( sequence_number != expected_sequence_number )
+ *lfds710_validity = LFDS710_MISC_VALIDITY_INVALID_ORDER;
+
+ if( sequence_number == expected_sequence_number )
+ expected_sequence_number = sequence_number + 1;
+ }
+
+ // TRD : now check against the expected number of elements
+
+ if( *lfds710_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_pal_uint_t
+ number_elements;
+
+ lfds710_queue_bmm_query( qbmms,
LFDS710_QUEUE_BMM_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL, (void *)
&number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c
new file mode 100644
index 0000000..0531038
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_cleanup.c
@@ -0,0 +1,30 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_singleproducer_singleconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_bss_cleanup( struct lfds710_queue_bss_state *qbsss,
+ void (*element_cleanup_callback)(struct
lfds710_queue_bss_state *qbsss, void *key, void *value) )
+{
+ lfds710_pal_uint_t
+ loop;
+
+ struct lfds710_queue_bss_element
+ *qbsse;
+
+ LFDS710_PAL_ASSERT( qbsss != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ if( element_cleanup_callback != NULL )
+ for( loop = qbsss->read_index ; loop < qbsss->read_index +
qbsss->number_elements ; loop++ )
+ {
+ qbsse = qbsss->element_array + (loop % qbsss->number_elements);
+ element_cleanup_callback( qbsss, qbsse->key, qbsse->value );
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c
new file mode 100644
index 0000000..2c5174e
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_dequeue.c
@@ -0,0 +1,41 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_singleproducer_singleconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_queue_bss_dequeue( struct lfds710_queue_bss_state *qbsss,
+ void **key,
+ void **value )
+{
+ struct lfds710_queue_bss_element
+ *qbsse;
+
+ LFDS710_PAL_ASSERT( qbsss != NULL );
+ // TRD : key can be NULL
+ // TRD : value can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( qbsss->read_index != qbsss->write_index )
+ {
+ qbsse = qbsss->element_array + qbsss->read_index;
+
+ if( key != NULL )
+ *key = qbsse->key;
+
+ if( value != NULL )
+ *value = qbsse->value;
+
+ qbsss->read_index = (qbsss->read_index + 1) & qbsss->mask;
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c
new file mode 100644
index 0000000..aaaae41
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_enqueue.c
@@ -0,0 +1,38 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_singleproducer_singleconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_queue_bss_enqueue( struct lfds710_queue_bss_state *qbsss,
+ void *key,
+ void *value )
+{
+ struct lfds710_queue_bss_element
+ *qbsse;
+
+ LFDS710_PAL_ASSERT( qbsss != NULL );
+ // TRD : key can be NULL
+ // TRD : value can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( ( (qbsss->write_index+1) & qbsss->mask ) != qbsss->read_index )
+ {
+ qbsse = qbsss->element_array + qbsss->write_index;
+
+ qbsse->key = key;
+ qbsse->value = value;
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ qbsss->write_index = (qbsss->write_index + 1) & qbsss->mask;
+
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_init.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_init.c
new file mode 100644
index 0000000..276d0a7
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_init.c
@@ -0,0 +1,63 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_singleproducer_singleconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_bss_init_valid_on_current_logical_core( struct lfds710_queue_bss_state
*qbsss,
+ struct
lfds710_queue_bss_element *element_array,
+ lfds710_pal_uint_t
number_elements,
+ void *user_state )
+{
+ LFDS710_PAL_ASSERT( qbsss != NULL );
+ LFDS710_PAL_ASSERT( element_array != NULL );
+ LFDS710_PAL_ASSERT( number_elements >= 2 );
+ LFDS710_PAL_ASSERT( ( number_elements & (number_elements-1) ) == 0 ); // TRD :
number_elements must be a positive integer power of 2
+ // TRD : user_state can be NULL
+
+ /* TRD : the use of mask and the restriction on a power of two
+ upon the number of elements bears some remark
+
+ in this queue, there are a fixed number of elements
+ we have a read index and a write index
+ when we write, and thre is space to write, we increment the write index
+ (if no space to write, we just return)
+ when we read, and there are elements to be read, we after reading increment
the read index
+ (if no elements to read, we just return)
+ the problem is - how do we handle wrap around?
+ e.g. when I write, but my write index is now equal to the number of elements
+ the usual solution is to modulus the write index by the nunmber of elements
+ problem is modulus is slow
+ there is a better way
+ first, we restrict the number of elements to be a power of two
+ so imagine we have a 64-bit system and we set the number of elements to be
2^64
+ this gives us a bit pattern of 1000 0000 0000 0000 (...etc, lots of zeros)
+ now (just roll with this for a bit) subtract one from this
+ this gives us a mask (on a two's compliment machine)
+ 0111 1111 1111 1111 (...etc, lots of ones)
+ so what we do now, when we increment an index (think of the write index as the
example)
+ we bitwise and it with the mask
+ now think about thwt happens
+ all the numbers up to 2^64 will be unchanged - their MSB is never set, and we
and with all the other bits
+ but when we finally hit 2^64 and need to roll over... bingo!
+ we drop MSB (which we finally have) and have the value 0!
+ this is exactly what we want
+ bitwise and is much faster than modulus
+ */
+
+ qbsss->number_elements = number_elements;
+ qbsss->mask = qbsss->number_elements - 1;
+ qbsss->read_index = 0;
+ qbsss->write_index = 0;
+ qbsss->element_array = element_array;
+ qbsss->user_state = user_state;
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_query.c
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_query.c
new file mode 100644
index 0000000..741834c
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_bounded_singleproducer_singleconsumer/lfds710_queue_bounded_singleproducer_singleconsumer_query.c
@@ -0,0 +1,87 @@
+/***** includes *****/
+#include "lfds710_queue_bounded_singleproducer_singleconsumer_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_queue_bss_internal_validate( struct lfds710_queue_bss_state *qbsss,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_bss_query( struct lfds710_queue_bss_state *qbsss,
+ enum lfds710_queue_bss_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( qbsss != NULL );
+ // TRD : query_type can be any value in its range
+
+ switch( query_type )
+ {
+ case LFDS710_QUEUE_BSS_QUERY_GET_POTENTIALLY_INACCURATE_COUNT:
+ {
+ lfds710_pal_uint_t
+ local_read_index,
+ local_write_index;
+
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ local_read_index = qbsss->read_index;
+ local_write_index = qbsss->write_index;
+
+ *(lfds710_pal_uint_t *) query_output = +( local_write_index - local_read_index );
+
+ if( local_read_index > local_write_index )
+ *(lfds710_pal_uint_t *) query_output = qbsss->number_elements -
*(lfds710_pal_uint_t *) query_output;
+ }
+ break;
+
+ case LFDS710_QUEUE_BSS_QUERY_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_queue_bss_internal_validate( qbsss, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_queue_bss_internal_validate( struct lfds710_queue_bss_state *qbsss,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_validity )
+{
+ LFDS710_PAL_ASSERT( qbsss != NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_validity != NULL );
+
+ *lfds710_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ if( vi != NULL )
+ {
+ lfds710_pal_uint_t
+ number_elements;
+
+ lfds710_queue_bss_query( qbsss,
LFDS710_QUEUE_BSS_QUERY_GET_POTENTIALLY_INACCURATE_COUNT, NULL, (void *)
&number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c
new file mode 100644
index 0000000..93dd24f
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.c
@@ -0,0 +1,48 @@
+/***** includes *****/
+#include "lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_umm_cleanup( struct lfds710_queue_umm_state *qumms,
+ void (*element_cleanup_callback)(struct
lfds710_queue_umm_state *qumms, struct lfds710_queue_umm_element *qumme, enum
lfds710_misc_flag dummy_element_flag) )
+{
+ struct lfds710_queue_umm_element
+ *qumme;
+
+ void
+ *value;
+
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( element_cleanup_callback != NULL )
+ {
+ while( qumms->dequeue[POINTER] != qumms->enqueue[POINTER] )
+ {
+ // TRD : trailing dummy element, so the first real value is in the next element
+ value = qumms->dequeue[POINTER]->next[POINTER]->value;
+
+ // TRD : user is given back *an* element, but not the one his user data was in
+ qumme = qumms->dequeue[POINTER];
+
+ // TRD : remove the element from queue
+ qumms->dequeue[POINTER] = qumms->dequeue[POINTER]->next[POINTER];
+
+ // TRD : write value into the qumme we're going to give the user
+ qumme->value = value;
+
+ element_cleanup_callback( qumms, qumme, LFDS710_MISC_FLAG_LOWERED );
+ }
+
+ // TRD : and now the final element
+ element_cleanup_callback( qumms, (struct lfds710_queue_umm_element *)
qumms->dequeue[POINTER], LFDS710_MISC_FLAG_RAISED );
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.gcno
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.gcno
new file mode 100644
index 0000000..cf0923d
Binary files /dev/null and
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_cleanup.gcno
differ
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c
new file mode 100644
index 0000000..5728967
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.c
@@ -0,0 +1,128 @@
+/***** includes *****/
+#include "lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_queue_umm_dequeue( struct lfds710_queue_umm_state *qumms,
+ struct lfds710_queue_umm_element **qumme )
+{
+ char unsigned
+ result = 0;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ enum lfds710_queue_umm_queue_state
+ state = LFDS710_QUEUE_UMM_QUEUE_STATE_UNKNOWN;
+
+ int
+ rv = 1;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_queue_umm_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *dequeue[PAC_SIZE],
+ *enqueue[PAC_SIZE],
+ *next[PAC_SIZE];
+
+ void
+ *key = NULL,
+ *value = NULL;
+
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ LFDS710_PAL_ASSERT( qumme != NULL );
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ do
+ {
+ /* TRD : note here the deviation from the white paper
+ in the white paper, next is loaded from dequeue, not from qumms->dequeue
+ what concerns me is that between the load of dequeue and the load of
+ enqueue->next, the element can be dequeued by another thread *and freed*
+
+ by ordering the loads (load barriers), and loading both from qumms,
+ the following if(), which checks dequeue is still the same as
qumms->enqueue
+ still continues to ensure next belongs to enqueue, while avoiding the
+ problem with free
+ */
+
+ dequeue[COUNTER] = qumms->dequeue[COUNTER];
+ dequeue[POINTER] = qumms->dequeue[POINTER];
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ enqueue[COUNTER] = qumms->enqueue[COUNTER];
+ enqueue[POINTER] = qumms->enqueue[POINTER];
+
+ next[COUNTER] = qumms->dequeue[POINTER]->next[COUNTER];
+ next[POINTER] = qumms->dequeue[POINTER]->next[POINTER];
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( qumms->dequeue[COUNTER] == dequeue[COUNTER] and qumms->dequeue[POINTER] ==
dequeue[POINTER] )
+ {
+ if( enqueue[POINTER] == dequeue[POINTER] and next[POINTER] == NULL )
+ state = LFDS710_QUEUE_UMM_QUEUE_STATE_EMPTY;
+
+ if( enqueue[POINTER] == dequeue[POINTER] and next[POINTER] != NULL )
+ state = LFDS710_QUEUE_UMM_QUEUE_STATE_ENQUEUE_OUT_OF_PLACE;
+
+ if( enqueue[POINTER] != dequeue[POINTER] )
+ state = LFDS710_QUEUE_UMM_QUEUE_STATE_ATTEMPT_DEQUEUE;
+
+ switch( state )
+ {
+ case LFDS710_QUEUE_UMM_QUEUE_STATE_UNKNOWN:
+ // TRD : eliminates compiler warning
+ break;
+
+ case LFDS710_QUEUE_UMM_QUEUE_STATE_EMPTY:
+ rv = 0;
+ *qumme = NULL;
+ result = 1;
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ break;
+
+ case LFDS710_QUEUE_UMM_QUEUE_STATE_ENQUEUE_OUT_OF_PLACE:
+ next[COUNTER] = enqueue[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( qumms->enqueue, enqueue, next,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+ // TRD : in fact if result is 1 (successful) I think we can now simply drop
down into the dequeue attempt
+ break;
+
+ case LFDS710_QUEUE_UMM_QUEUE_STATE_ATTEMPT_DEQUEUE:
+ key = next[POINTER]->key;
+ value = next[POINTER]->value;
+
+ next[COUNTER] = dequeue[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( qumms->dequeue, dequeue, next,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 1 )
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ break;
+ }
+ }
+ else
+ result = 0;
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( qumms->dequeue_backoff, backoff_iteration
);
+ }
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED );
+
+ if( result == 1 )
+ {
+ *qumme = dequeue[POINTER];
+ (*qumme)->key = key;
+ (*qumme)->value = value;
+ }
+
+ LFDS710_BACKOFF_AUTOTUNE( qumms->dequeue_backoff, backoff_iteration );
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.gcno
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.gcno
new file mode 100644
index 0000000..6d7b344
Binary files /dev/null and
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_dequeue.gcno
differ
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c
new file mode 100644
index 0000000..cd65a57
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.c
@@ -0,0 +1,96 @@
+/***** includes *****/
+#include "lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_umm_enqueue( struct lfds710_queue_umm_state *qumms,
+ struct lfds710_queue_umm_element *qumme )
+{
+ char unsigned
+ result = 0;
+
+ enum lfds710_misc_flag
+ finished_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_queue_umm_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *volatile enqueue[PAC_SIZE],
+ *new_enqueue[PAC_SIZE],
+ *volatile next[PAC_SIZE];
+
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ LFDS710_PAL_ASSERT( qumme != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) qumme->next %
LFDS710_PAL_ALIGN_DOUBLE_POINTER == 0 );
+
+ qumme->next[POINTER] = NULL;
+ LFDS710_PAL_ATOMIC_ADD( &qumms->aba_counter, 1, qumme->next[COUNTER], struct
lfds710_queue_umm_element * );
+ LFDS710_MISC_BARRIER_STORE;
+
+ new_enqueue[POINTER] = qumme;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ do
+ {
+ /* TRD : note here the deviation from the white paper
+ in the white paper, next is loaded from enqueue, not from qumms->enqueue
+ what concerns me is that between the load of enqueue and the load of
+ enqueue->next, the element can be dequeued by another thread *and freed*
+
+ by ordering the loads (load barriers), and loading both from qumms,
+ the following if(), which checks enqueue is still the same as
qumms->enqueue
+ still continues to ensure next belongs to enqueue, while avoiding the
+ problem with free
+ */
+
+ enqueue[COUNTER] = qumms->enqueue[COUNTER];
+ enqueue[POINTER] = qumms->enqueue[POINTER];
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ next[COUNTER] = qumms->enqueue[POINTER]->next[COUNTER];
+ next[POINTER] = qumms->enqueue[POINTER]->next[POINTER];
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( qumms->enqueue[COUNTER] == enqueue[COUNTER] and qumms->enqueue[POINTER] ==
enqueue[POINTER] )
+ {
+ if( next[POINTER] == NULL )
+ {
+ new_enqueue[COUNTER] = next[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( enqueue[POINTER]->next, next, new_enqueue,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+ if( result == 1 )
+ finished_flag = LFDS710_MISC_FLAG_RAISED;
+ }
+ else
+ {
+ next[COUNTER] = enqueue[COUNTER] + 1;
+ // TRD : strictly, this is a weak CAS, but we do an extra iteration of the main
loop on a fake failure, so we set it to be strong
+ LFDS710_PAL_ATOMIC_DWCAS( qumms->enqueue, enqueue, next,
LFDS710_MISC_CAS_STRENGTH_STRONG, result );
+ }
+ }
+ else
+ result = 0;
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( qumms->enqueue_backoff, backoff_iteration
);
+ }
+ while( finished_flag == LFDS710_MISC_FLAG_LOWERED );
+
+ // TRD : move enqueue along; only a weak CAS as the dequeue will solve this if it's
out of place
+ new_enqueue[COUNTER] = enqueue[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( qumms->enqueue, enqueue, new_enqueue,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( qumms->enqueue_backoff, backoff_iteration );
+
+ LFDS710_BACKOFF_AUTOTUNE( qumms->enqueue_backoff, backoff_iteration );
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.gcno
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.gcno
new file mode 100644
index 0000000..208a9aa
Binary files /dev/null and
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_enqueue.gcno
differ
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.c
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.c
new file mode 100644
index 0000000..8cfc0d8
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.c
@@ -0,0 +1,50 @@
+/***** includes *****/
+#include "lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_umm_init_valid_on_current_logical_core( struct lfds710_queue_umm_state
*qumms,
+ struct
lfds710_queue_umm_element *qumme_dummy,
+ void *user_state )
+{
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &qumms->enqueue %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &qumms->dequeue %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &qumms->user_state %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( qumme_dummy != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) qumme_dummy->next %
LFDS710_PAL_ALIGN_DOUBLE_POINTER == 0 );
+ // TRD : user_state can be NULL
+
+ /* TRD : qumme_dummy is a dummy element, needed for init
+ the qumms->enqueue and qumms->dequeue counters do not need to be
initialized
+ but it does no harm to do so, and stops a valgrind complaint
+ */
+
+ LFDS710_PRNG_GENERATE( lfds710_misc_globals.ps, qumms->aba_counter );
+
+ qumms->enqueue[POINTER] = qumme_dummy;
+ qumms->enqueue[COUNTER] = (struct lfds710_queue_umm_element *) 0;
+ qumms->dequeue[POINTER] = qumme_dummy;
+ qumms->dequeue[COUNTER] = (struct lfds710_queue_umm_element *) 0;
+
+ qumme_dummy->next[POINTER] = NULL;
+ // TRD : no need here for an atomic add as we have a store barrier and force store
below
+ qumme_dummy->next[COUNTER] = (struct lfds710_queue_umm_element *)
qumms->aba_counter++;
+ qumme_dummy->key = NULL;
+ qumme_dummy->value = NULL;
+
+ qumms->user_state = user_state;
+
+ lfds710_misc_internal_backoff_init( &qumms->dequeue_backoff );
+ lfds710_misc_internal_backoff_init( &qumms->enqueue_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.gcno
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.gcno
new file mode 100644
index 0000000..ca82c7c
Binary files /dev/null and
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_init.gcno
differ
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
new file mode 100644
index 0000000..c38f6d5
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h
@@ -0,0 +1,14 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** enums *****/
+enum lfds710_queue_umm_queue_state
+{
+ LFDS710_QUEUE_UMM_QUEUE_STATE_UNKNOWN,
+ LFDS710_QUEUE_UMM_QUEUE_STATE_EMPTY,
+ LFDS710_QUEUE_UMM_QUEUE_STATE_ENQUEUE_OUT_OF_PLACE,
+ LFDS710_QUEUE_UMM_QUEUE_STATE_ATTEMPT_DEQUEUE
+};
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.c
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.c
new file mode 100644
index 0000000..1ae9ee1
--- /dev/null
+++
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.c
@@ -0,0 +1,133 @@
+/***** includes *****/
+#include "lfds710_queue_unbounded_manyproducer_manyconsumer_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_queue_umm_internal_validate( struct lfds710_queue_umm_state *qumms,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_queue_umm_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_queue_umm_query( struct lfds710_queue_umm_state *qumms,
+ enum lfds710_queue_umm_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ struct lfds710_queue_umm_element
+ *qumme;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ // TRD : query_type can be any value in its range
+
+ switch( query_type )
+ {
+ case LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_GET_COUNT:
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ qumme = (struct lfds710_queue_umm_element *) qumms->dequeue[POINTER];
+
+ while( qumme != NULL )
+ {
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ qumme = (struct lfds710_queue_umm_element *) qumme->next[POINTER];
+ }
+
+ // TRD : remember there is a dummy element in the queue
+ ( *(lfds710_pal_uint_t *) query_output )--;
+ break;
+
+ case LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_queue_umm_internal_validate( qumms, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_queue_umm_internal_validate( struct lfds710_queue_umm_state *qumms,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_queue_umm_validity )
+{
+ lfds710_pal_uint_t
+ number_elements = 0;
+
+ struct lfds710_queue_umm_element
+ *qumme_fast,
+ *qumme_slow;
+
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_queue_umm_validity != NULL );
+
+ *lfds710_queue_umm_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ qumme_slow = qumme_fast = (struct lfds710_queue_umm_element *)
qumms->dequeue[POINTER];
+
+ /* TRD : first, check for a loop
+ we have two pointers
+ both of which start at the dequeue end of the queue
+ we enter a loop
+ and on each iteration
+ we advance one pointer by one element
+ and the other by two
+
+ we exit the loop when both pointers are NULL
+ (have reached the end of the queue)
+
+ or
+
+ if we fast pointer 'sees' the slow pointer
+ which means we have a loop
+ */
+
+ if( qumme_slow != NULL )
+ do
+ {
+ qumme_slow = qumme_slow->next[POINTER];
+
+ if( qumme_fast != NULL )
+ qumme_fast = qumme_fast->next[POINTER];
+
+ if( qumme_fast != NULL )
+ qumme_fast = qumme_fast->next[POINTER];
+ }
+ while( qumme_slow != NULL and qumme_fast != qumme_slow );
+
+ if( qumme_fast != NULL and qumme_slow != NULL and qumme_fast == qumme_slow )
+ *lfds710_queue_umm_validity = LFDS710_MISC_VALIDITY_INVALID_LOOP;
+
+ /* TRD : now check for expected number of elements
+ vi can be NULL, in which case we do not check
+ we know we don't have a loop from our earlier check
+ */
+
+ if( *lfds710_queue_umm_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_queue_umm_query( qumms, LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_GET_COUNT,
NULL, (void *) &number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_queue_umm_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_queue_umm_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.gcno
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.gcno
new file mode 100644
index 0000000..92e91bf
Binary files /dev/null and
b/src/libsds/external/liblfds710/src/lfds710_queue_unbounded_manyproducer_manyconsumer/lfds710_queue_unbounded_manyproducer_manyconsumer_query.gcno
differ
diff --git
a/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_cleanup.c
new file mode 100644
index 0000000..6ec6f57
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_cleanup.c
@@ -0,0 +1,92 @@
+/***** includes *****/
+#include "lfds710_ringbuffer_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_ringbuffer_internal_queue_umm_element_cleanup_callback( struct
lfds710_queue_umm_state *qumms,
+ struct
lfds710_queue_umm_element *qumme,
+ enum
lfds710_misc_flag dummy_element_flag );
+static void lfds710_ringbuffer_internal_freelist_element_cleanup_callback( struct
lfds710_freelist_state *fs,
+ struct
lfds710_freelist_element *fe );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_ringbuffer_cleanup( struct lfds710_ringbuffer_state *rs,
+ void (*element_cleanup_callback)(struct
lfds710_ringbuffer_state *rs, void *key, void *value, enum lfds710_misc_flag unread_flag)
)
+{
+ LFDS710_PAL_ASSERT( rs != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ if( element_cleanup_callback != NULL )
+ {
+ rs->element_cleanup_callback = element_cleanup_callback;
+ lfds710_queue_umm_cleanup( &rs->qumms,
lfds710_ringbuffer_internal_queue_umm_element_cleanup_callback );
+ lfds710_freelist_cleanup( &rs->fs,
lfds710_ringbuffer_internal_freelist_element_cleanup_callback );
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+#pragma warning( disable : 4100 )
+
+static void lfds710_ringbuffer_internal_queue_umm_element_cleanup_callback( struct
lfds710_queue_umm_state *qumms,
+ struct
lfds710_queue_umm_element *qumme,
+ enum
lfds710_misc_flag dummy_element_flag )
+{
+ struct lfds710_ringbuffer_element
+ *re;
+
+ struct lfds710_ringbuffer_state
+ *rs;
+
+ LFDS710_PAL_ASSERT( qumms != NULL );
+ LFDS710_PAL_ASSERT( qumme != NULL );
+ // TRD : dummy_element can be any value in its range
+
+ rs = (struct lfds710_ringbuffer_state *) LFDS710_QUEUE_UMM_GET_USER_STATE_FROM_STATE(
*qumms );
+ re = (struct lfds710_ringbuffer_element *) LFDS710_QUEUE_UMM_GET_VALUE_FROM_ELEMENT(
*qumme );
+
+ if( dummy_element_flag == LFDS710_MISC_FLAG_LOWERED )
+ rs->element_cleanup_callback( rs, re->key, re->value,
LFDS710_MISC_FLAG_RAISED );
+
+ return;
+}
+
+#pragma warning( default : 4100 )
+
+
+
+
+
+/****************************************************************************/
+#pragma warning( disable : 4100 )
+
+static void lfds710_ringbuffer_internal_freelist_element_cleanup_callback( struct
lfds710_freelist_state *fs,
+ struct
lfds710_freelist_element *fe )
+{
+ struct lfds710_ringbuffer_element
+ *re;
+
+ struct lfds710_ringbuffer_state
+ *rs;
+
+ LFDS710_PAL_ASSERT( fs != NULL );
+ LFDS710_PAL_ASSERT( fe != NULL );
+
+ rs = (struct lfds710_ringbuffer_state *) LFDS710_FREELIST_GET_USER_STATE_FROM_STATE(
*fs );
+ re = (struct lfds710_ringbuffer_element *) LFDS710_FREELIST_GET_VALUE_FROM_ELEMENT( *fe
);
+
+ rs->element_cleanup_callback( rs, re->key, re->value,
LFDS710_MISC_FLAG_LOWERED );
+
+ return;
+}
+
+#pragma warning( default : 4100 )
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_init.c
b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_init.c
new file mode 100644
index 0000000..a0a1be7
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_init.c
@@ -0,0 +1,42 @@
+/***** includes *****/
+#include "lfds710_ringbuffer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_ringbuffer_init_valid_on_current_logical_core( struct
lfds710_ringbuffer_state *rs,
+ struct
lfds710_ringbuffer_element *re_array_inc_dummy,
+ lfds710_pal_uint_t
number_elements_inc_dummy,
+ void *user_state )
+{
+ lfds710_pal_uint_t
+ loop;
+
+ LFDS710_PAL_ASSERT( rs != NULL );
+ LFDS710_PAL_ASSERT( re_array_inc_dummy != NULL );
+ LFDS710_PAL_ASSERT( number_elements_inc_dummy >= 2 );
+ // TRD : user_state can be NULL
+
+ rs->user_state = user_state;
+
+ re_array_inc_dummy[0].qumme_use = &re_array_inc_dummy[0].qumme;
+
+ lfds710_freelist_init_valid_on_current_logical_core( &rs->fs, NULL, 0, rs );
+ lfds710_queue_umm_init_valid_on_current_logical_core( &rs->qumms,
&re_array_inc_dummy[0].qumme, rs );
+
+ for( loop = 1 ; loop < number_elements_inc_dummy ; loop++ )
+ {
+ re_array_inc_dummy[loop].qumme_use = &re_array_inc_dummy[loop].qumme;
+ LFDS710_FREELIST_SET_VALUE_IN_ELEMENT( re_array_inc_dummy[loop].fe,
&re_array_inc_dummy[loop] );
+ lfds710_freelist_push( &rs->fs, &re_array_inc_dummy[loop].fe, NULL );
+ }
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h
b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_query.c
b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_query.c
new file mode 100644
index 0000000..4da1624
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_query.c
@@ -0,0 +1,81 @@
+/***** includes *****/
+#include "lfds710_ringbuffer_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_ringbuffer_internal_validate( struct lfds710_ringbuffer_state *rs,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_queue_umm_validity,
+ enum lfds710_misc_validity
*lfds710_freelist_validity );
+
+
+
+/****************************************************************************/
+void lfds710_ringbuffer_query( struct lfds710_ringbuffer_state *rs,
+ enum lfds710_ringbuffer_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ LFDS710_PAL_ASSERT( rs != NULL );
+ // TRD : query_type can be any value in its range
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ switch( query_type )
+ {
+ case LFDS710_RINGBUFFER_QUERY_SINGLETHREADED_GET_COUNT:
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_queue_umm_query( &rs->qumms,
LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_GET_COUNT, NULL, query_output );
+ break;
+
+ case LFDS710_RINGBUFFER_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_ringbuffer_internal_validate( rs, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output, ((enum lfds710_misc_validity *)
query_output)+1 );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_ringbuffer_internal_validate( struct lfds710_ringbuffer_state *rs,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_queue_umm_validity,
+ enum lfds710_misc_validity
*lfds710_freelist_validity )
+{
+ LFDS710_PAL_ASSERT( rs != NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_queue_umm_validity != NULL );
+ LFDS710_PAL_ASSERT( lfds710_freelist_validity != NULL );
+
+ if( vi == NULL )
+ {
+ lfds710_queue_umm_query( &rs->qumms,
LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_VALIDATE, NULL, lfds710_queue_umm_validity );
+ lfds710_freelist_query( &rs->fs,
LFDS710_FREELIST_QUERY_SINGLETHREADED_VALIDATE, NULL, lfds710_freelist_validity );
+ }
+
+ if( vi != NULL )
+ {
+ struct lfds710_misc_validation_info
+ freelist_vi,
+ queue_vi;
+
+ queue_vi.min_elements = 0;
+ freelist_vi.min_elements = 0;
+ queue_vi.max_elements = vi->max_elements;
+ freelist_vi.max_elements = vi->max_elements;
+
+ lfds710_queue_umm_query( &rs->qumms,
LFDS710_QUEUE_UMM_QUERY_SINGLETHREADED_VALIDATE, &queue_vi, lfds710_queue_umm_validity
);
+ lfds710_freelist_query( &rs->fs,
LFDS710_FREELIST_QUERY_SINGLETHREADED_VALIDATE, &freelist_vi,
lfds710_freelist_validity );
+ }
+
+ return;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_read.c
b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_read.c
new file mode 100644
index 0000000..3bf739e
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_read.c
@@ -0,0 +1,43 @@
+/***** includes *****/
+#include "lfds710_ringbuffer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_ringbuffer_read( struct lfds710_ringbuffer_state *rs,
+ void **key,
+ void **value )
+{
+ int
+ rv;
+
+ struct lfds710_queue_umm_element
+ *qumme;
+
+ struct lfds710_ringbuffer_element
+ *re;
+
+ LFDS710_PAL_ASSERT( rs != NULL );
+ // TRD : key can be NULL
+ // TRD : value can be NULL
+ // TRD : psts can be NULL
+
+ rv = lfds710_queue_umm_dequeue( &rs->qumms, &qumme );
+
+ if( rv == 1 )
+ {
+ re = LFDS710_QUEUE_UMM_GET_VALUE_FROM_ELEMENT( *qumme );
+ re->qumme_use = (struct lfds710_queue_umm_element *) qumme;
+ if( key != NULL )
+ *key = re->key;
+ if( value != NULL )
+ *value = re->value;
+ LFDS710_FREELIST_SET_VALUE_IN_ELEMENT( re->fe, re );
+ lfds710_freelist_push( &rs->fs, &re->fe, NULL );
+ }
+
+ return rv;
+}
+
diff --git
a/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_write.c
b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_write.c
new file mode 100644
index 0000000..4aedd18
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_ringbuffer/lfds710_ringbuffer_write.c
@@ -0,0 +1,77 @@
+/***** includes *****/
+#include "lfds710_ringbuffer_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_ringbuffer_write( struct lfds710_ringbuffer_state *rs,
+ void *key,
+ void *value,
+ enum lfds710_misc_flag *overwrite_occurred_flag,
+ void **overwritten_key,
+ void **overwritten_value )
+{
+ int
+ rv = 0;
+
+ struct lfds710_freelist_element
+ *fe;
+
+ struct lfds710_queue_umm_element
+ *qumme;
+
+ struct lfds710_ringbuffer_element
+ *re = NULL;
+
+ LFDS710_PAL_ASSERT( rs != NULL );
+ // TRD : key can be NULL
+ // TRD : value can be NULL
+ // TRD : overwrite_occurred_flag can be NULL
+ // TRD : overwritten_key can be NULL
+ // TRD : overwritten_value can be NULL
+ // TRD : psts can be NULL
+
+ if( overwrite_occurred_flag != NULL )
+ *overwrite_occurred_flag = LFDS710_MISC_FLAG_LOWERED;
+
+ do
+ {
+ rv = lfds710_freelist_pop( &rs->fs, &fe, NULL );
+
+ if( rv == 1 )
+ re = LFDS710_FREELIST_GET_VALUE_FROM_ELEMENT( *fe );
+
+ if( rv == 0 )
+ {
+ // TRD : the queue can return empty as well - remember, we're lock-free;
anything could have happened since the previous instruction
+ rv = lfds710_queue_umm_dequeue( &rs->qumms, &qumme );
+
+ if( rv == 1 )
+ {
+ re = LFDS710_QUEUE_UMM_GET_VALUE_FROM_ELEMENT( *qumme );
+ re->qumme_use = (struct lfds710_queue_umm_element *) qumme;
+
+ if( overwrite_occurred_flag != NULL )
+ *overwrite_occurred_flag = LFDS710_MISC_FLAG_RAISED;
+
+ if( overwritten_key != NULL )
+ *overwritten_key = re->key;
+
+ if( overwritten_value != NULL )
+ *overwritten_value = re->value;
+ }
+ }
+ }
+ while( rv == 0 );
+
+ re->key = key;
+ re->value = value;
+
+ LFDS710_QUEUE_UMM_SET_VALUE_IN_ELEMENT( *re->qumme_use, re );
+ lfds710_queue_umm_enqueue( &rs->qumms, re->qumme_use );
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_cleanup.c
b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_cleanup.c
new file mode 100644
index 0000000..fdcf288
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_cleanup.c
@@ -0,0 +1,36 @@
+/***** includes *****/
+#include "lfds710_stack_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_stack_cleanup( struct lfds710_stack_state *ss,
+ void (*element_cleanup_callback)(struct lfds710_stack_state
*ss, struct lfds710_stack_element *se) )
+{
+ struct lfds710_stack_element
+ *se,
+ *se_temp;
+
+ LFDS710_PAL_ASSERT( ss != NULL );
+ // TRD : element_cleanup_callback can be NULL
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ if( element_cleanup_callback != NULL )
+ {
+ se = ss->top[POINTER];
+
+ while( se != NULL )
+ {
+ se_temp = se;
+ se = se->next;
+
+ element_cleanup_callback( ss, se_temp );
+ }
+ }
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_init.c
b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_init.c
new file mode 100644
index 0000000..b159b4b
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_init.c
@@ -0,0 +1,31 @@
+/***** includes *****/
+#include "lfds710_stack_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_stack_init_valid_on_current_logical_core( struct lfds710_stack_state *ss,
+ void *user_state )
+{
+ LFDS710_PAL_ASSERT( ss != NULL );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) ss->top %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ LFDS710_PAL_ASSERT( (lfds710_pal_uint_t) &ss->user_state %
LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES == 0 );
+ // TRD : user_state can be NULL
+
+ ss->top[POINTER] = NULL;
+ ss->top[COUNTER] = 0;
+
+ ss->user_state = user_state;
+
+ lfds710_misc_internal_backoff_init( &ss->pop_backoff );
+ lfds710_misc_internal_backoff_init( &ss->push_backoff );
+
+ LFDS710_MISC_BARRIER_STORE;
+
+ lfds710_misc_force_store();
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_internal.h
b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_internal.h
new file mode 100644
index 0000000..7db9158
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_internal.h
@@ -0,0 +1,5 @@
+/***** the library wide include file *****/
+#include "../liblfds710_internal.h"
+
+/***** private prototypes *****/
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_pop.c
b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_pop.c
new file mode 100644
index 0000000..eded13e
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_pop.c
@@ -0,0 +1,57 @@
+/***** includes *****/
+#include "lfds710_stack_internal.h"
+
+
+
+
+
+/****************************************************************************/
+int lfds710_stack_pop( struct lfds710_stack_state *ss,
+ struct lfds710_stack_element **se )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_stack_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *new_top[PAC_SIZE],
+ *volatile original_top[PAC_SIZE];
+
+ LFDS710_PAL_ASSERT( ss != NULL );
+ LFDS710_PAL_ASSERT( se != NULL );
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ original_top[COUNTER] = ss->top[COUNTER];
+ original_top[POINTER] = ss->top[POINTER];
+
+ do
+ {
+ if( original_top[POINTER] == NULL )
+ {
+ *se = NULL;
+ return 0;
+ }
+
+ new_top[COUNTER] = original_top[COUNTER] + 1;
+ new_top[POINTER] = original_top[POINTER]->next;
+
+ LFDS710_PAL_ATOMIC_DWCAS( ss->top, original_top, new_top,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ {
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( ss->pop_backoff, backoff_iteration );
+ LFDS710_MISC_BARRIER_LOAD;
+ }
+ }
+ while( result == 0 );
+
+ *se = original_top[POINTER];
+
+ LFDS710_BACKOFF_AUTOTUNE( ss->pop_backoff, backoff_iteration );
+
+ return 1;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_push.c
b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_push.c
new file mode 100644
index 0000000..7aa12bb
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_push.c
@@ -0,0 +1,47 @@
+/***** includes *****/
+#include "lfds710_stack_internal.h"
+
+
+
+
+
+/****************************************************************************/
+void lfds710_stack_push( struct lfds710_stack_state *ss,
+ struct lfds710_stack_element *se )
+{
+ char unsigned
+ result;
+
+ lfds710_pal_uint_t
+ backoff_iteration = LFDS710_BACKOFF_INITIAL_VALUE;
+
+ struct lfds710_stack_element LFDS710_PAL_ALIGN(LFDS710_PAL_ALIGN_DOUBLE_POINTER)
+ *new_top[PAC_SIZE],
+ *volatile original_top[PAC_SIZE];
+
+ LFDS710_PAL_ASSERT( ss != NULL );
+ LFDS710_PAL_ASSERT( se != NULL );
+
+ new_top[POINTER] = se;
+
+ original_top[COUNTER] = ss->top[COUNTER];
+ original_top[POINTER] = ss->top[POINTER];
+
+ do
+ {
+ se->next = original_top[POINTER];
+ LFDS710_MISC_BARRIER_STORE;
+
+ new_top[COUNTER] = original_top[COUNTER] + 1;
+ LFDS710_PAL_ATOMIC_DWCAS( ss->top, original_top, new_top,
LFDS710_MISC_CAS_STRENGTH_WEAK, result );
+
+ if( result == 0 )
+ LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( ss->push_backoff, backoff_iteration );
+ }
+ while( result == 0 );
+
+ LFDS710_BACKOFF_AUTOTUNE( ss->push_backoff, backoff_iteration );
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_query.c
b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_query.c
new file mode 100644
index 0000000..1cf8579
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/lfds710_stack/lfds710_stack_query.c
@@ -0,0 +1,130 @@
+/***** includes *****/
+#include "lfds710_stack_internal.h"
+
+/***** private prototypes *****/
+static void lfds710_stack_internal_stack_validate( struct lfds710_stack_state *ss,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_stack_validity );
+
+
+
+
+
+/****************************************************************************/
+void lfds710_stack_query( struct lfds710_stack_state *ss,
+ enum lfds710_stack_query query_type,
+ void *query_input,
+ void *query_output )
+{
+ struct lfds710_stack_element
+ *se;
+
+ LFDS710_MISC_BARRIER_LOAD;
+
+ LFDS710_PAL_ASSERT( ss != NULL );
+ // TRD : query_type can be any value in its range
+
+ switch( query_type )
+ {
+ case LFDS710_STACK_QUERY_SINGLETHREADED_GET_COUNT:
+ LFDS710_PAL_ASSERT( query_input == NULL );
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ *(lfds710_pal_uint_t *) query_output = 0;
+
+ se = (struct lfds710_stack_element *) ss->top[POINTER];
+
+ while( se != NULL )
+ {
+ ( *(lfds710_pal_uint_t *) query_output )++;
+ se = (struct lfds710_stack_element *) se->next;
+ }
+ break;
+
+ case LFDS710_STACK_QUERY_SINGLETHREADED_VALIDATE:
+ // TRD : query_input can be NULL
+ LFDS710_PAL_ASSERT( query_output != NULL );
+
+ lfds710_stack_internal_stack_validate( ss, (struct lfds710_misc_validation_info *)
query_input, (enum lfds710_misc_validity *) query_output );
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+/****************************************************************************/
+static void lfds710_stack_internal_stack_validate( struct lfds710_stack_state *ss,
+ struct lfds710_misc_validation_info
*vi,
+ enum lfds710_misc_validity
*lfds710_stack_validity )
+{
+ lfds710_pal_uint_t
+ number_elements = 0;
+
+ struct lfds710_stack_element
+ *se_fast,
+ *se_slow;
+
+ LFDS710_PAL_ASSERT( ss != NULL );
+ // TRD : vi can be NULL
+ LFDS710_PAL_ASSERT( lfds710_stack_validity != NULL );
+
+ *lfds710_stack_validity = LFDS710_MISC_VALIDITY_VALID;
+
+ se_slow = se_fast = (struct lfds710_stack_element *) ss->top[POINTER];
+
+ /* TRD : first, check for a loop
+ we have two pointers
+ both of which start at the top of the stack
+ we enter a loop
+ and on each iteration
+ we advance one pointer by one element
+ and the other by two
+
+ we exit the loop when both pointers are NULL
+ (have reached the end of the stack)
+
+ or
+
+ if we fast pointer 'sees' the slow pointer
+ which means we have a loop
+ */
+
+ if( se_slow != NULL )
+ do
+ {
+ se_slow = se_slow->next;
+
+ if( se_fast != NULL )
+ se_fast = se_fast->next;
+
+ if( se_fast != NULL )
+ se_fast = se_fast->next;
+ }
+ while( se_slow != NULL and se_fast != se_slow );
+
+ if( se_fast != NULL and se_slow != NULL and se_fast == se_slow )
+ *lfds710_stack_validity = LFDS710_MISC_VALIDITY_INVALID_LOOP;
+
+ /* TRD : now check for expected number of elements
+ vi can be NULL, in which case we do not check
+ we know we don't have a loop from our earlier check
+ */
+
+ if( *lfds710_stack_validity == LFDS710_MISC_VALIDITY_VALID and vi != NULL )
+ {
+ lfds710_stack_query( ss, LFDS710_STACK_QUERY_SINGLETHREADED_GET_COUNT, NULL, (void *)
&number_elements );
+
+ if( number_elements < vi->min_elements )
+ *lfds710_stack_validity = LFDS710_MISC_VALIDITY_INVALID_MISSING_ELEMENTS;
+
+ if( number_elements > vi->max_elements )
+ *lfds710_stack_validity = LFDS710_MISC_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;
+ }
+
+ return;
+}
+
diff --git a/src/libsds/external/liblfds710/src/liblfds710_internal.h
b/src/libsds/external/liblfds710/src/liblfds710_internal.h
new file mode 100644
index 0000000..655a1bd
--- /dev/null
+++ b/src/libsds/external/liblfds710/src/liblfds710_internal.h
@@ -0,0 +1,102 @@
+/***** public prototypes *****/
+#include "../inc/liblfds710.h"
+
+/***** defines *****/
+#define and &&
+#define or ||
+
+#define NO_FLAGS 0x0
+
+#define LFDS710_VERSION_STRING "7.1.0"
+#define LFDS710_VERSION_INTEGER 710
+
+#if( defined KERNEL_MODE )
+ #define MODE_TYPE_STRING "kernel-mode"
+#endif
+
+#if( !defined KERNEL_MODE )
+ #define MODE_TYPE_STRING "user-mode"
+#endif
+
+#if( defined NDEBUG && !defined COVERAGE && !defined TSAN &&
!defined PROF )
+ #define BUILD_TYPE_STRING "release"
+#endif
+
+#if( !defined NDEBUG && !defined COVERAGE && !defined TSAN &&
!defined PROF )
+ #define BUILD_TYPE_STRING "debug"
+#endif
+
+#if( !defined NDEBUG && defined COVERAGE && !defined TSAN &&
!defined PROF )
+ #define BUILD_TYPE_STRING "coverage"
+#endif
+
+#if( !defined NDEBUG && !defined COVERAGE && defined TSAN &&
!defined PROF )
+ #define BUILD_TYPE_STRING "threadsanitizer"
+#endif
+
+#if( !defined NDEBUG && !defined COVERAGE && !defined TSAN &&
defined PROF )
+ #define BUILD_TYPE_STRING "profiling"
+#endif
+
+#define LFDS710_BACKOFF_INITIAL_VALUE 0
+#define LFDS710_BACKOFF_LIMIT 10
+
+#define LFDS710_BACKOFF_EXPONENTIAL_BACKOFF( backoff_state, backoff_iteration )
\
+{
\
+ lfds710_pal_uint_t volatile
\
+ loop;
\
+
\
+ lfds710_pal_uint_t
\
+ endloop;
\
+
\
+ if( (backoff_iteration) == LFDS710_BACKOFF_LIMIT )
\
+ (backoff_iteration) = LFDS710_BACKOFF_INITIAL_VALUE;
\
+ else
\
+ {
\
+ endloop = ( ((lfds710_pal_uint_t) 0x1) << (backoff_iteration) ) *
(backoff_state).metric; \
+ for( loop = 0 ; loop < endloop ; loop++ );
\
+ }
\
+
\
+ (backoff_iteration)++;
\
+}
+
+#define LFDS710_BACKOFF_AUTOTUNE( bs, backoff_iteration )
\
+{
\
+ if( (backoff_iteration) < 2 )
\
+ (bs).backoff_iteration_frequency_counters[(backoff_iteration)]++;
\
+
\
+ if( ++(bs).total_operations >= 10000 and (bs).lock == LFDS710_MISC_FLAG_LOWERED )
\
+ {
\
+ char unsigned
\
+ result;
\
+
\
+ lfds710_pal_uint_t LFDS710_PAL_ALIGN(LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES)
\
+ compare = LFDS710_MISC_FLAG_LOWERED;
\
+
\
+ LFDS710_PAL_ATOMIC_CAS( &(bs).lock, &compare, LFDS710_MISC_FLAG_RAISED,
LFDS710_MISC_CAS_STRENGTH_WEAK, result ); \
+
\
+ if( result == 1 )
\
+ {
\
+ /* TRD : if E[1] is less than 1/100th of E[0], decrease the metric, to increase
E[1] */ \
+ if( (bs).backoff_iteration_frequency_counters[1] <
(bs).backoff_iteration_frequency_counters[0] / 100 ) \
+ {
\
+ if( (bs).metric >= 11 )
\
+ (bs).metric -= 10;
\
+ }
\
+ else
\
+ (bs).metric += 10;
\
+
\
+ (bs).backoff_iteration_frequency_counters[0] = 0;
\
+ (bs).backoff_iteration_frequency_counters[1] = 0;
\
+ (bs).total_operations = 0;
\
+
\
+ LFDS710_MISC_BARRIER_STORE;
\
+
\
+ LFDS710_PAL_ATOMIC_SET( &(bs).lock, LFDS710_MISC_FLAG_LOWERED );
\
+ }
\
+ }
\
+}
+
+/***** library-wide prototypes *****/
+void lfds710_misc_internal_backoff_init( struct lfds710_misc_backoff_state *bs );
+
diff --git a/src/libsds/include/sds.h b/src/libsds/include/sds.h
new file mode 100644
index 0000000..31507a7
--- /dev/null
+++ b/src/libsds/include/sds.h
@@ -0,0 +1,1328 @@
+/* BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#pragma once
+
+/**
+ * \defgroup sds_misc SDS Miscelaneous
+ * Miscelaneous functions for SDS. This may be replaced by a PAL from ns-slapd soon.
+ */
+
+/**
+ * \defgroup sds_queue SDS Queue
+ * Unprotected queue for void types
+ */
+
+/**
+ * \defgroup sds_tqueue SDS Thread Safe Queue
+ * Thread safe queue, protected by platform mutexs.
+ */
+
+/**
+ * \defgroup sds_lqueue SDS Lock Free Queue
+ * Thread safe lock free queue. Falls back to platform mutex if cpu intrinsics are not
present.
+ */
+
+/**
+ * \defgroup sds_bptree SDS B+Tree
+ * Unprotected B+Tree for void types. Supports fast set operations, search, insert,
delete and build.
+ *
+ * This structure should be considered for all locations where unprotected AVL treels, or
array based
+ * binary search trees are used.
+ */
+
+/**
+ * \defgroup sds_bptree_cow SDS Copy on Write B+Tree
+ * Thread safe Copy of Write B+Tree. Supports parallel read transactions and serialised
writes with ref counted members.
+ *
+ * This is a very important structure, and is the primary reason to consume this library.
There are a number of important properties
+ * that this tree provides.
+ *
+ * A key concept of this structure is that all values and keys contained have their
lifetimes bound to the life of the tree and it's
+ * nodes. This is due to the transactional nature of the structure. Consider a value that
has been inserted into the tree. We take a
+ * read transaction of the tree in thread A. Thread B now deletes the value that was
inserted. Because A still refers to the value,
+ * it is not freed until thread A closes it's read transaction. This guarantees that
all read transactions are always valid, while
+ * also maintaining that memory can be freed correctly. This has many applications
including the Directory Server plugin subsystem.
+ * We can use this property to guarantee a plugin exists for the life of an operation,
and we only free it once all consumers have
+ * completed their operations with it.
+ *
+ * Another key aspect of this tree is that write transactions *do not* block read
transactions. Reads can be completed in parallel
+ * to writes with complete safety. Write operations are serialised in this structure.
This makes the structure extremely effective
+ * for long read operations with many queries, without interrupting other threads.
+ *
+ * Along with the value and key ownership properties, we can guarantee that the content
of the tree within a read transaction
+ * will *not* be removed or added and will be consistent within the tree (however, we
can't guarantee a user doesn't change the value.)
+ *
+ * As a comparison, the single thread tree is on paper, much faster than the COW version
due to less memory operations. However in a
+ * multi thread use case, the COW version is signifigantly faster *and* safer than a
mutex protected single thread tree.
+ *
+ * This tree can be used as a form of object manager for C, as well as a way to allow
async operations between threads while maintaining,
+ * thread safety across an application. This assumes that the operations are largely
parallel and batch read oriented over write heavy small
+ * transactions.
+ */
+
+/** \addtogroup sds_misc
+ * @{
+ */
+
+/**
+ * sds_result encapsulates the set of possible success or failures an operation
+ * may have during processing. You must always check this value from function
+ * calls.
+ */
+typedef enum _sds_result {
+ /**
+ * SDS_SUCCESS 0 represents that the operation had no errors in processing.
+ */
+ SDS_SUCCESS = 0,
+ /**
+ * SDS_UNKNOWN_ERROR 1 represents that an unexpected issue was encountered,
+ * you should raise a bug about this if you encounter it!
+ */
+ SDS_UNKNOWN_ERROR = 1,
+ /**
+ * SDS_NULL_POINTER 2 represents that a NULL pointer was provided to a function
+ * that expects a true pointer to valid data. It may also represent that
+ * the internals of a datastructure have been corrupted with an unexpected NULL
+ * pointer, and the operation can not proceed.
+ */
+ SDS_NULL_POINTER = 2,
+ /**
+ * SDS_TEST_FAILED 3 represents that a test case has failed. You may need to
+ * go back to the test case to see the originating error.
+ */
+ SDS_TEST_FAILED = 3,
+ /**
+ * SDS_DUPLICATE_KEY 4 represents that the key value already exists in the
+ * datastructure. This is generally seen during insertion operations.
+ */
+ SDS_DUPLICATE_KEY = 4,
+ /**
+ * SDS_CHECKSUM_FAILURE 5 represents that the in memory data does not pass
+ * checksum validation. This may be due to the fault of the application or
+ * hardware errors.
+ */
+ SDS_CHECKSUM_FAILURE = 5,
+ /**
+ * SDS_INVALID_NODE_ID 6 represents that the internal data of a datastructure
+ * node contains invalid identifying information.
+ */
+ SDS_INVALID_NODE_ID = 6,
+ /**
+ * SDS_INVALID_KEY 7 represents that a key is invalid in the state of this node.
+ * generally this is due to a bug in the datastructure code where keys are
+ * out of order, or incorrectly nulled.
+ */
+ SDS_INVALID_KEY = 7,
+ /**
+ * SDS_INVALID_VALUE_SIZE 8 represents that the size of the value provided
+ * does not hold true for certain assumptions. For example, a null pointer
+ * with a non zero size, or a pointer with a zero size.
+ */
+ SDS_INVALID_VALUE_SIZE = 8,
+ /**
+ * SDS_INVALID_POINTER 9 represents that within the datastructure an invalid
+ * pointer structure has been created that may cause further dataloss or
+ * corruption. This could be due to error in the datastructure code, or
+ * memory issues.
+ */
+ SDS_INVALID_POINTER = 9,
+ /**
+ * SDS_INVALID_NODE 10 represents that certain datastructure properties of
+ * this node have not been upheld. For example, branches should never contain
+ * data in a b+tree, or that the capacity of the node is incorrect and a
+ * split or merge should have occured.
+ */
+ SDS_INVALID_NODE = 10,
+ /**
+ * SDS_INVALID_KEY_ORDER 11 indicates that the ordering of keys within a
+ * structure is incorrect, and may cause data loss.
+ */
+ SDS_INVALID_KEY_ORDER = 11,
+ /**
+ * SDS_RETRY 12 represents that the previous operation should be attempted
+ * again. This is only for INTERNAL use. You will never see this returned.
+ */
+ SDS_RETRY = 12,
+ /**
+ * SDS_KEY_PRESENT 13 is a SUCCESS condition, similar to SDS_SUCCESS. This
+ * differs from SDS_SUCCESS in that it shows that a key value exists within
+ * some datastructure, and you must explcitly check for this case (vs
+ * SDS_KEY_NOT_PRESENT).
+ */
+ SDS_KEY_PRESENT = 13,
+ /**
+ * SDS_KEY_NOT_PRESENT 14 is a SUCCESS condition, similar to SDS_SUCCESS. This
+ * differs from SDS_SUCCESS in that is show that a key value does not exist
+ * with some datastructure, but the operation was still a SUCCESS in completion.
+ * You must explicitly check for this case to ensure you know the true result
+ * of a function.
+ */
+ SDS_KEY_NOT_PRESENT = 14,
+ /**
+ * SDS_INCOMPATIBLE_INSTANCE 15 represents that during a set operation, you
+ * are attempting to manipulate datastructures with potentiall different key
+ * and value types. This would result in datacorruption or undefined behaviour
+ * so instead, we return a pre-emptive error.
+ */
+ SDS_INCOMPATIBLE_INSTANCE = 15,
+ /**
+ * SDS_LIST_EXHAUSTED 16 represents that there are no more elements in this
+ * datastructure to be consumed during an iteration.
+ */
+ SDS_LIST_EXHAUSTED = 16,
+ /**
+ * SDS_INVALID_TXN 17 represents that a transaction identifier is invalid
+ * for the requested operation. IE using a read only transaction to attempt
+ * a write.
+ */
+ SDS_INVALID_TXN = 17,
+} sds_result;
+
+/**
+ * sds_malloc wraps the system memory allocator with an OOM check. During debugging
+ * this call guarantees that memory is zeroed before use.
+ *
+ * \param size the amount of memory to allocate in bytes.
+ * \retval pointer to the allocated memory.
+ */
+void * sds_malloc(size_t size);
+/**
+ * sds_calloc wraps the system calloc call with an OOM check. This call like calloc,
+ * guarantees the returned memory is zeroed before usage.
+ *
+ * \param size the amount of memory to allocate in bytes.
+ * \retval pointer to the allocated memory.
+ */
+void * sds_calloc(size_t size);
+/**
+ * sds_memalign wraps the posix_memalign function with an OOM and alignment check. During
debugging
+ * this call guarantees that memory is zeroed before use.
+ *
+ * \param size the amount of memory to allocate in bytes.
+ * \param alignment the size to align to in bytes. Must be a power of 2.
+ * \retval pointer to the allocated memory.
+ */
+void * sds_memalign(size_t size, size_t alignment);
+/**
+ * sds_free wraps the system free call with a null check.
+ *
+ * \param ptr the memory to free.
+ */
+void sds_free(void *ptr);
+
+/**
+ * sds_crc32c uses the crc32c algorithm to create a verification checksum of data.
+ * This checksum is for data verification, not cryptographic purposes. It is used
+ * largely in debugging to find cases when bytes in structures are updated incorrectly,
+ * or to find memory bit flips during operation. If avaliable, this will use the
+ * intel sse4 crc32c hardware acceleration.
+ *
+ * \param crc The running CRC value. Initially should be 0. If in doubt, use 0.
+ * \param data Pointer to the data to checksum.
+ * \param length number of bytes to validate.
+ * \retval crc The crc of this data. May be re-used in subsequent sds_crc32c calls
+ * for certain datatypes.
+ */
+uint32_t sds_crc32c(uint32_t crc, const unsigned char *data, size_t length);
+
+/**
+ * sds_uint64_t_compare takes two void *, and treats them as uint64_t *.
+ *
+ * \param a The first uint64_t *.
+ * \param b The second uint64_t *.
+ * \retval result of the comparison.
+ */
+int64_t sds_uint64_t_compare(void *a, void *b);
+/**
+ * Free the allocated uint64_t * from sds_uint64_t_alloc.
+ *
+ * \param key Do nothing to the key.
+ */
+void sds_uint64_t_free(void *key);
+/**
+ * sds_uint64_t_dup Returns a copy of the uint64_t * to the caller.
+ *
+ * \param key The uint64_t * to copy.
+ * \retval result the copy of the value.
+ */
+void *sds_uint64_t_dup(void *key);
+/**
+ * sds_strcmp exists to wrap the system strcmp call with the correct types needed
+ * for libsds datastructures to operate correctly.
+ *
+ * \param a Pointer to the first string.
+ * \param b Pointer to the second string.
+ * \retval Difference between the values. 0 indicates they are the same.
+ */
+int64_t sds_strcmp(void *a, void *b);
+/**
+ * sds_strdup exists to wrap the system strdup call with the correct types needed for
+ * libsds datastructures to operate correctly.
+ *
+ * \param key Pointer to the string to duplicate.
+ * \retval Pointer to the newly allocated string.
+ */
+void *sds_strdup(void *key);
+
+
+/**
+ * @}
+ */
+/* end sds_misc */
+
+/** \addtogroup sds_bptree_cow
+ * @{
+ */
+
+/**
+ * sds_txt_state lists the possible states of a transaction.
+ */
+typedef enum _sds_txn_state {
+ /**
+ * SDS_TXN_READ 0 This is a read only transaction. Not mutable actions may
+ * be taken.
+ */
+ SDS_TXN_READ = 0,
+ /**
+ * SDS_TXN_WRITE 1 This is a read and write transaction. You may perform
+ * all read operations, and also all write operations.
+ */
+ SDS_TXN_WRITE = 1,
+} sds_txn_state;
+
+/**
+ * sds_bptree_node_list stores a linked list of sds_bptree_nodes for tracking.
+ * Internally this is used extensively for transaction references.
+ */
+typedef struct _sds_bptree_node_list {
+ /**
+ * checksum of the node list item.
+ */
+ uint32_t checksum;
+ /**
+ * Pointer to the node item.
+ */
+ struct _sds_bptree_node *node;
+ /**
+ * Pointer to the next node list item. NULL indicates list termination.
+ */
+ struct _sds_bptree_node_list *next;
+} sds_bptree_node_list;
+
+/**
+ * sds_bptree_transaction Manages the content and lifetime of nodes within the tree. It
is the basis of our
+ * garbage collection system, using atomic reference counts to synchronise our
behaviour.
+ */
+typedef struct _sds_bptree_transaction {
+ /**
+ * Checksum of the data in this structure.
+ */
+ uint32_t checksum;
+ /**
+ * Reference count to the number of consumers of this transaction. This is
+ * atomically updated. If this is the "active" master transaction, this is
+ * guaranteed to be 1 or greater. When a new write transaction is commited,
+ * if this reference count falls to 0, this transaction is implicitly
+ * destroy.
+ */
+ uint32_t reference_count;
+ /**
+ * The state of the transaction. All transactions start as SDS_WRITE_TXN,
+ * and upon commit move to the SDS_READ_TXN state.
+ */
+ sds_txn_state state;
+ /**
+ * Pointer to the cow b+tree instance that created us.
+ */
+ struct _sds_bptree_cow_instance *binst;
+ /**
+ * Pointer to the b+tree instance that this transaction holds.
+ */
+ struct _sds_bptree_instance *bi;
+ /**
+ * The unique identifier of this transaction.
+ */
+ uint64_t txn_id;
+ /**
+ * The list of nodes that this transaction "owns". When the reference
count
+ * moves to 0, these nodes will be freed. This list is created during a write
+ * transaction of "items that will not be needed when we are removed.".
+ *
+ * IE, when we have a txn A, then a new transaction B is made, we "copy"
node
+ * 1a to node 1b. Node 1b is "created" by txn B, and node 1a is
"owend" by
+ * txn A, because it's the last transaction that depends on this nodes
existance.
+ */
+ sds_bptree_node_list *owned;
+ /**
+ * The list of nodes that this transaction "created". This is used during
an
+ * abort of the txn to roll back any changes that we made.
+ */
+ sds_bptree_node_list *created;
+ /**
+ * The current root node. Each transaction owns a unique root node which
+ * anchors the branches of the copy on write structure.
+ */
+ struct _sds_bptree_node *root;
+ /**
+ * The previous transaction that we are derived from.
+ */
+ struct _sds_bptree_transaction *parent_txn;
+ /**
+ * The next transaction that derives from us.
+ */
+ struct _sds_bptree_transaction *child_txn;
+} sds_bptree_transaction;
+
+/**
+ * @}
+ */
+/* end sds_bptree_cow */
+
+
+/** \addtogroup sds_bptree
+ * @{
+ */
+
+/**
+ * SDS_BPTREE_DEFAULT_CAPACITY 5 represements that there are 5 values held per
+ * in the B+Tree and COW B+Tree. A signifigant amount of testing went into the
+ * tuning of this value for best performance.
+ */
+#define SDS_BPTREE_DEFAULT_CAPACITY 5
+/**
+ * SDS_BPTREE_HALF_CAPACITY 3 is pre-calculated as "ceiling(default capacity /
2)".
+ * This is used to assert when node splits and merges should be taken.
+ */
+#define SDS_BPTREE_HALF_CAPACITY 3
+/**
+ * SDS_BPTREE_BRANCH 6 indicates that each node may potentially have up to 6 child
+ * nodes. This yields a broad tree, that requires fewer cache misses to travese
+ * (despite the higher number of comparisons to search).
+ */
+#define SDS_BPTREE_BRANCH 6
+
+/**
+ * This is the core of the B+Tree structures. This node represents the branches
+ * and leaves of the structure. It is 128 bytes, which fits on two cachelines.
+ * Thanks to modern prediction strategies, this is faster than a 64 byte struct
+ *, and yields the best performance in real tests.
+ */
+typedef struct _sds_bptree_node {
+ /**
+ * checksum of the structure data to detect errors.
+ */
+ uint32_t checksum;
+ /**
+ * The number of values currently stored in this structure.
+ */
+ uint16_t item_count;
+ /**
+ * This number of "rows" above the leaves this node is. 0 represents a
true
+ * leaf node, anything greater is a branch.
+ */
+ uint64_t level;
+ /**
+ * The id of the transaction that created this node. This is used so that
+ * within a transaction, we don't double copy values if we already copied
+ * them.
+ */
+ uint64_t txn_id;
+ /**
+ * Back reference to our parent. This is faster than creating a traversal
+ * list during each insertion (by a large factor).
+ */
+ struct _sds_bptree_node *parent;
+ /**
+ * Statically sized array of pointers to the keys of this structure.
+ */
+ void *keys[SDS_BPTREE_DEFAULT_CAPACITY];
+ /**
+ * Statically sized array of pointers to values. This is tagged by the level
+ * flag. If level is 0, this is a set of values that have been inserted
+ * by the consumer. If the level is > 0, this is the pointers to further
+ * node structs.
+ *
+ * In a leaf, this is [value, value, value, value, value, link -> ]
+ *
+ * In a non-leaf, this is [link, link, link, link, link, link]
+ */
+ void *values[SDS_BPTREE_BRANCH];
+} sds_bptree_node;
+
+/**
+ * The instance of the B+Tree. Stores references to function pointers for
+ * manipulation of keys and values within the tree. Maintains the root checksum
+ * which estabilshes the "root of trust" to all other nodes in the tree.
+ */
+typedef struct _sds_bptree_instance {
+ /**
+ * checksum of the instance data.
+ */
+ uint32_t checksum;
+ /**
+ * This flag determines if we maintain and update checksum values during
+ * tree operations, and that we verify these during the verification operation.
+ */
+ uint16_t offline_checksumming;
+ /**
+ * This flag determines if we verify all checksums during the read and write
+ * paths of the code. Adds a large performance overhead, but guarantees
+ * the data in the tree is "consistent".
+ */
+ uint16_t search_checksumming;
+ /**
+ * Internal tracking id for tree display.
+ */
+ uint16_t print_iter;
+
+ /**
+ * Pointer to the current tree root node.
+ */
+ sds_bptree_node *root; /* The current root node of the DB */
+
+ /**
+ * This function should be able to free values of the type that will be
+ * inserted into the tree.
+ */
+ void (*value_free_fn)(void *value);
+ /**
+ * This function should be able to duplicate value of the type that will be
+ * inserted into the tree. Values may be freed and duplicated in an order
+ * you do not expect, so blindly returning a pointer to the same data may
+ * not be wise if free then destroys it.
+ */
+ void *(*value_dup_fn)(void *value);
+ /**
+ * Key comparison function. This must return an int64_t of the difference in
+ * the values. A result of < 0 indicates that a is less than b. A result of
+ * 0 indicates that the values of a and b are identical. A result of > 0
+ * indicates that a is greater than b.
+ */
+ int64_t (*key_cmp_fn)(void *a, void *b); /* Comparison function pointer */
+ /**
+ * Key free function. This must free the pointer provided by key.
+ */
+ void (*key_free_fn)(void *key);
+ /**
+ * Key duplication function. This duplicates the value of key and returns a
+ * new pointer to it. This is used extensively in the tree structure, and
+ * the key or result may be freed out of order, so don't blindly return the
+ * same data.
+ */
+ void *(*key_dup_fn)(void *key);
+} sds_bptree_instance;
+
+/**
+ * @}
+ */
+/* end sds_bptree */
+
+
+/** \addtogroup sds_bptree_cow
+ * @{
+ */
+
+/**
+ * The copy on write tree instance. This stores the current active reading
+ * transaction, number of active transactions, and pointers to the root of the
+ * b+tree.
+ *
+ * This instance is the most important part of the tree and provides us with memory
safety. Once we
+ * have memory safety, individual transactions are free to garbage collect on their own.
Garbage collection
+ * is coordinated from the instance.
+ *
+ * ## MEMORY SAFTEY
+ *
+ * Within the transaction, we have a small number of important fields. These are:
+ *
+ * - read_lock
+ * - write_lock
+ * - tail_txn
+ * - vacuum_lock
+ *
+ * ### Read Transaction Begin
+ *
+ * At the beginning of a read transaction, we take a rolock on read_lock. This ensures
that on
+ * our working thread, that the correct sequence of barriers for memory consistency of
the tree
+ * is issued.
+ *
+ * Within the read_lock, we use cpu atomics to increment the transaction reference count
by 1.
+ * We then release the read_lock.
+ *
+ * In this way, we allow multiple read transactions to be initiated at the same time,
with the
+ * only serialisation point being the atomic increment.
+ *
+ * While the read transaction is "active" (IE the transaction that is listed in
binst, where new
+ * transactions will reference), the reference count will never go below 1.
+ *
+ * ### Write Transaction
+ *
+ * At the beginning of a write transaction, we take the write_lock mutex. This guarantees
we are
+ * the only writer in the tree at a point in time. Due to the way mutexes work, this
guarantees
+ * our memory barries of the tree are issued, so we see all other writes that have
occured.
+ *
+ * During a write, we store a list of all nodes that were cloned: Both so that we know
who we cloned
+ * *from* but aso what nodes we *created*.
+ *
+ * ### Write Abort
+ *
+ * During a write abort, we release the write lock immediately. We then use the created
node list, and
+ * free all contents as they were never made active.
+ *
+ * ### Write commit
+ *
+ * During a commit, we prepare the transaction by changing it's state flags, and
setting it's reference
+ * count atomically. We relinquish the created list, because we have no need to free the
nodes that are
+ * about to become part of the tree. However, we update the previous transaction with the
"owned" list,
+ * as these are nodes we cloned from: IE these are nodes that serve no *future* role in
the tree, so the
+ * previous transaction is the last point where they are valid and needed - The last
transaction now must
+ * clean up after itself when it's ready to GC!
+ *
+ * We now take the write variant of the rw read_lock. This waits for all in progress read
transactions to
+ * begin, and we take exclusive access of this lock. We then pivot the older transaction
out with our
+ * transaction. The read_lock is released, and read transactions may now resume.
+ *
+ * At this point, we release the write_lock, and a new write may begin.
+ *
+ * We decrement the reference count of the previous transaction which may cause it to
drop to 0. This is
+ * the equivalent of a read transaction close. After this point, the previous
transactions reference
+ * count will only decrement, as previous holders close their transactions. Increment
only occurs from the
+ * active transaction!
+ *
+ * ### Read transaction close.
+ *
+ * When we close a read transaction, we require no locks. We use cpu atomic to set and
fetch the
+ * reference count.
+ *
+ * If the reference count remains positive, we complete, and continue.
+ *
+ * If the reference count drops to 0, we take the vacuum lock. If our reference count is
0 *and*
+ * we are the oldest generation of transaction (ie tail_txn), then we free our
transaction, and all
+ * associated owned nodes, values and keys. We update the tail_txn to be the next oldest
transaction
+ * in the chain and repeat the check.
+ *
+ * While the tail transaction reference count is 0, and the oldest transaction, we
continue to garbage
+ * collect until we find a live transaction. At this point, we release the vacuum lock
and are complete.
+ *
+ * NOTE: Due to this design, if a reference count is dropped to 0 of the tail_txn, while
someone else
+ * is vacuuming there are two states. Either, the other thread does not ees the reference
count drop, and will
+ * release the vacuum lock, allowing us to take it and contine the garbage collection.
Alternately, the other
+ * thread *does* see the reference count drop, and garbage collects the transaction. In
this case, we are blocked
+ * on the vacuum lock, but because the ref count is 0, and the tail_txn pointer is now a
newer generation than
+ * our transaction id, we do *not* attempt the double free.
+ *
+ * ### Usage of the memory safe properties.
+ *
+ * Due to the way the barriers are issued, when you have a read transaction or a write
transaction, you can
+ * guarantee that the tree memory is consistent and correct, and that all values will
remain alive until
+ * you close the transaction. This means you can use this to allow parallel tasks in
complex cases. Directory
+ * Server has such a case with the plugin subsystem. Consider every operation needs a
consistent list of plugins.
+ *
+ * You build a tree of the plugin references (dlopen pointers, reference count, pblock)
and store them.
+ *
+ * When you begin the operation you take a read transaction of this. This means every
plugin in the operation is
+ * guaranteed to be in the tree and held open for the duration of this operation. During
the read, if another thread
+ * commits the delete on a plugin, this drops the ref count, but not below 0 - the
operation can continue safely.
+ *
+ * Once the operation is complete, the transaction is closed. At this point, the vacuum
runs during close, which would
+ * trigger the value free function. This would now actually close the plugin pointers,
values. Additionally, the correct use
+ * of the transaction guarantees the memory content of the plugin is correct due to the
barriers inside of the transaction.
+ *
+ */
+typedef struct _sds_bptree_cow_instance {
+ /**
+ * checksum of the instance data.
+ */
+ uint32_t checksum;
+ /**
+ * Pointer to the current b+tree instance. This contains our function pointers
+ * for free, dup of key and values.
+ */
+ sds_bptree_instance *bi;
+ /**
+ * The pointer to the current "latest" commited transaction. When a new
read
+ * transaction is taken, this is the value that is returned (until a new
+ * write transaction is commited.)
+ */
+ sds_bptree_transaction *txn;
+ /**
+ * The pointer to the current "oldest" alive transaction. This is needed
for
+ * correct cleanup without corrupting intermediate trees.
+ *
+ * Consider we have a set of transactions, such as A -> B -> C. Between each
+ * a set of changes has occured. We assume that if we have branches in these,
+ * then during a cow operation they can be updated. So we end up in this
+ * situation.
+ *
+ * [ A ] [ B ] [ C ]
+ * | | |
+ * [ root a ] [ root b ] [ root c ]
+ * / \\ / \\ / \\
+ * [ a ] [ x ] [ a ] [ b ] [ c ] [ b ]
+ *
+ * Now, when we clone txn B, because C performed a cow on the leaf A, txn B
+ * "owns" leaf A.
+ * At this point, leaf A will be freed, which frees it from underneath
+ * transaction A. We have corrupted the tree of a previous node!
+ *
+ * To prevent this, we only truly free a transaction when the ref count is 0
+ * AND it's the last transaction in the list of live transactions. This way, B
+ * even though ref count reached 0, would not be freed til A is also freed.
+ *
+ */
+ sds_bptree_transaction *tail_txn;
+ /**
+ * The number of active transactions in the system.
+ */
+ uint64_t txn_count;
+ /**
+ * The transaction read lock. This is used to prevent corruption of the txn
+ * pointer during a read or write transaction acquisition.
+ */
+ pthread_rwlock_t *read_lock;
+ /**
+ * The transaction write lock. During a write transaction, this is held for
+ * the duration to allow only a single writer.
+ */
+ pthread_mutex_t *write_lock;
+ /**
+ * The vacuum lock. When a transaction final hits a ref count of 0, we have
+ * to clean from the tail. To do this we need to update the tail_txn of the
+ * binst atomically.
+ */
+ pthread_mutex_t *vacuum_lock;
+} sds_bptree_cow_instance;
+
+/**
+ * @}
+ */
+/* end sds_bptree_cow */
+
+/* Queue operations */
+
+/** \addtogroup sds_queue
+ * @{
+ */
+
+/**
+ * An internal queue node element.
+ */
+typedef struct _sds_queue_node {
+ /**
+ * The data this node holds.
+ */
+ void *element;
+ /**
+ * The pointer to the next element of the queue.
+ */
+ struct _sds_queue_node *next;
+ /**
+ * The pointer to the previous element of the queue.
+ */
+ struct _sds_queue_node *prev;
+} sds_queue_node;
+
+/**
+ * A queue that is internally a doubly linked list.
+ */
+typedef struct _sds_queue {
+ /**
+ * The pointer to the current active head node. This is the "next" node
that
+ * will be dequeued and acted upon during the dequeue (pop) operation.
+ */
+ struct _sds_queue_node *head;
+ /**
+ * The tail of the queue. This is the "last" value that was inserted.
+ */
+ struct _sds_queue_node *tail;
+ /**
+ * If there are remaining nodes when the queue is destroyed, the queue will
+ * be drained and this free function called on the value of within.
+ */
+ void (*value_free_fn)(void *value);
+} sds_queue;
+
+/**
+ * Initialise a queue suitable for access within a single thread. Free function
+ * must be set.
+ *
+ * \param q_ptr A pointer to the struct pointer you wish to allocate. This may be
+ * on the heap or stack.
+ * \param value_free_fn A function pointer to free values that are enqueued to
+ * this structure.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_queue_init(sds_queue **q_ptr, void (*value_free_fn)(void *value));
+/**
+ * Enqueue an item to the tail of this queue. Equivalent to "push".
+ *
+ * \param q The struct pointer to the queue you wish to push to.
+ * \param elem The data to enqueue.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_queue_enqueue(sds_queue *q, void *elem);
+/**
+ * Dequeue an item from the head of the queue. Equivalent to "pop".
+ *
+ * \param q The struct pointer to the queue you wish to pop from.
+ * \param elem a pointer to a location which will be over-ridden with the data that
+ * is dequeued. This may be NULLed, even during an error.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_queue_dequeue(sds_queue *q, void **elem);
+/**
+ * Indicate you are complete with the queue. Free and delete any remaining
+ * internal structures, and free and still queued nodes. You must *always*
+ * call this function to dispose of this structure.
+ *
+ * \param q The struct pointer to the queue you wish to dispose of.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_queue_destroy(sds_queue *q);
+
+/**
+ * @}
+ */
+/* end sds_queue */
+
+/** \addtogroup sds_tqueue
+ * @{
+ */
+
+/**
+ * Implement a thread safe wrapper around an sds queue. This guarantees multithread
+ * safety to the operations of the queue.
+ */
+typedef struct _sds_tqueue {
+ /**
+ * Pointer to the underlying queue structure.
+ */
+ sds_queue *uq;
+ /**
+ * Lock that protects queue operations.
+ */
+ PRLock *trust_e_threadz;
+} sds_tqueue;
+/* Thread safe queue operations. */
+
+/**
+ * Initialise a queue suitable for access across multiple threads. Free function
+ * must be set.
+ *
+ * \param q_ptr A pointer to the struct pointer you wish to allocate. This may be
+ * on the heap or stack.
+ * \param value_free_fn A function pointer to free values that are enqueued to
+ * this structure.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_tqueue_init(sds_tqueue **q_ptr, void (*value_free_fn)(void *value));
+/**
+ * Enqueue an item to the tail of this queue. Equivalent to "push".
+ * The safety of this operation is implicit, you do not need any other mutexes
+ * to protect this operation.
+ *
+ * \param q The struct pointer to the queue you wish to push to.
+ * \param elem The data to enqueue.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_tqueue_enqueue(sds_tqueue *q, void *elem);
+/**
+ * Dequeue an item from the head of the queue. Equivalent to "pop".
+ * The safety of this operation is implicit, you do not need any other mutexes
+ * to protect this operation.
+ *
+ * \param q The struct pointer to the queue you wish to pop from.
+ * \param elem a pointer to a location which will be over-ridden with the data that
+ * is dequeued. This may be NULLed, even during an error.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_tqueue_dequeue(sds_tqueue *q, void **elem);
+/**
+ * Indicate you are complete with the queue. Free and delete any remaining
+ * internal structures, and free and still queued nodes. You must *always*
+ * call this function to dispose of this structure.
+ *
+ * \param q The struct pointer to the queue you wish to dispose of.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_tqueue_destroy(sds_tqueue *q);
+
+/**
+ * @}
+ */
+/* end sds_tqueue */
+
+/** \addtogroup sds_lqueue
+ * @{
+ */
+/* Lock free queue operations. */
+
+#ifdef ATOMIC_QUEUE_OPERATIONS
+/* Mask the definition of the queue */
+struct _sds_lqueue;
+/**
+ * Structure that provides a wrapper to the liblfds queue. This *must* be allocated
+ * with memalign to a 128 byte boundary! The SDS library will *correctly* do this
+ * for you!
+ */
+typedef struct _sds_lqueue sds_lqueue;
+#else
+/**
+ * Type definition of lock free queue to the mutex queue in the case that cpu
+ * intrinsics are not available, or the operating system is unsupported.
+ */
+typedef struct _sds_tqueue sds_lqueue;
+#endif
+
+/**
+ * Initialise a queue suitable for access across multiple threads. Free function
+ * must be set. This utilises hardware intrinsics to provide atomicity that may
+ * be faster in highly contended applications. Use this structure in place of a
+ * tqueue only if you know that content of the queue will be high.
+ *
+ * \param q_ptr A pointer to the struct pointer you wish to allocate. This may be
+ * on the heap or stack.
+ * \param value_free_fn A function pointer to free values that are enqueued to
+ * this structure.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_lqueue_init(sds_lqueue **q_ptr, void (*value_free_fn)(void *value));
+/**
+ * Initialise this thread ready to use the lqueue. This is *critical*, and accessing
+ * the queue without calling tprep may result in undefined behaviour.
+ */
+sds_result sds_lqueue_tprep(sds_lqueue *q);
+/**
+ * Enqueue an item to the tail of this queue. Equivalent to "push".
+ * The safety of this operation is implicit, you do not need any other mutexes
+ * to protect this operation.
+ *
+ * \param q The struct pointer to the queue you wish to push to.
+ * \param elem The data to enqueue.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_lqueue_enqueue(sds_lqueue *q, void *elem);
+/**
+ * Dequeue an item from the head of the queue. Equivalent to "pop".
+ * The safety of this operation is implicit, you do not need any other mutexes
+ * to protect this operation.
+ *
+ * \param q The struct pointer to the queue you wish to pop from.
+ * \param elem a pointer to a location which will be over-ridden with the data that
+ * is dequeued. This may be NULLed, even during an error.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_lqueue_dequeue(sds_lqueue *q, void **elem);
+/**
+ * Indicate you are complete with the queue. Free and delete any remaining
+ * internal structures, and free and still queued nodes. You must *always*
+ * call this function to dispose of this structure.
+ *
+ * Important to note, is that all thread consumers of this queue must be stopped
+ * before you call lqueue destroy. This is your responsibility to ensure this.
+ *
+ * \param q The struct pointer to the queue you wish to dispose of.
+ * \retval sds_result to indicate the status of the operation.
+ */
+sds_result sds_lqueue_destroy(sds_lqueue *q);
+
+/**
+ * @}
+ */
+/* end sds_lqueue */
+
+/*
+ * How does the COW locking work? It depends on which operation is occuring.
+ *
+ * If we are beginning a search, first we take the read_lock. We then increment
+ * txn ref_count, and release the lock after taking a reference.
+ *
+ * If we are doing a write, we take the write_lock, perform the operation. If
+ * we roll back, release the write_lock.
+ * If we commit, take the read_lock, now update the current active txn. Dec the
+ * ref count to the former transaction.
+ */
+
+/** \addtogroup sds_bptree
+ * @{
+ */
+
+/**
+ * Initialise an sds b+tree for usage.
+ *
+ * \param binst_ptr Pointer to a struct pointer for the instance to initialise.
+ * \param checksumming Flag to enable online and search checksumming. 0 to disable.
+ * \param key_cmp_fn Key comparison function.
+ * \param value_free_fn Function to free values that are assigned to this structure.
+ * \param key_free_fn Function to free keys that are inside of this structure.
+ * \param key_dup_fn Function to duplication keys within the structure.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_init(sds_bptree_instance **binst_ptr, uint16_t checksumming,
int64_t (*key_cmp_fn)(void *a, void *b), void (*value_free_fn)(void *value), void
(*key_free_fn)(void *key), void *(*key_dup_fn)(void *key));
+/**
+ * Bulk load data into a b+tree instance. The existing data in the b+tree instance
+ * will be destroyed during the operation. Keys *must* be sorted prior to calling
+ * this function. The index of items in values must match the corresponding key
+ * in keys, or be NULL. values may be NULL, which is assumed that all keys have null
+ * values associated. Count is the number of elements in keys and values. Key values
+ * are owned by the tree from this point, you MUST NOT free them. They must be able to
+ * be freed with your key_free_fn.
+ *
+ * If you must load a large amount of data to the tree, this operation is
+ * signifigantly faster than repeated calls to insert, but relies on you using
+ * an appropriate qsort function.
+ *
+ * \param binst Instance to purge and load.
+ * \param keys Array of sorted keys to load.
+ * \param values Array of sorted values to load.
+ * \param count Number of values in the arrays.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_load(sds_bptree_instance *binst, void **keys, void **values, size_t
count);
+
+/* Operations */
+
+/**
+ * Destroy the instance. This frees all remaining values and keys from the structure.
+ * After this is called, the bptree may not be accessed.
+ *
+ * \param binst Instance to destroy.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_destroy(sds_bptree_instance *binst);
+/**
+ * Search the instance for a key. Returns a SDS_KEY_PRESENT or SDS_KEY_NOT_PRESENT.
+ *
+ * \param binst Instance to search.
+ * \param key Key to search for.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_search(sds_bptree_instance *binst, void *key);
+/**
+ * Retrieve the value associated with key from this structure.
+ *
+ * \param binst Instance to retrieve the value from.
+ * \param key Key to search for.
+ * \param target Destination for the value to end up in. May be NULLed even if the
+ * key is not found.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_retrieve(sds_bptree_instance *binst, void *key, void **target);
+/**
+ * Delete a key and value from the tree. This implies that the values are freed
+ * as part of this process.
+ *
+ * \param binst Instance to remove the key and value from.
+ * \param key Key to delete, along with it's data.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_delete(sds_bptree_instance *binst, void *key);
+/**
+ * Insert the key and value to the tree. If the key already exists, this operation
+ * will fail. The key is duplicated on insert, and the duplicated key's life is
bound
+ * to the tree. This allows you to use stack values as keys, as we duplicate them
+ * on insert.
+ *
+ * \param binst Instance to insert into.
+ * \param key Key to insert.
+ * \param value Value to insert associated with key. May be NULL.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_insert(sds_bptree_instance *binst, void *key, void *value);
+/**
+ * Verify the contents of the tree are correct and sane. If checksumming is enabled,
+ * this will validate all checksums. This is generally useful for debugging only
+ * or if you think you have some data issue related to your key comparison function.
+ *
+ * \param binst Instance to verify.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_verify(sds_bptree_instance *binst);
+
+/* Set manipulation */
+
+/**
+ * Map a function over the instance. This does not create a new instance, you
+ * are expected to use the function to hand the data out to some other function.
+ *
+ * WARNING! This function will probably change soon!
+ *
+ * \param binst The instance to map over.
+ * \param fn The function to be applied to each key-value pair.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_map(sds_bptree_instance *binst, void (*fn)(void *k, void *v));
+/**
+ * From instance a, and instance b, create a new insance that contains the
+ * keys and values where keys exist in a or b but not both.
+ *
+ * \param binst_a Instance A
+ * \param binst_b Instance B
+ * \param binst_difference Output for a new instance containing clones of different
elements.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_difference(sds_bptree_instance *binst_a, sds_bptree_instance
*binst_b, sds_bptree_instance **binst_difference);
+/**
+ * Union the sets A and B, and return a new instance that contains keys and values
+ * that are present in either or both.
+ *
+ * \param binst_a Instance A
+ * \param binst_b Instance B
+ * \param binst_union Output for a new instance containing the elements from both sets.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_union(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_union);
+/**
+ * Intersect the sets A and B, and return the elements that are present in both
+ * sets (but not either).
+ *
+ * \param binst_a Instance A
+ * \param binst_b Instance B
+ * \param binst_intersect Output for a new instance containing the elements that are
intersecting.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_intersect(sds_bptree_instance *binst_a, sds_bptree_instance
*binst_b, sds_bptree_instance **binst_intersect);
+/**
+ * Return the set where elements that exist in A but not B only are returned.
+ *
+ * \param binst_a Instance A
+ * \param binst_b Instance B
+ * \param binst_compliment Output for a new instance that contains elements unique to
A.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_compliment(sds_bptree_instance *binst_a, sds_bptree_instance
*binst_b, sds_bptree_instance **binst_compliment);
+/**
+ * Filter the set by applying a predicate, and return a new set containing the matching
elements.
+ *
+ * \param binst_a Instance A
+ * \param fn Predicate to apply. If this function returns 0 the element is excluded. All
other values include the key/value.
+ * \param binst_subset Output for a new instance containing the matched elements.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_filter(sds_bptree_instance *binst_a, int64_t (*fn)(void *k, void
*v), sds_bptree_instance **binst_subset);
+
+/**
+ * @}
+ */
+/* end sds_bptree */
+
+/*
+sds_result sds_bptree_subset(struct sds_bptree_instance *binst_a, struct
sds_bptree_instance *binst_b);
+*/
+
+/** \addtogroup sds_bptree_cow
+ * @{
+ */
+
+// Similar for the COW versions
+
+/**
+ * Initialise an sds cow b+tree for usage. This allocates space for the tree
+ * and bootstraps the initial blank transaction.
+ *
+ * \param binst_ptr The pointer you wish to have filled with the cow b+tree instance.
+ * \param checksumming During DEBUG, this flag enables tree content checksumming.
+ * \param key_cmp_fn Comparison function for keys in the tree.
+ * \param value_free_fn During operation, the values assigned to this tree are owned by
the instance.
+ * This allows us to free the values when required.
+ * \param value_dup_fn During a copy on write operation, we need to be able to copy the
values in the tree.
+ * \param key_free_fn Free the key value
+ * \param key_dup_fn Duplicate a key value
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_init(sds_bptree_cow_instance **binst_ptr, uint16_t
checksumming, int64_t (*key_cmp_fn)(void *a, void *b), void (*value_free_fn)(void *value),
void *(*value_dup_fn)(void *key), void (*key_free_fn)(void *key), void *(*key_dup_fn)(void
*key));
+/**
+ * Destroy an instance. This will destroy all open transactions and free all
+ * tree elements.
+ *
+ * \param binst The cow b+tree to destroy.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_destroy(sds_bptree_cow_instance *binst);
+/**
+ * Verify an instance holds under a number of properties. This should only be used
+ * in debbuging issues. If you find an issue, add it to the test cases!
+ *
+ * \param binst The cow b+tree to verify.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_verify(sds_bptree_cow_instance *binst);
+
+/**
+ * Begin a read only transaction on the tree. This guarantees the memory consistency
+ * of all values in the tree for the duration of the operation.
+ *
+ * \param binst The cow b+tree to start a transaction in.
+ * \param btxn The pointer to a transaction to be allocated.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_rotxn_begin(sds_bptree_cow_instance *binst,
sds_bptree_transaction **btxn);
+/**
+ * Complete a read only transaction. After you have closed the transaction, it may
+ * not be used again. This may trigger a garbage collection. After you close the
+ * transaction, you may NOT reference any elements you viewed in the tree. Given
+ * that there is no penalty to holding this open, just keep the transaction open
+ * til you are sure you do not need the values any longer.
+ *
+ * \param btxn The transaction to close.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_rotxn_close(sds_bptree_transaction **btxn);
+/**
+ * Begin a write transaction. This allows you to alter the content of the tree.
+ * Due to the exclusive nature of this transaction, it is best if you are able
+ * to keep this transaction for the "minimal" time possible as it is
serialised.
+ * Changes made in a write transaction are *guaranteed* to have no impact on any
+ * types currently in a read transaction.
+ *
+ * \param binst The b+tree to begin a write transaction in.
+ * \param btxn The pointer to a location for the transaction to be created into.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_wrtxn_begin(sds_bptree_cow_instance *binst,
sds_bptree_transaction **btxn);
+/**
+ * Abort and roll back the changes made during a write transaction. This operation
+ * is guaranteed safe to all other transactions, including future writes. After the
+ * abort function is called, you must *NOT* access this transaction again.
+ *
+ * \param btxn The transaction to abort and destroy.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_wrtxn_abort(sds_bptree_transaction **btxn);
+/**
+ * Commit the transaction. After this operation, the transaction must not be accessed
+ * again. All changes to the tree are now visible after this call is made, and new
+ * read transactions will have this view. Commit does not affect pre-existing
+ * read transactions data.
+ *
+ * \param btxn The transaction to commit.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_wrtxn_commit(sds_bptree_transaction **btxn);
+
+/**
+ * Search a tree with a valid transaction reference. This returns KEY_PRESENT
+ * or KEY_NOT_PRESENT if the search suceeds or not. Search may operation on a valid
+ * uncommited write transaction, or a read transaction.
+ *
+ * \param btxn The transaction point to search.
+ * \param key The key to search for.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_search(sds_bptree_transaction *btxn, void *key);
+/**
+ * Search a tree with a valid transaction reference. This returns KEY_PRESENT
+ * or KEY_NOT_PRESENT if the search suceeds or not. Additionally, the value
+ * attached to key is placed into the pointer for target. Search may operation on a
valid
+ * uncommited write transaction, or a read transaction.
+ *
+ * \param btxn The transaction point to search.
+ * \param key The key to search for.
+ * \param target The pointer where value will be placed on sucessful search. NULL is a
+ * valid value, so be sure to check the result.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_retrieve(sds_bptree_transaction *btxn, void *key, void
**target);
+/**
+ * Delete key and the associated data from the tree. This operates only on a valid
+ * write transaction, and changes made are not reflected until a commit is made.
+ * existing reads will never see this change til they close and open new transactions.
+ *
+ * \param btxn The write transaction to operate on.
+ * \param key The key to delete, along with associated data. If you have called retrieve
+ * on this key, the key and value must not be accessed after this point within this
+ * transactions lifetime as the values may be invalidated.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_delete(sds_bptree_transaction *btxn, void *key);
+/**
+ * Insert a key and associated value to the tree via a valid write transaction.
+ * values and keys inserted to the tree now completely belong to the tree, and may
+ * be duplicated or freed at any time. After you have given a key and value to the
+ * tree, you must only access them via the retrieve interface in valid scenarios.
+ *
+ * \param btxn The write transaction to operate on.
+ * \param key The key to insert. If a duplicate key is detected, and error is returned.
+ * \param value The value to insert. May be NULL.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_insert(sds_bptree_transaction *btxn, void *key, void *value);
+
+/**
+ * Update a key to have a new associated value within a valid write transaction.
+ * This is more efficient than delete -> insert, so for updates it's preferred.
+ * This is needed in the case that you have previous read transactions, and want
+ * to alter a value, without affecting the read. You would use this by calling
+ * retrieve, copying the value, then calling update on the b+tree.
+ *
+ * \param btxn The write transaction to operate on.
+ * \param key The key to update. If the key does not exist, fall back to insert.
+ * \param value The value to update. May be NULL.
+ * \retval Result of the operation as sds_result.
+ */
+
+sds_result sds_bptree_cow_update(sds_bptree_transaction *btxn, void *key, void *value);
+
+/**
+ * Search atomic functions as search, but implies a single short lived read transaction.
+ *
+ * If you have multiple searches to make, it is better to use a read transaction due to
+ * the memory design of the transaction. Multiple atomics may cause contention on
+ * certain parts of the transaction code.
+ *
+ * \param binst The cow b+tree to search.
+ * \param key The key to search for.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_search_atomic(sds_bptree_cow_instance *binst, void *key);
+/**
+ * Retrieve atomic functions as retrieve, but implies a single short lived read
transaction. Calling this implies that you *must* free tha value returned to you.
+ *
+ * If you have multiple searches to make, it is better to use a read transaction due to
+ * the memory design of the transaction. Multiple atomics may cause contention on
+ * certain parts of the transaction code.
+ *
+ * \param binst The cow b+tree to search.
+ * \param key The key to search for.
+ * \param target The value retrieved. You must free this after use, with the same free
function as the binst holds.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_retrieve_atomic(sds_bptree_cow_instance *binst, void *key, void
**target);
+/**
+ * Delete atomic functions as delete, but implise a single short livied write
transaction, and commit phase.
+ *
+ * If you have multiple searches to make, it is better to use a write transaction due to
+ * the memory design of the transaction. Multiple atomics may cause contention on
+ * certain parts of the transaction code.
+ *
+ * \param binst The cow b+tree to delete from.
+ * \param key The key to delete. This removes the associated value.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_delete_atomic(sds_bptree_cow_instance *binst, void *key);
+/**
+ * Insert atomic functions as insert, but implies a single short lived write transaction
and commit phase.
+ *
+ * If you have multiple searches to make, it is better to use a write transaction due to
+ * the memory design of the transaction. Multiple atomics may cause contention on
+ * certain parts of the transaction code.
+ *
+ * \param binst The cow b+tree to insert to.
+ * \param key The key to insert.
+ * \param value The value to insert associated with key.
+ * \retval Result of the operation as sds_result.
+ */
+sds_result sds_bptree_cow_insert_atomic(sds_bptree_cow_instance *binst, void *key, void
*value);
+
+
+/**
+ * @}
+ */
+/* end sds_bptree_cow */
+
diff --git a/src/libsds/sds/bpt/bpt.c b/src/libsds/sds/bpt/bpt.c
new file mode 100644
index 0000000..e20d0c5
--- /dev/null
+++ b/src/libsds/sds/bpt/bpt.c
@@ -0,0 +1,386 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+
+#include "bpt.h"
+
+sds_result
+sds_bptree_init(sds_bptree_instance **binst_ptr, uint16_t checksumming, int64_t
(*key_cmp_fn)(void *a, void *b), void (*value_free_fn)(void *value), void
(*key_free_fn)(void *key), void *(*key_dup_fn)(void *key) ) {
+ if (binst_ptr == NULL) {
+#ifdef DEBUG
+ sds_log("sds_btree_init", "Invalid pointer");
+#endif
+ return SDS_NULL_POINTER;
+ }
+
+ *binst_ptr = sds_memalign(sizeof(sds_bptree_instance), SDS_CACHE_ALIGNMENT);
+ (*binst_ptr)->print_iter = 0;
+ (*binst_ptr)->offline_checksumming = checksumming;
+ (*binst_ptr)->search_checksumming = checksumming;
+ (*binst_ptr)->key_cmp_fn = key_cmp_fn;
+ (*binst_ptr)->value_free_fn = value_free_fn;
+ (*binst_ptr)->key_free_fn = key_free_fn;
+ (*binst_ptr)->key_dup_fn = key_dup_fn;
+
+ (*binst_ptr)->root = sds_bptree_node_create();
+
+ // Now update the checksums
+#ifdef DEBUG
+ if ((*binst_ptr)->offline_checksumming) {
+ sds_bptree_crc32c_update_node((*binst_ptr)->root);
+ sds_bptree_crc32c_update_instance(*binst_ptr);
+ }
+#endif
+
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_bptree_load(sds_bptree_instance *binst, void **keys, void **values, size_t count) {
+ sds_result result = sds_bptree_map_nodes(binst, binst->root,
sds_bptree_node_destroy);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ /* Convert the sorted arrays to a list of nodes */
+ sds_bptree_node *left_node = sds_bptree_arrays_to_node_list(keys, values, count);
+ /* Now build the tree! */
+ return sds_bptree_node_list_to_tree(binst, left_node);
+}
+
+/* Searching functions */
+
+sds_result
+sds_bptree_search(sds_bptree_instance *binst, void *key) {
+ return sds_bptree_search_internal(binst, binst->root, key);
+}
+
+sds_result
+sds_bptree_retrieve(sds_bptree_instance *binst, void *key, void **target) {
+ return sds_bptree_retrieve_internal(binst, binst->root, key, target);
+}
+
+
+/* Insertion functions */
+
+sds_result
+sds_bptree_insert(sds_bptree_instance *binst, void *key, void *value) {
+
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_node *target_node = NULL;
+ sds_bptree_node *next_node = NULL;
+ void *next_key = key;
+
+#ifdef DEBUG
+ sds_log("sds_bptree_insert", "==> Beginning insert of %d",
key);
+#endif
+
+ /* Check key is valid */
+ if (key == NULL) {
+ return SDS_INVALID_KEY;
+ }
+
+ /* Get the target node, and path to it */
+ result = sds_bptree_search_node(binst, binst->root, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ /* CHECK FOR DUPLICATE KEY HERE. */
+ if (sds_bptree_node_contains_key(binst, target_node, key) == SDS_KEY_PRESENT) {
+ return SDS_DUPLICATE_KEY;
+ }
+
+ /* At this point, the insert will happen. Prepare the key ... */
+ void *insert_key = binst->key_dup_fn(key);
+
+ /* Insert to the leaf. */
+ if (target_node->item_count < SDS_BPTREE_DEFAULT_CAPACITY) {
+ sds_bptree_leaf_insert(binst, target_node, insert_key, value);
+ } else {
+ /* We have to split the leaf now. */
+ next_node = sds_bptree_node_create();
+ next_node->level = target_node->level;
+ next_node->parent = target_node->parent;
+ sds_bptree_leaf_split_and_insert(binst, target_node, next_node, insert_key,
value);
+ /* This is the only duplication we need to do */
+ next_key = binst->key_dup_fn(next_node->keys[0]);
+
+ /* This now walks up the branches and inserts as needed. */
+
+ result = sds_bptree_insert_leaf_node(binst, target_node, next_node, next_key);
+
+ }
+
+#ifdef DEBUG
+ sds_log("sds_bptree_insert", "<== Finishing insert of %d",
key);
+#endif
+
+ return result;
+}
+
+/* Deletion functions */
+
+sds_result
+sds_bptree_delete(sds_bptree_instance *binst, void *key) {
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_node *target_node = NULL;
+ sds_bptree_node *next_node = NULL;
+ sds_bptree_node *deleted_node = NULL;
+
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "==> Beginning delete of %d",
key);
+#endif
+
+ /* Check key is valid */
+ if (key == NULL) {
+ return SDS_INVALID_KEY;
+ }
+
+ /* Get the target node, and path to it */
+ result = sds_bptree_search_node(binst, binst->root, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ /* If an error occured, the search_node cleans up the path. */
+ return result;
+ }
+
+ /* CHECK FOR DUPLICATE KEY HERE. */
+ result = sds_bptree_node_contains_key(binst, target_node, key);
+ if (result != SDS_KEY_PRESENT) {
+ return result;
+ }
+
+ /* First, we handle the leaf. */
+ /* Given this algo, first, delete the required key from the target_node. */
+ sds_bptree_leaf_delete(binst, target_node, key);
+ result = SDS_KEY_PRESENT;
+
+
+ /*
+ * If we are deleteing from a leaf, and the leaf has still got >= HALF - 1
+ * (ie if we have 7 keys, and half is 4, we need 3or more keys to be valid)
+ * then we are complete.
+ *
+ * If we will fall below that amount, and a sibling has >= HALF, we move
+ * one of their elements to us.
+ *
+ * If the sibling has HALF - 1, then we MERGE with them
+ *
+ * If we are the root, and the sum our our children keys is less than
+ * capacity, we remove the root, and compress the children to one root?
+ *
+ */
+
+ /*
+ * Delete is insane, so there are some values running about here.
+ *
+ * key - The key we have been asked to delete.
+ * next_key and next_node - If we have compacted, the key and node to remove..
+ only applies to the parent once.
+ */
+
+ /* Then, check the properties of the node are upheld. If not, work out
+ * what needs doing.
+ */
+ next_node = target_node;
+ target_node = target_node->parent;
+
+ /* Is the node less than half? */
+ if (target_node != NULL && next_node->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Get our siblings. */
+ sds_bptree_node *left = NULL;
+ sds_bptree_node *right = NULL;
+ sds_bptree_node_siblings(next_node, &left, &right);
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", " %p -> %p -> %p", left,
next_node, right);
+ sds_log("sds_bptree_delete", " next_node->item_count =
%d", next_node->item_count);
+ if (right != NULL) {
+ sds_log("sds_bptree_delete", " right->item_count =
%d", right->item_count);
+ }
+ if (left != NULL) {
+ sds_log("sds_bptree_delete", " left->item_count = %d",
left->item_count);
+ }
+#endif
+ if (right != NULL && right->item_count > SDS_BPTREE_HALF_CAPACITY)
{
+ /* Does right have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Right leaf borrow");
+#endif
+ sds_bptree_leaf_right_borrow(binst, next_node, right);
+ /* We setup next_node to be right now, so that key fixing on the path works
*/
+ next_node = right;
+ } else if (left != NULL && left->item_count >
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Left leaf borrow");
+#endif
+ sds_bptree_leaf_left_borrow(binst, left, next_node);
+ } else if (right != NULL && right->item_count <=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does right want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Right leaf contract");
+#endif
+ sds_bptree_leaf_compact(binst, next_node, right);
+ /* Setup the branch delete properly */
+ deleted_node = right;
+ } else if (left != NULL && left->item_count <=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Left leaf contract");
+#endif
+ sds_bptree_leaf_compact(binst, left, next_node);
+ deleted_node = next_node;
+ } else {
+ /* Mate, if you get here you are fucked. */
+ return SDS_INVALID_NODE;
+ }
+
+
+ /************** Now start on the branches ****************/
+
+ while (target_node != NULL) {
+
+ /*
+ * We need to ensure that the key we are deleting is NOT in any other node
on
+ * the path. If it is, we must replace it, by the rules of the B+Tree.
+ */
+
+ /*
+ * Dear william of the future.
+ * The reason you do not need to perform a key fixup on leaf or branch
delete
+ * is because you only do a leaf compact to the left: So if you have:
+ * [ A ]
+ * [ 1 2 ]
+ * / \
+ * [ B ] [ C ]
+ * No MATTER what you delete from, B or C, B, will ALWAYS survive, and C
will
+ * be deleted. This means you DON'T need a keyfix in A -> B, because
everything
+ * merged in MUST be greater. It solves the problem by never creating it.
+ *
+ * PS: Get Char more snacks <3
+ */
+
+ if (deleted_node != NULL) {
+ /* Make sure we delete this value from our branch */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Should be removing %p from
branch %p here!", deleted_node, target_node);
+#endif
+ sds_bptree_branch_delete(binst, target_node, deleted_node);
+ /* The deed is done, remove the next reference */
+ deleted_node = NULL;
+ } else {
+ /* It means a borrow was probably done somewhere, so we need to fix the
path */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Should be fixing %p key to
child %p here!", target_node, next_node);
+#endif
+ sds_bptree_branch_key_fixup(binst, target_node, next_node);
+ }
+ /* Then, check the properties of the node are upheld. If not, work out
+ * what needs doing.
+ */
+ next_node = target_node;
+ target_node = target_node->parent;
+
+ /* IMPROVE THIS!!! This could allow a key to bounce back and forth.
+ * Instead, check if node < half and node->item_count +
right/left->item_count < MAX
+ */
+
+ /* Is the node less than half? */
+ if (target_node != NULL && next_node->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Get our siblings. */
+ sds_bptree_node *left = NULL;
+ sds_bptree_node *right = NULL;
+ sds_bptree_node_siblings(next_node, &left, &right);
+ /* Note the conditions for HALF_CAPACITY change here due to
+ * space requirements. we only merge if < half. IE for 7 keys
+ * we have 8 links. If we have a node at 3 keys, and one at 4 keys
+ * they have 4 and 5 links each. Too many! So we have to delay
+ * the merge by a fraction, to allow space for 3 keys and 3 keys.
+ *
+ */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", " %p -> %p -> %p",
left, next_node, right);
+ sds_log("sds_bptree_delete", " next_node->item_count =
%d", next_node->item_count);
+ if (right != NULL) {
+ sds_log("sds_bptree_delete", " right->item_count =
%d", right->item_count);
+ }
+ if (left != NULL) {
+ sds_log("sds_bptree_delete", " left->item_count =
%d", left->item_count);
+ }
+#endif
+
+ if (right != NULL && right->item_count >=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does right have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Right branch
borrow");
+#endif
+ sds_bptree_branch_right_borrow(binst, next_node, right);
+ /* We setup next_node to be right now, so that key fixing on the path
works */
+ next_node = right;
+ } else if (left != NULL && left->item_count >=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Left branch
borrow");
+#endif
+ sds_bptree_branch_left_borrow(binst, left, next_node);
+ } else if (right != NULL && right->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does right want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Right branch
contract");
+#endif
+ sds_bptree_branch_compact(binst, next_node, right);
+ /* Setup the branch delete properly */
+ next_node = right;
+ deleted_node = right;
+ } else if (left != NULL && left->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Left branch
contract");
+#endif
+ sds_bptree_branch_compact(binst, left, next_node);
+ deleted_node = next_node;
+ } else {
+ /* Mate, if you get here you are fucked. */
+ return SDS_INVALID_NODE;
+ }
+ } else if (target_node == NULL && next_node->item_count == 0) {
+ /* It's time to compact the root! */
+ /* We only have one child at this point, so they become the new root */
+#ifdef DEBUG
+ sds_log("sds_bptree_delete", "Should be deleting root
here!");
+ if (binst->root != next_node) {
+ result = SDS_UNKNOWN_ERROR;
+ goto fail;
+ }
+#endif
+ sds_bptree_root_promote(binst, next_node);
+ }
+
+ } // While target node
+ } // If under half capacity
+
+#ifdef DEBUG
+fail:
+ sds_log("sds_bptree_insert", "<== Finishing delete of %d",
key);
+#endif
+
+ return result;
+}
+
+
+sds_result
+sds_bptree_destroy(sds_bptree_instance *binst) {
+ // Remove all the other elements
+ sds_result result = SDS_SUCCESS;
+ result = sds_bptree_map_nodes(binst, binst->root, sds_bptree_node_destroy);
+ // Finally remove the binst
+ sds_free(binst);
+ return result;
+}
+
+
diff --git a/src/libsds/sds/bpt/bpt.h b/src/libsds/sds/bpt/bpt.h
new file mode 100644
index 0000000..6c429f7
--- /dev/null
+++ b/src/libsds/sds/bpt/bpt.h
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#pragma once
+
+#include "../sds_internal.h"
+#include <sds.h>
+
+#ifdef DEBUG
+sds_result sds_bptree_crc32c_verify_instance(sds_bptree_instance *binst);
+void sds_bptree_crc32c_update_instance(sds_bptree_instance *binst);
+sds_result sds_bptree_crc32c_verify_node(sds_bptree_node *node);
+void sds_bptree_crc32c_update_node(sds_bptree_node *node);
+/*
+sds_result sds_bptree_crc32c_verify_value(struct sds_bptree_value *value);
+void sds_bptree_crc32c_update_value(struct sds_bptree_value *value);
+*/
+#endif
+
+/* Node manipulation */
+
+sds_bptree_node *sds_bptree_arrays_to_node_list(void **keys, void **values, size_t
count);
+sds_result sds_bptree_node_list_to_tree(sds_bptree_instance *binst, sds_bptree_node
*node);
+sds_bptree_node * sds_bptree_node_create(void);
+sds_result sds_bptree_node_destroy(sds_bptree_instance *binst, sds_bptree_node *node);
+sds_result sds_bptree_node_contains_key(sds_bptree_instance *binst, sds_bptree_node
*node, void *key);
+size_t sds_bptree_node_key_eq_index(sds_bptree_instance *binst, sds_bptree_node *node,
void *key);
+size_t sds_bptree_node_key_lt_index(sds_bptree_instance *binst, sds_bptree_node *node,
void *key);
+void sds_bptree_node_siblings(sds_bptree_node *target, sds_bptree_node **left,
sds_bptree_node **right);
+sds_result sds_bptree_node_retrieve_key(sds_bptree_instance *binst, sds_bptree_node
*node, void *key, void **target);
+void sds_bptree_node_node_replace(sds_bptree_node *target_node, sds_bptree_node
*origin_node, sds_bptree_node *replace_node);
+/*
+sds_result sds_bptree_value_create(struct sds_bptree_instance *binst, void *value, size_t
value_size, struct sds_bptree_value **new_value);
+*/
+
+/* Search and retrieve */
+sds_result sds_bptree_search_node(sds_bptree_instance *binst, sds_bptree_node *root, void
*key, sds_bptree_node** target_out_node);
+sds_result sds_bptree_search_internal(sds_bptree_instance *binst, sds_bptree_node *root,
void *key);
+sds_result sds_bptree_retrieve_internal(sds_bptree_instance *binst, sds_bptree_node
*root, void *key, void **target);
+
+void * sds_bptree_node_leftmost_child_key(sds_bptree_node *parent);
+
+/* Leaf insert and delete */
+
+void sds_bptree_leaf_insert(sds_bptree_instance *binst, sds_bptree_node *node, void *key,
void *new_value);
+void sds_bptree_leaf_split_and_insert(sds_bptree_instance *binst, sds_bptree_node
*left_node, sds_bptree_node *right_node, void *key, void *new_value);
+void sds_bptree_leaf_compact(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right);
+void sds_bptree_leaf_delete(sds_bptree_instance *binst, sds_bptree_node *node, void
*key);
+void sds_bptree_leaf_right_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right);
+void sds_bptree_leaf_left_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right);
+
+/* Branch insert and delete */
+
+sds_result sds_bptree_insert_leaf_node(sds_bptree_instance *binst, sds_bptree_node
*tnode, sds_bptree_node *nnode, void *nkey);
+void sds_bptree_branch_split_and_insert(sds_bptree_instance *binst, sds_bptree_node
*left_node, sds_bptree_node *right_node, void *key, sds_bptree_node *new_node, void
**excluded_key);
+void sds_bptree_branch_insert(sds_bptree_instance *binst, sds_bptree_node *node, void
*key, sds_bptree_node *new_node);
+void sds_bptree_branch_delete(sds_bptree_instance *binst, sds_bptree_node *node,
sds_bptree_node *delete_node);
+void sds_bptree_branch_key_fixup(sds_bptree_instance *binst, sds_bptree_node *parent,
sds_bptree_node *child);
+void sds_bptree_branch_compact(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right);
+void sds_bptree_branch_right_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right);
+void sds_bptree_branch_left_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right);
+
+/* Root management */
+void sds_bptree_root_promote(sds_bptree_instance *binst, sds_bptree_node *root);
+void sds_bptree_root_insert(sds_bptree_instance *binst, sds_bptree_node *left_node,
sds_bptree_node *right_node, void *key);
+
+/* Node path tracking */
+
+void sds_bptree_node_list_push(sds_bptree_node_list **list, sds_bptree_node *node);
+sds_bptree_node * sds_bptree_node_list_pop(sds_bptree_node_list **list);
+void sds_bptree_node_list_release(sds_bptree_node_list **list);
+sds_bptree_node *sds_bptree_node_min(sds_bptree_instance *binst);
+
+/* Set list operators */
+
+void sds_bptree_node_list_append(sds_bptree_node **node, void *key, void *value);
+
+/* Internal */
+
+sds_result sds_bptree_map_nodes(sds_bptree_instance *binst, sds_bptree_node *root,
sds_result (*fn)(sds_bptree_instance *binst, sds_bptree_node *));
+sds_result sds_bptree_display(sds_bptree_instance *binst);
+
+/* Verification */
+sds_result sds_bptree_verify_node(sds_bptree_instance *binst, sds_bptree_node *node);
+
+
diff --git a/src/libsds/sds/bpt/common.c b/src/libsds/sds/bpt/common.c
new file mode 100644
index 0000000..05b748e
--- /dev/null
+++ b/src/libsds/sds/bpt/common.c
@@ -0,0 +1,788 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt.h"
+
+/* Node manipulation functions */
+sds_bptree_node *
+sds_bptree_node_create() {
+ sds_bptree_node *node = sds_memalign(sizeof(sds_bptree_node), SDS_CACHE_ALIGNMENT);
+ // Without memset, we need to null the max link in a value
+ node->values[SDS_BPTREE_DEFAULT_CAPACITY] = NULL;
+ node->level = 0;
+ node->item_count = 0;
+ node->parent = NULL;
+ node->txn_id = 0;
+
+#ifdef DEBUG
+ printf("sds_bptree_node_create: Creating node_%p item_count=%d\n", node,
node->item_count);
+#endif
+
+ return node;
+}
+
+
+sds_result
+sds_bptree_node_destroy(sds_bptree_instance *binst, sds_bptree_node *node) {
+#ifdef DEBUG
+ sds_log("sds_bptree_node_destroy", "Freeing node_%p", node);
+#endif
+ for (size_t i = 0; i < node->item_count; i += 1) {
+ if (node->level == 0) {
+ void *value = node->values[i];
+ if (value != NULL && binst->value_free_fn != NULL) {
+ binst->value_free_fn(value);
+ }
+ }
+ binst->key_free_fn(node->keys[i]);
+ }
+
+ sds_free(node);
+ // Since we updated the node id.
+#ifdef DEBUG
+ // binst->node_count -= 1;
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_instance(binst);
+ }
+#endif
+ return SDS_SUCCESS;
+}
+
+size_t
+sds_bptree_node_key_lt_index(sds_bptree_instance *binst, sds_bptree_node *node, void
*key) {
+ /* This is a very busy part of the code. */
+ size_t index = 0;
+ for (;index < node->item_count; index++) {
+ if (binst->key_cmp_fn(key, node->keys[index]) < 0) {
+ return index;
+ }
+ }
+ return index;
+}
+
+size_t
+sds_bptree_node_key_eq_index(sds_bptree_instance *binst, sds_bptree_node *node, void
*key) {
+ size_t index = 0;
+ int64_t result = 0;
+ for (;index < node->item_count; index++) {
+ result = binst->key_cmp_fn(key, node->keys[index]);
+ if (result == 0) {
+ return index;
+ } else if (result < 0) {
+ /* Short cut out if we don't exist */
+ return node->item_count;
+ }
+ }
+ return index;
+}
+
+inline static size_t __attribute__((always_inline))
+sds_bptree_node_node_index(sds_bptree_node *parent, sds_bptree_node *child) {
+ size_t index = 0;
+ /* GCC has an issue where uint16_t + something becomes signed ... fml */
+ for (; index < (size_t)(parent->item_count + 1); index++) {
+ if (parent->values[index] == child) {
+ return index;
+ }
+ }
+ return index;
+}
+
+// How can we make this handle errors safely?
+void
+sds_bptree_node_node_replace(sds_bptree_node *target_node, sds_bptree_node *origin_node,
sds_bptree_node *replace_node) {
+#ifdef DEBUG
+ printf("sds_bptree_node_node_replace: Replace node_%p to overwrite node_%p in
node_%p\n", origin_node, replace_node, target_node);
+#endif
+ size_t index = sds_bptree_node_node_index(target_node, origin_node);
+ target_node->values[index] = replace_node;
+ replace_node->parent = target_node;
+}
+
+void *
+sds_bptree_node_leftmost_child_key(sds_bptree_node *parent) {
+ sds_bptree_node *right_prime = parent;
+ while (right_prime->level > 0) {
+ right_prime = (sds_bptree_node *)right_prime->values[0];
+ }
+ return right_prime->keys[0];
+}
+
+sds_result
+sds_bptree_node_contains_key(sds_bptree_instance *binst, sds_bptree_node *node, void
*key) {
+ /* Very busy part of the code. Could be improved? */
+ if (sds_bptree_node_key_eq_index(binst, node, key) != node->item_count) {
+ return SDS_KEY_PRESENT;
+ }
+ return SDS_KEY_NOT_PRESENT;
+}
+
+sds_result
+sds_bptree_node_retrieve_key(sds_bptree_instance *binst, sds_bptree_node *node, void
*key, void **target) {
+ size_t index = sds_bptree_node_key_eq_index(binst, node, key);
+
+ if (index == node->item_count) {
+ return SDS_KEY_NOT_PRESENT;
+ }
+ *target = node->values[index];
+ return SDS_KEY_PRESENT;
+}
+
+sds_bptree_node *
+sds_bptree_node_min(sds_bptree_instance *binst) {
+ sds_bptree_node *work_node = binst->root;
+ while (work_node->level > 0) {
+ work_node = (sds_bptree_node *)work_node->values[0];
+ }
+ return work_node;
+}
+
+void
+sds_bptree_node_list_append(sds_bptree_node **node, void *key, void *value) {
+ if ((*node)->item_count >= SDS_BPTREE_DEFAULT_CAPACITY) {
+ sds_bptree_node *new_work_node = NULL;
+ new_work_node = sds_bptree_node_create();
+ (*node)->values[SDS_BPTREE_DEFAULT_CAPACITY] = (void *)new_work_node;
+ (*node) = new_work_node;
+ }
+ (*node)->keys[(*node)->item_count] = key;
+ (*node)->values[(*node)->item_count] = value;
+ (*node)->item_count++;
+}
+
+sds_bptree_node *
+sds_bptree_arrays_to_node_list(void **keys, void **values, size_t count) {
+ /* Allocate the work node. It's first, so make it the left too. */
+ sds_bptree_node *left_node = sds_bptree_node_create();
+ sds_bptree_node *work_node = left_node;
+
+ for (size_t index = 0; index < count; index++) {
+ if (values != NULL) {
+ sds_bptree_node_list_append(&work_node, keys[index], values[index]);
+ } else {
+ sds_bptree_node_list_append(&work_node, keys[index], NULL);
+ }
+ }
+ return left_node;
+}
+
+sds_result
+sds_bptree_node_list_to_tree(sds_bptree_instance *binst, sds_bptree_node *node) {
+ /* First, push all our nodes to a list. This also counts them. */
+ sds_bptree_node *next_node = (sds_bptree_node
*)node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ sds_bptree_node *target_node = node;
+ void *next_key = NULL;
+
+ binst->root = node;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(node);
+ sds_bptree_crc32c_update_instance(binst);
+ }
+#endif
+ /* We actually have a list of nodes. Better start inserting! */
+ while (next_node != NULL) {
+ next_node->parent = target_node->parent;
+ next_key = binst->key_dup_fn(next_node->keys[0]);
+ sds_bptree_insert_leaf_node(binst, target_node, next_node, next_key);
+
+ target_node = next_node;
+ next_node = (sds_bptree_node
*)next_node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ }
+ return SDS_SUCCESS;
+}
+
+void
+sds_bptree_leaf_insert(sds_bptree_instance *binst, sds_bptree_node *node, void *key, void
*new_value) {
+ /* This is called when you know you have space already */
+#ifdef DEBUG
+ sds_log("sds_bptree_leaf_insert", "node_%p key %" PRIu64 "
", node, key);
+#endif
+ size_t index = sds_bptree_node_key_lt_index(binst, node, key);
+ // Move everything else to the right
+ if (node->item_count > 0) {
+ for (size_t i = node->item_count; i > index; i--) {
+ node->keys[i] = node->keys[i - 1];
+ node->values[i] = node->values[i - 1];
+ }
+ }
+ // Insert
+ node->keys[index] = key;
+ node->values[index] = new_value;
+ node->item_count = node->item_count + 1;
+
+ // Update the checksum.
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(node);
+ }
+#endif
+
+}
+
+sds_result
+sds_bptree_insert_leaf_node(sds_bptree_instance *binst, sds_bptree_node *tnode,
sds_bptree_node *nnode, void *nkey) {
+ sds_bptree_node *target_node = tnode;
+ sds_bptree_node *next_node = nnode;
+ sds_bptree_node *right_node = NULL;
+ void *next_key = nkey;
+ /* Now work up the path to the branches. */
+ /* The path != NULL is important, as if we are the root, we want to make a new root!
*/
+ /*
+ * How does this magic work? Well, because we did a split and insert, we now
+ * have a new_node, but it's not in it's parent yet! IE
+ * [ Parent ]
+ * /
+ * [ target_node ] -> [ new_node ]
+ * Were target node the root, we would just create the new root, stick the
+ * values in and be done. But we need to get this into parent.
+ * So by leaving new_node in that spot, the while loop triggers and we
+ * try again with the new_node->keys[0] as the key, and new_node as
+ * as the insertion target.
+ */
+
+ while (target_node->parent != NULL && next_node != NULL &&
next_key != NULL) {
+ target_node = target_node->parent;
+ /* Does the target have space? If so insert! */
+ if (target_node->item_count < SDS_BPTREE_DEFAULT_CAPACITY) {
+ // Insert the value in the correct place.
+ /* It's a branch, and we are updating to say we have a new child */
+ sds_bptree_branch_insert(binst, target_node, next_key, next_node);
+ /* Done! Say we are. In theory, next_node is already null, but lets be
explicit */
+ next_node = NULL;
+ next_key = NULL;
+ } else {
+ /* If the target doesn't have space, split, then insert */
+
+ /*
+ * So we have the current target_node, and we will split to the right
+ * some new node. So we create the new node, then move half our
+ * values to it.
+ *
+ * Now, we put our next_key and value *or* new_node into the old node
+ * or the new node.
+ */
+
+ /* create new node */
+ right_node = sds_bptree_node_create();
+ right_node->level = target_node->level;
+ right_node->parent = target_node->parent;
+
+ /* The way we split a branch is a bit different for link management. */
+ void *copy_key = next_key;
+ sds_bptree_branch_split_and_insert(binst, target_node, right_node, copy_key,
next_node, &next_key);
+
+ next_node = right_node;
+ /* don't alter next key here! This is set by branchnodesplit */
+ }
+
+
+ }
+ /* Done looping, but we still have to insert the right_node we just made..
+ * means we need a new root!
+ */
+
+ if (next_node != NULL) {
+ /*
+ * If we are the root, we need to make a new root now, and add our values.
+ */
+ sds_bptree_root_insert(binst, target_node, next_node, next_key);
+ }
+ return SDS_SUCCESS;
+}
+
+void
+sds_bptree_branch_insert(sds_bptree_instance *binst, sds_bptree_node *node, void *key,
sds_bptree_node *new_node) {
+ /* Remember, we already checked for duplicate keys! */
+#ifdef DEBUG
+ sds_log("sds_bptree_branch_insert", "new_node %p key %" PRIu64
" to node %p", new_node, key, node);
+#endif
+
+ /* !!!!! STARTING TO CHANGE THE NODE !!!!!! */
+ /* Find where our key belongs.
+ * Now, we should always have space, but there is a different condition
+ * for a branch compared to a leaf. If we have:
+ * [ 2, 6, 8, NULL ]
+ * If we are inserting 10, we need to put the new_node to the *RIGHT*.
+ * If our key is LESS, then we insert to the LEFT.
+ */
+ size_t index = sds_bptree_node_key_lt_index(binst, node, key);
+
+ // If index == node->item_count, nothing will move.
+ if (node->item_count > 0) {
+ for (size_t i = node->item_count; i > index; i--) {
+ node->keys[i] = node->keys[i - 1];
+ // Copy the value to the RIGHT of the key ....
+ node->values[i + 1] = node->values[i];
+ }
+ }
+
+ /* sds_bptree_node *current_min_node = (sds_bptree_node *)node->values[0];
+ if (index > 0 || binst->key_cmp_fn(current_min_node->keys[0],
new_node->keys[0]) < 0) {
+ */
+ // node->keys[index] = binst->key_dup_fn(key);
+ /* This key is already duplicated. */
+ node->keys[index] = key;
+ node->values[index + 1] = (void *)new_node;
+ node->item_count += 1;
+ new_node->parent = node;
+
+ // Update the checksum.
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(node);
+ sds_bptree_crc32c_update_node(new_node);
+ }
+#endif
+
+}
+
+void
+sds_bptree_leaf_split_and_insert(sds_bptree_instance *binst, sds_bptree_node *left_node,
sds_bptree_node *right_node, void *key, void *new_value) {
+ /* Remember, we already checked for duplicate keys! */
+#ifdef DEBUG
+ sds_log("sds_bptree_leaf_split_and_insert", "left %p -> right %p
key %" PRIu64 " ", left_node, right_node, key);
+#endif
+
+ /* !!!!! STARTING TO CHANGE THE NODE !!!!!! */
+ /* Right node is always new! */
+ /* Move values to it */
+ /* This has a cost of average 3. */
+ for (size_t i = 0; i < (SDS_BPTREE_HALF_CAPACITY - 1); i++) {
+ /* Is memcpy faster? */
+ right_node->keys[i] = left_node->keys[i + SDS_BPTREE_HALF_CAPACITY];
+ right_node->values[i] = left_node->values[i + SDS_BPTREE_HALF_CAPACITY];
+ right_node->item_count += 1;
+ // Now clear the values out.
+ left_node->keys[i + SDS_BPTREE_HALF_CAPACITY] = NULL;
+ left_node->values[i + SDS_BPTREE_HALF_CAPACITY] = NULL;
+ left_node->item_count -= 1;
+ }
+
+ /* Fix the linked list pointers */
+ right_node->values[SDS_BPTREE_DEFAULT_CAPACITY] =
left_node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ left_node->values[SDS_BPTREE_DEFAULT_CAPACITY] = (void *)right_node;
+
+ /* Pick the node we need to insert to */
+ if (binst->key_cmp_fn(key, right_node->keys[0]) >= 1) {
+ /* Insert to the right */
+ // Update the checksum.
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left_node);
+ }
+#endif
+ sds_bptree_leaf_insert(binst, right_node, key, new_value);
+ } else {
+ /* Insert to the left */
+ // Update the checksum.
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(right_node);
+ }
+#endif
+ sds_bptree_leaf_insert(binst, left_node, key, new_value);
+ }
+}
+
+void
+sds_bptree_branch_split_and_insert(sds_bptree_instance *binst, sds_bptree_node
*left_node, sds_bptree_node *right_node, void *key, sds_bptree_node *new_node, void
**excluded_key) {
+ /* !!!!! STARTING TO CHANGE THE NODE !!!!!! */
+ /* Right node is always new! */
+ sds_bptree_node *rchild = NULL;
+#ifdef DEBUG
+ sds_log("sds_bptree_branch_split_and_insert", "left %p -> right %p
key %" PRIu64 "", left_node, right_node, key);
+#endif
+
+ /* We are left!
+ * In a left case, we move half the nodes over like a normal insert and split.
+ * Then we remove the "last" node
+ */
+ /* Has a cost of about 4 times per call? */
+ for (size_t i = 0; i < (SDS_BPTREE_HALF_CAPACITY - 1); i++) {
+ right_node->keys[i] = left_node->keys[i + SDS_BPTREE_HALF_CAPACITY];
+ // Move the node pointer to the right ...
+ right_node->values[i + 1] = left_node->values[i + SDS_BPTREE_HALF_CAPACITY
+ 1];
+ // Reset the parent for the right nodes.
+ rchild = (sds_bptree_node *)right_node->values[i + 1];
+ rchild->parent = right_node;
+ right_node->item_count += 1;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(rchild);
+ }
+#endif
+ // Now clear the values out.
+ left_node->keys[i + SDS_BPTREE_HALF_CAPACITY] = NULL;
+ left_node->values[i + SDS_BPTREE_HALF_CAPACITY + 1] = NULL;
+ left_node->item_count -= 1;
+ }
+ /* Finally, move the last pointer from the tail of left to right */
+ right_node->values[0] = left_node->values[SDS_BPTREE_HALF_CAPACITY];
+ rchild = (sds_bptree_node *)right_node->values[0];
+ rchild->parent = right_node;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(rchild);
+ }
+#endif
+ left_node->values[SDS_BPTREE_HALF_CAPACITY] = NULL;
+ /* And null the excluded key. */
+ /* The excluded key will have been duped from insert, so we are just pushing
+ * it up the tree at this point. No need to dup!
+ */
+ *excluded_key = left_node->keys[SDS_BPTREE_HALF_CAPACITY - 1];
+#ifdef DEBUG
+ sds_log("sds_bptree_branch_split_and_insert", "excluding %d",
*excluded_key);
+#endif
+ left_node->keys[SDS_BPTREE_HALF_CAPACITY - 1] = NULL;
+ left_node->item_count -= 1;
+
+ if (binst->key_cmp_fn(key, *excluded_key) < 0) {
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(right_node);
+ }
+#endif
+ /* Now trigger the insert to the left node. */
+ sds_bptree_branch_insert(binst, left_node, key, new_node);
+ } else {
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left_node);
+ }
+#endif
+ /* Now trigger the insert to the left node. */
+ sds_bptree_branch_insert(binst, right_node, key, new_node);
+ }
+}
+
+void
+sds_bptree_root_insert(sds_bptree_instance *binst, sds_bptree_node *left_node,
sds_bptree_node *right_node, void *key) {
+#ifdef DEBUG
+ sds_log("sds_bptree_root_insert", "left_node %p, key %d, right_node
%p", left_node, key, right_node);
+#endif
+ sds_bptree_node *root_node = sds_bptree_node_create();
+ root_node->level = left_node->level + 1;
+
+ /* Is already duplicated */
+ root_node->keys[0] = key;
+ root_node->values[0] = (void *)left_node;
+ root_node->values[1] = (void *)right_node;
+ root_node->item_count = 1;
+ root_node->parent = NULL;
+ left_node->parent = root_node;
+ right_node->parent = root_node;
+ binst->root = root_node;
+ // Update the checksum.
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(root_node);
+ sds_bptree_crc32c_update_node(left_node);
+ sds_bptree_crc32c_update_node(right_node);
+ sds_bptree_crc32c_update_instance(binst);
+ }
+#endif
+}
+
+void
+sds_bptree_leaf_delete(sds_bptree_instance *binst, sds_bptree_node *node, void *key) {
+#ifdef DEBUG
+ sds_log("sds_bptree_leaf_delete", "deleting %d from %p", key,
node);
+#endif
+ /* Find the value */
+ size_t index = sds_bptree_node_key_eq_index(binst, node, key);
+
+ /* extract the contents (if any) */
+ void *value = node->values[index];
+ if (value != NULL) {
+ binst->value_free_fn(value);
+ }
+ /* Delete the key + value */
+ binst->key_free_fn(node->keys[index]);
+ /* Move remaining values and keys into place. */
+ /* nearly a 5x cost in this call.... */
+ node->item_count = node->item_count - 1;
+ for (; index < (node->item_count); index++) {
+ node->keys[index] = node->keys[index + 1];
+ node->values[index] = node->values[index + 1];
+ }
+ /* Finally, NULL the left over two values. */
+ node->keys[index] = NULL;
+ node->values[index] = NULL;
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(node);
+ }
+#endif
+}
+
+void
+sds_bptree_leaf_compact(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right) {
+
+ /* Has a 5x cost */
+ for (size_t i = 0; i < right->item_count; i++) {
+ left->keys[left->item_count] = right->keys[i];
+ left->values[left->item_count] = right->values[i];
+ left->item_count++;
+ right->keys[i] = NULL;
+ right->values[i] = NULL;
+ }
+ left->values[SDS_BPTREE_DEFAULT_CAPACITY] =
right->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ right->item_count = 0;
+ sds_bptree_node_destroy(binst, right);
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ }
+#endif
+}
+
+void
+sds_bptree_leaf_right_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right) {
+ /* Take from the right node, and put into the left. */
+ left->keys[left->item_count] = right->keys[0];
+ left->values[left->item_count] = right->values[0];
+ left->item_count += 1;
+#ifdef DEBUG
+ assert(right->item_count > 0);
+#endif
+ for (size_t i = 0; i < (size_t)(right->item_count - 1); i++) {
+ right->keys[i] = right->keys[i + 1];
+ right->values[i] = right->values[i + 1];
+ }
+ right->item_count -= 1;
+ right->keys[right->item_count] = NULL;
+ right->values[right->item_count] = NULL;
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ sds_bptree_crc32c_update_node(right);
+ }
+#endif
+}
+
+void
+sds_bptree_leaf_left_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right) {
+ /* Take a node from the left and put it into the right. */
+ for (size_t i = right->item_count; i > 0; i--) {
+ right->keys[i] = right->keys[i - 1];
+ right->values[i] = right->values[i - 1];
+ }
+ left->item_count -= 1;
+ right->item_count += 1;
+ right->keys[0] = left->keys[left->item_count];
+ right->values[0] = left->values[left->item_count];
+ left->keys[left->item_count] = NULL;
+ left->values[left->item_count] = NULL;
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ sds_bptree_crc32c_update_node(right);
+ }
+#endif
+}
+
+void
+sds_bptree_root_promote(sds_bptree_instance *binst, sds_bptree_node *root) {
+ /* Current root is empty! We have one child, so we promote them. */
+ binst->root = (sds_bptree_node *)root->values[0];
+ sds_bptree_node_destroy(binst, root);
+ binst->root->parent = NULL;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(binst->root);
+ sds_bptree_crc32c_update_instance(binst);
+ }
+#endif
+}
+
+void
+sds_bptree_branch_delete(sds_bptree_instance *binst, sds_bptree_node *node,
sds_bptree_node *delete_node) {
+
+ /* This deletes by NODE not by KEY!!!! */
+ size_t index = sds_bptree_node_node_index(node, delete_node);
+
+ /* Most of the times, keys / values right associate. IE index key -> value + 1 */
+ binst->key_free_fn(node->keys[index - 1]);
+
+ /* Have to move everything down. */
+ /* Remember, we belong to key index - 1 */
+ for (; index < node->item_count; index++) {
+ node->keys[index - 1] = node->keys[index];
+ node->values[index] = node->values[index + 1];
+ }
+ /* Finally, null the last item. */
+ node->item_count-- ;
+ node->keys[node->item_count] = NULL;
+ node->values[node->item_count + 1] = NULL;
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(node);
+ }
+#endif
+}
+
+void
+sds_bptree_node_siblings(sds_bptree_node *target, sds_bptree_node **left, sds_bptree_node
**right) {
+ /* Searches for a NODE not a KEY */
+ size_t index = sds_bptree_node_node_index(target->parent, target);
+
+ if (index == 0) {
+ *left = NULL;
+ *right = target->parent->values[index + 1];
+ } else if (index == target->parent->item_count) {
+ *left = target->parent->values[index - 1];
+ *right = NULL;
+ } else {
+ *left = target->parent->values[index - 1];
+ *right = target->parent->values[index + 1];
+ }
+
+}
+
+void
+sds_bptree_branch_key_fixup(sds_bptree_instance *binst, sds_bptree_node *parent,
sds_bptree_node *child) {
+ /* Find the index of child in parent. */
+ size_t index = sds_bptree_node_node_index(parent, child);
+ /* We are the parent of child at some point. We need to fix our key relationship to
them. */
+ if (index == 0) {
+ /* Left children never need key updates. */
+ return;
+ }
+ /* Now, the index is one to the left, so sub 1. */
+ /* IE [ 1 2 3 4 ] */
+ /* [ A B C D E ] */
+ /* So node B, index is 1, but key is 1, so one less */
+ index--;
+ binst->key_free_fn(parent->keys[index]);
+ parent->keys[index] =
binst->key_dup_fn(sds_bptree_node_leftmost_child_key(child));
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(parent);
+ }
+#endif
+}
+
+void
+sds_bptree_branch_compact(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right) {
+ sds_bptree_node *rchild = NULL;
+ /* Merge the right to the left. */
+ /* We have to create the missing intermediate key. */
+ left->keys[left->item_count] =
binst->key_dup_fn(sds_bptree_node_leftmost_child_key(right));
+
+ rchild = (sds_bptree_node *)right->values[0];
+ rchild->parent = left;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(rchild);
+ }
+#endif
+ left->values[left->item_count + 1] = rchild;
+ right->values[0] = NULL;
+ left->item_count++;
+
+ /* Has about a 3 times impact */
+ for (size_t i = 0; i < right->item_count; i++) {
+ left->keys[left->item_count] = right->keys[i];
+ rchild = (sds_bptree_node *)right->values[i + 1];
+ rchild->parent = left;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(rchild);
+ }
+#endif
+ left->values[left->item_count + 1] = rchild;
+ left->item_count++;
+ right->keys[i] = NULL;
+ right->values[i + 1] = NULL;
+ }
+
+ right->item_count = 0;
+ sds_bptree_node_destroy(binst, right);
+
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ }
+#endif
+}
+
+void
+sds_bptree_branch_right_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right) {
+ sds_bptree_node *rchild = NULL;
+ /* Take from the right, and give to the left. */
+ left->keys[left->item_count] =
binst->key_dup_fn(sds_bptree_node_leftmost_child_key(right));
+ binst->key_free_fn(right->keys[0]);
+
+ rchild = (sds_bptree_node *)right->values[0];
+ rchild->parent = left;
+ left->values[left->item_count + 1] = rchild;
+ left->item_count += 1;
+#ifdef DEBUG
+ assert(right->item_count > 0);
+#endif
+ for (size_t i = 0; i < (size_t)(right->item_count - 1); i++) {
+ right->keys[i] = right->keys[i + 1];
+ right->values[i] = right->values[i + 1];
+ }
+ right->values[right->item_count -1] = right->values[right->item_count];
+
+ right->item_count -= 1;
+ right->keys[right->item_count] = NULL;
+ right->values[right->item_count + 1] = NULL;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ sds_bptree_crc32c_update_node(right);
+ sds_bptree_crc32c_update_node(rchild);
+ }
+#endif
+}
+
+void
+sds_bptree_branch_left_borrow(sds_bptree_instance *binst, sds_bptree_node *left,
sds_bptree_node *right) {
+ sds_bptree_node *rchild = NULL;
+ /* Take from the left and give to the right. */
+ for (size_t i = right->item_count; i > 0; i--) {
+ right->keys[i] = right->keys[i - 1];
+ right->values[i + 1] = right->values[i];
+ }
+ /* shuffle the last value over. */
+ right->values[1] = right->values[0];
+
+ left->item_count -= 1;
+ right->item_count += 1;
+ /* keys[0] will already be blank here. */
+
+ right->keys[0] =
binst->key_dup_fn(sds_bptree_node_leftmost_child_key((sds_bptree_node
*)right->values[1]));
+ right->values[0] = left->values[left->item_count + 1];
+
+ rchild = (sds_bptree_node *)right->values[0];
+ rchild->parent = right;
+
+ /* Now free the key in the left. */
+ binst->key_free_fn(left->keys[left->item_count]);
+ left->keys[left->item_count] = NULL;
+ left->values[left->item_count + 1] = NULL;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ sds_bptree_crc32c_update_node(right);
+ sds_bptree_crc32c_update_node(rchild);
+ }
+#endif
+}
+
+
diff --git a/src/libsds/sds/bpt/list.c b/src/libsds/sds/bpt/list.c
new file mode 100644
index 0000000..de7c9b3
--- /dev/null
+++ b/src/libsds/sds/bpt/list.c
@@ -0,0 +1,54 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt.h"
+
+/* Will be used for transactions. */
+void
+sds_bptree_node_list_push(sds_bptree_node_list **list, sds_bptree_node *node) {
+ // node, next
+ if (list == NULL) {
+ return;
+ }
+ sds_bptree_node_list *new_head = sds_malloc(sizeof(sds_bptree_node_list));
+ new_head->node = node;
+ new_head->next = *list;
+ *list = new_head;
+ return;
+}
+
+sds_bptree_node *
+sds_bptree_node_list_pop(sds_bptree_node_list **list) {
+ // Pop and free the list element.
+ if (list == NULL || *list == NULL) {
+ return NULL;
+ }
+ sds_bptree_node *next_node = (*list)->node;
+ sds_bptree_node_list *old;
+ old = *list;
+ *list = old->next;
+ sds_free(old);
+ return next_node;
+}
+
+void
+sds_bptree_node_list_release(sds_bptree_node_list **list) {
+ if (list == NULL || *list == NULL) {
+ return;
+ }
+ sds_bptree_node_list *next_node = *list;
+ sds_bptree_node_list *old;
+ while (next_node != NULL) {
+ old = next_node;
+ next_node = old->next;
+ sds_free(old);
+ }
+}
+
+
+
diff --git a/src/libsds/sds/bpt/map.c b/src/libsds/sds/bpt/map.c
new file mode 100644
index 0000000..4205aa5
--- /dev/null
+++ b/src/libsds/sds/bpt/map.c
@@ -0,0 +1,361 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt.h"
+// Used by our printing code.
+FILE *fp = NULL;
+
+
+sds_result
+sds_bptree_map_nodes(sds_bptree_instance *binst, sds_bptree_node *root, sds_result
(*fn)(sds_bptree_instance *binst, sds_bptree_node *) ) {
+ sds_bptree_node_list *cur = sds_malloc(sizeof(sds_bptree_node_list));
+ sds_bptree_node_list *prev = cur;
+ sds_bptree_node_list *tail = cur;
+
+ if (binst == NULL) {
+ return SDS_NULL_POINTER;
+ }
+
+ cur->node = root;
+ cur->next = NULL;
+ // This should map over all NODEs of the DB, and call FN on them.
+ // The fn takes one node, and returs a result if successful. No reason it can't
recurse ....
+ // WARNING: In the future you may need to make this non recursive, as large data sets
may exceed stack in recursive.
+ sds_result final_result = SDS_SUCCESS;
+ sds_result result = SDS_SUCCESS;
+
+ while (cur != NULL) {
+#ifdef DEBUG
+ // sds_log("sds_bptree_map_nodes", "node_%p ...",
cur->node);
+#endif
+ if (cur->node->level > 0) {
+ // Has to be <= here as this is access values, not keys!
+ for (size_t i = 0; i <= cur->node->item_count; i++) {
+ // Alloc a new element, and shuffle along ....
+ if (cur->node->values[i] != NULL) {
+ tail->next = sds_malloc(sizeof(sds_bptree_node_list));
+ tail = tail->next;
+ tail->node = (sds_bptree_node *)cur->node->values[i];
+ tail->next = NULL;
+ }
+ }
+ }
+ result = fn(binst, cur->node);
+ if (result != SDS_SUCCESS) {
+#ifdef DEBUG
+ sds_log("sds_bptree_map_nodes", "node_%p failed %d",
cur->node, result);
+#endif
+ final_result = result;
+ }
+ prev = cur;
+ cur = cur->next;
+ free(prev);
+ }
+ // Start at the root ...
+ return final_result;
+}
+
+sds_result
+sds_bptree_verify_instance(sds_bptree_instance *binst)
+{
+ sds_result result = SDS_SUCCESS;
+ // check the checksum.
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ result = sds_bptree_crc32c_verify_instance(binst);
+ }
+#endif
+ // Verify that root node is not NULL (It can be empty)
+ return result;
+}
+
+sds_result
+sds_bptree_verify_node(sds_bptree_instance *binst, sds_bptree_node *node) {
+ // - verify the hash of the node metadata
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_node(node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+ // - item_count matches number of non-null keys.
+ for (size_t i = 0; i < node->item_count; i++) {
+ if (node->keys[i] == NULL)
+ {
+ printf("%d \n", node->item_count);
+ return SDS_INVALID_KEY;
+ }
+
+ }
+
+
+ // Check that our keys are in order in the node.
+ // We only need to compare left to right, because if we have:
+ // A B C D
+ // If A is < B, C, D, then when we check B, we only need assert B < C, D, as A
< B is already true
+ for (size_t i = 0; i < node->item_count; i++) {
+ for (size_t j = 0; j < i && i != j; j++) {
+ // printf("cmp: ! node->keys[%d] %d > node->keys[%d]
%d\n", j, node->keys[j], i, node->keys[i]);
+ int64_t result = binst->key_cmp_fn(node->keys[i], node->keys[j]);
+ if (result == 0) {
+ return SDS_DUPLICATE_KEY;
+ } else if (result < 0) {
+ // If the key on the right is less than the key on the left
+ return SDS_INVALID_KEY;
+ }
+ }
+ }
+
+ if (node->level == 0) {
+
+ /* Verify the values in the node if we are a leaf. */
+ /* Leaves must always adhere to the properties of the tree. IE half size */
+ if (node->parent != NULL && node->item_count <
(SDS_BPTREE_HALF_CAPACITY - 1)) {
+ return SDS_INVALID_NODE;
+ }
+
+#ifdef DEBUG
+ /*
+ // - verify the value hashes
+ // Now that we are sure of all value sizes and pointers, lets do the checksum of
the data in the values
+ // - NULL values have 0 size
+ // - non-null have > 0 size
+ for (size_t i = 0; i < node->item_count; i++) {
+ sds_bptree_value *value = (sds_bptree_value *)node->values[i];
+
+ if (value != NULL && value->size == 0 )
+ {
+ return SDS_INVALID_VALUE_SIZE;
+ }
+
+ }
+
+ for (size_t i = 0; i < node->item_count; i++) {
+ if (node->values[i] != NULL) {
+ if (sds_bptree_crc32c_verify_value(node->values[i]) != SDS_SUCCESS) {
+ return SDS_CHECKSUM_FAILURE;
+ }
+ }
+ }
+ */
+#endif
+
+ // Check that our highest element is smaller than binst->branch_factor[0]
+ // Because of the above assertion, we now know that our node is in order
+ // And given the next is also, we can assert ALL nodes are in key order.
+ sds_bptree_node *rnode = (sds_bptree_node
*)node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ if (rnode != NULL) {
+ int64_t result = binst->key_cmp_fn(node->keys[node->item_count - 1],
rnode->keys[0]);
+ if (result > 0) {
+ return SDS_INVALID_KEY;
+ } else if (result == 0) {
+ return SDS_DUPLICATE_KEY;
+ }
+ }
+
+ // Is there an effective way to do a duplicate key check?
+ // I think the above checks already assert there are no dupes in the tree.
+
+ } else {
+ // As a branch, to be valid we must have at LEAST 1 item and 2 links
+ // The later check will validate the links for us :)
+ if (node->item_count == 0) {
+ return SDS_INVALID_NODE;
+ }
+
+ // Check that all our childrens parent refs are to us.
+ for (uint16_t i = 0; i <= node->item_count; i++) {
+ sds_bptree_node *cnode = (sds_bptree_node *)node->values[i];
+ if (cnode->parent != node) {
+ return SDS_INVALID_POINTER;
+ }
+ }
+
+ if (binst->root == node) {
+ if (node->parent != NULL) {
+ return SDS_INVALID_POINTER;
+ }
+ }
+
+ // Check that for key at index i, link i key[0] is < key.
+ // For key at index i, link i + 1, is > key
+ // For one key, there will always be a left and right link. That's the way it
is :)
+ // So we can fail if one is NULL
+ for (uint16_t i = 0; i < node->item_count; i++) {
+ if (node->keys[i] != NULL) {
+ sds_bptree_node *lnode = (sds_bptree_node *)node->values[i];
+ sds_bptree_node *rnode = (sds_bptree_node *)node->values[i + 1];
+
+ if (lnode == NULL || rnode == NULL) {
+ // We should have two children!
+ return SDS_INVALID_POINTER;
+ }
+ // Check that all left keys are LESS.
+ sds_bptree_node_list *path = NULL;
+ size_t j = 0;
+
+ while (lnode != NULL) {
+ for (j = 0; j < lnode->item_count; j++) {
+ /* This checks that all left keys *and* their children are less
*/
+ int64_t result = binst->key_cmp_fn(lnode->keys[j],
node->keys[i]);
+ if (result >= 0) {
+#ifdef DEBUG
+ sds_log("sds_bptree_verify_node", " fault
is in node %p with left child %p", node, lnode);
+#endif
+ return SDS_INVALID_KEY_ORDER;
+ }
+ if (lnode->level > 0) {
+ sds_bptree_node_list_push(&path, lnode->values[j]);
+ }
+ }
+ if (lnode->level > 0) {
+ sds_bptree_node_list_push(&path, lnode->values[j]);
+ }
+ lnode = sds_bptree_node_list_pop(&path);
+ }
+ // All right keys are greater or equal
+ while (rnode != NULL) {
+ for (j = 0; j < rnode->item_count; j++) {
+ /* This checks that all right keys are greatr or equal and their
children */
+ int64_t result = binst->key_cmp_fn(rnode->keys[j],
node->keys[i]);
+ if (result < 0) {
+#ifdef DEBUG
+ sds_log("sds_bptree_verify_node", " fault
is in node %p with right child %p", node, rnode);
+#endif
+ return SDS_INVALID_KEY_ORDER;
+ }
+ if (rnode->level > 0) {
+ sds_bptree_node_list_push(&path, rnode->values[j]);
+ }
+ }
+ if (rnode->level > 0) {
+ sds_bptree_node_list_push(&path, rnode->values[j]);
+ }
+ rnode = sds_bptree_node_list_pop(&path);
+ } // While rnode
+ }
+ } // end for
+
+ } // end else is_leaf
+
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_bptree_verify(sds_bptree_instance *binst) {
+#ifdef DEBUG
+ sds_log("sds_bptree_verify", "==> Beginning verification of
instance %p", binst);
+#endif
+ // How do we get *all* the errors here, for every node? ...
+ // Do me make a error msg buffer and fill it?
+ sds_result total_result = SDS_SUCCESS;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_verify_instance(binst);
+ if (result != SDS_SUCCESS) {
+ total_result = result;
+ }
+
+ // - No node id exceeds max id. -- This requires a tree walk ...
+ // - no duplicate node id
+ // - no duplicate keys
+ result = sds_bptree_map_nodes(binst, binst->root, sds_bptree_verify_node);
+ if (result != SDS_SUCCESS) {
+ total_result = result;
+ }
+
+#ifdef DEBUG
+ sds_log("sds_bptree_verify", "==> Completing verification of
instance %p %d", binst, total_result);
+#endif
+
+ return total_result;
+}
+
+/* This display code runs in two passes. */
+static sds_result
+sds_node_to_dot(sds_bptree_instance *binst __attribute__((unused)), sds_bptree_node
*node) {
+ if (node == NULL) {
+ return SDS_INVALID_NODE;
+ }
+ // Given the node write it out as:
+ fprintf(fp, "subgraph c%"PRIu64" { \n
rank=\"same\";\n", node->level);
+ fprintf(fp, " node_%p [label =\" { node=%p items=%d
txn=%"PRIu64" parent=%p | { <f0> ", node, node, node->item_count,
node->txn_id, node->parent );
+ for (size_t i = 0; i < SDS_BPTREE_DEFAULT_CAPACITY; i++) {
+ fprintf(fp, "| %" PRIu64 " | <f%"PRIu64"> ",
(uint64_t)node->keys[i], i + 1 );
+ }
+ fprintf(fp, "}}\"]; \n}\n");
+ return SDS_SUCCESS;
+}
+
+static sds_result
+sds_node_ptrs_to_dot(sds_bptree_instance *binst __attribute__((unused)), sds_bptree_node
*node) {
+ // for a given node,
+ // printf("\"node0\":f0 -> \"node1\"");
+ if (node->level == 0) {
+ if (node->values[SDS_BPTREE_DEFAULT_CAPACITY] != NULL) {
+ // Work around a graphviz display issue, with Left and Right pointers
+ fprintf(fp, "\"node_%p\" -> \"node_%p\";
\n", node, node->values[SDS_BPTREE_DEFAULT_CAPACITY] );
+ }
+ } else {
+ for (size_t i = 0; i < SDS_BPTREE_BRANCH; i++) {
+ if (node->values[i] != NULL) {
+ if (i == SDS_BPTREE_DEFAULT_CAPACITY) {
+ // Work around a graphviz display issue, with Left and Right
pointers
+ fprintf(fp, "\"node_%p\" -> \"node_%p\";
\n", node, node->values[i] );
+ } else {
+ fprintf(fp, "\"node_%p\":f%"PRIu64" ->
\"node_%p\"; \n", node, i, node->values[i] );
+ }
+ }
+ }
+ }
+
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_bptree_display(sds_bptree_instance *binst) {
+ sds_result result = SDS_SUCCESS;
+
+ char *path = malloc(sizeof(char) * 20);
+#ifdef DEBUG
+ sds_log("sds_bptree_display", "Writing step %03d\n",
binst->print_iter);
+#endif
+ sprintf(path, "/tmp/graph_%03d.dot", binst->print_iter);
+ binst->print_iter += 1;
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_bptree_crc32c_update_instance(binst);
+ }
+#endif
+ // Because we change the root inst, we have to checksum it.
+ // Open a file to write into.
+ fp = fopen(path, "w+");
+
+ fprintf(fp, "digraph g {\n");
+ fprintf(fp, "node [shape = record,height=.1];\n");
+
+ fprintf(fp, "nodehdr[label = \"B+Tree \"];\n");
+
+ result = sds_bptree_map_nodes(binst, binst->root, sds_node_to_dot);
+ // HANDLE THE RESULT
+ result = sds_bptree_map_nodes(binst, binst->root, sds_node_ptrs_to_dot);
+ // HANDLE THE RESULT
+
+ // Should this write a dot file?
+ fprintf(fp, "}\n");
+ fclose(fp);
+ fp = NULL;
+ free(path);
+
+ return result;
+}
+
+
diff --git a/src/libsds/sds/bpt/search.c b/src/libsds/sds/bpt/search.c
new file mode 100644
index 0000000..f406bcc
--- /dev/null
+++ b/src/libsds/sds/bpt/search.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Contains the actual tree manipulation algorithms. This abstraction exists
+ * so that code between the cow and non-cow version can be shared.
+ */
+
+#include "bpt.h"
+
+sds_result
+sds_bptree_search_node(sds_bptree_instance *binst, sds_bptree_node *root, void *key,
sds_bptree_node **target_out_node) {
+ sds_bptree_node *target_node = root;
+ uint64_t i = 0;
+
+ /* We do this first, as we need the node to pass before we access it! */
+#ifdef DEBUG
+ if (binst->search_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_instance(binst);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_node(target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+branch_loop:
+ while (target_node->level > 0) {
+ while (i < target_node->item_count) {
+ if (binst->key_cmp_fn(key, (target_node)->keys[i]) < 0) {
+ target_node = (sds_bptree_node *)target_node->values[i];
+#ifdef DEBUG
+ if (binst->search_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_node(target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+ i = 0;
+ goto branch_loop;
+ } else {
+ i++;
+ }
+ }
+ target_node = (sds_bptree_node
*)target_node->values[target_node->item_count];
+ i = 0;
+ }
+ *target_out_node = target_node;
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_bptree_search_internal(sds_bptree_instance *binst, sds_bptree_node *root, void *key)
{
+
+#ifdef DEBUG
+ sds_log("sds_bptree_search_internal", "<== Beginning search of
%d", key);
+#endif
+
+ sds_bptree_node *target_node;
+#ifdef DEBUG
+ sds_result result = sds_bptree_search_node(binst, root, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+#else
+ sds_bptree_search_node(binst, root, key, &target_node);
+#endif
+
+ for (size_t i = 0; i < target_node->item_count; i++) {
+ if (binst->key_cmp_fn(key, (target_node)->keys[i]) == 0) {
+#ifdef DEBUG
+ sds_log("sds_bptree_search_internal", "<== Completing
search of %d", key);
+#endif
+ return SDS_KEY_PRESENT;
+ }
+ }
+#ifdef DEBUG
+ sds_log("sds_bptree_search_internal", "==> Failing search of
%d", key);
+#endif
+ return SDS_KEY_NOT_PRESENT;
+}
+
+
+sds_result
+sds_bptree_retrieve_internal(sds_bptree_instance *binst, sds_bptree_node *root, void
*key, void **target) {
+ // This is the public retrieve function
+ // It's basically the same as search.
+#ifdef DEBUG
+ sds_log("sds_bptree_retrieve_internal", "==> Beginning retrieve of
%d", key);
+#endif
+ sds_bptree_node *target_node = NULL;
+
+#ifdef DEBUG
+ if (binst->search_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_instance(binst);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+#ifdef DEBUG
+ sds_result result = sds_bptree_search_node(binst, root, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+#else
+ sds_bptree_search_node(binst, root, key, &target_node);
+#endif
+ /* Now get the key from the node. */
+
+#ifdef DEBUG
+ sds_log("sds_bptree_retrieve_internal", "==> Completing retrieve of
%d", key);
+#endif
+ return sds_bptree_node_retrieve_key(binst, target_node, key, target);
+}
+
+
+
diff --git a/src/libsds/sds/bpt/set.c b/src/libsds/sds/bpt/set.c
new file mode 100644
index 0000000..50f4426
--- /dev/null
+++ b/src/libsds/sds/bpt/set.c
@@ -0,0 +1,272 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt.h"
+/* This is just used by the sets to clone instances */
+
+sds_result
+sds_bptree_instance_clone(sds_bptree_instance *binst, sds_bptree_instance **binst_ptr) {
+ if (binst_ptr == NULL) {
+ sds_log("sds_btree_init", "Invalid pointer");
+ return SDS_NULL_POINTER;
+ }
+
+ *binst_ptr = sds_memalign(sizeof(sds_bptree_instance), SDS_CACHE_ALIGNMENT);
+ (*binst_ptr)->print_iter = 0;
+ (*binst_ptr)->offline_checksumming = binst->offline_checksumming;
+ (*binst_ptr)->search_checksumming = binst->search_checksumming;
+ (*binst_ptr)->key_cmp_fn = binst->key_cmp_fn;
+ (*binst_ptr)->value_free_fn = binst->value_free_fn;
+ (*binst_ptr)->key_free_fn = binst->key_free_fn;
+ (*binst_ptr)->key_dup_fn = binst->key_dup_fn;
+
+ // Now update the checksums
+#ifdef DEBUG
+ if ((*binst_ptr)->offline_checksumming) {
+ sds_bptree_crc32c_update_instance(*binst_ptr);
+ }
+#endif
+ return SDS_SUCCESS;
+}
+
+/* If we can, get the requested key */
+sds_result
+sds_bptree_list_advance(sds_bptree_node **item, size_t *index) {
+
+#ifdef DEBUG
+ sds_log("sds_bptree_list_advance", "%p current index is %"
PRIu64"", *item, *index);
+#endif
+ /* Now, if we have the ability */
+ if (*index < (size_t)((*item)->item_count - 1)) {
+ *index += 1;
+ return SDS_SUCCESS;
+ } else {
+ sds_bptree_node *next = (sds_bptree_node
*)(*item)->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ if (next != NULL) {
+ *index = 0;
+ *item = next;
+ return SDS_SUCCESS;
+ } else {
+ /* index is max, and no more nodes. */
+ return SDS_LIST_EXHAUSTED;
+ }
+ }
+}
+
+/* Tree mapping functions */
+/* Shouldn't this make a set of results? */
+sds_result
+sds_bptree_map(sds_bptree_instance *binst, void (*fn)(void *k, void *v)) {
+ /* If this is the non-cow tree, this is easy. */
+ /* Find the bottom left node, then iterate to the right! */
+ sds_bptree_node *work_node = sds_bptree_node_min(binst);
+
+ while (work_node != NULL) {
+ for (size_t index = 0; index < work_node->item_count; index++) {
+ fn(work_node->keys[index], work_node->values[index]);
+ }
+ work_node = (sds_bptree_node
*)work_node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ }
+ return SDS_SUCCESS;
+}
+
+sds_result sds_bptree_filter(sds_bptree_instance *binst_a, int64_t (*fn)(void *k, void
*v), sds_bptree_instance **binst_subset) {
+ /* */
+ sds_result result = sds_bptree_instance_clone(binst_a, binst_subset);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ sds_bptree_node *work_node = sds_bptree_node_min(binst_a);
+ int64_t presult = 0;
+ sds_bptree_node *result_root = sds_bptree_node_create();
+ sds_bptree_node *result_ptr = result_root;
+
+ while (work_node != NULL) {
+ for (size_t index = 0; index < work_node->item_count; index++) {
+ presult = fn(work_node->keys[index], work_node->values[index]);
+ if (presult != 0) {
+ // Add the item to the new set.
+ /* WHAT DO WE DO WITH VALUES */
+ void *key = binst_a->key_dup_fn(work_node->keys[index]);
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ }
+ work_node = (sds_bptree_node
*)work_node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+ }
+
+ return sds_bptree_node_list_to_tree(*binst_subset, result_root);
+}
+
+static sds_result
+sds_bptree_set_operation(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_out, uint16_t both, uint16_t diff, uint16_t alist) {
+
+ /* Based on a set of flags we choose to include the element from:
+ * - Both lists
+ * - If it is only in one list or the other.
+ * - if it is only in A list.
+ *
+ */
+ sds_result result = SDS_SUCCESS;
+ /* Make sure that our function pointers are all the same .... */
+ if (binst_a->key_cmp_fn != binst_b->key_cmp_fn || binst_a->value_free_fn !=
binst_b->value_free_fn ||
+ binst_a->key_free_fn != binst_b->key_free_fn || binst_a->key_dup_fn !=
binst_b->key_dup_fn) {
+ return SDS_INCOMPATIBLE_INSTANCE;
+ }
+ /* Make the output instance. */
+ result = sds_bptree_instance_clone(binst_a, binst_out);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ /* Need a pointer to track node_a and index_a, vs node_b and index_b */
+ sds_bptree_node *node_a = sds_bptree_node_min(binst_a);
+ size_t index_a = 0;
+ sds_result result_a = SDS_SUCCESS;
+ sds_bptree_node *node_b = sds_bptree_node_min(binst_b);
+ size_t index_b = 0;
+ sds_result result_b = SDS_SUCCESS;
+ /* As for the new instance, we can just keep the left node, and build */
+ sds_bptree_node *result_root = sds_bptree_node_create();
+ sds_bptree_node *result_ptr = result_root;
+
+ /* Can't rely on keys being zero, so check item count */
+ if (node_a->item_count == 0) {
+ result_a = SDS_LIST_EXHAUSTED;
+ }
+ if (node_b->item_count == 0) {
+ result_b = SDS_LIST_EXHAUSTED;
+ }
+
+ /* We have to handle two cases;
+ * - both sets have "some" content, so we can actually do comparisons
+ * - one set is empty, so we can *never* compare them.
+ */
+
+ if (result_a != SDS_LIST_EXHAUSTED && result_b != SDS_LIST_EXHAUSTED) {
+ /* Both lists have at least *some* content, so this comparison works. */
+
+ /* Now iterate over the values. */
+ while (result_a == SDS_SUCCESS && result_b == SDS_SUCCESS) {
+ int64_t cmp = binst_a->key_cmp_fn(node_a->keys[index_a],
node_b->keys[index_b]);
+ if (cmp == 0) {
+ /* These values are the same, advance! */
+ if (both) {
+ void *key = binst_a->key_dup_fn(node_a->keys[index_a]);
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ result_a = sds_bptree_list_advance(&node_a, &index_a);
+ result_b = sds_bptree_list_advance(&node_b, &index_b);
+ } else if (cmp < 0) {
+ /* If A is smaller, we advance a, and include the value */
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ if (diff || alist) {
+ void *key = binst_a->key_dup_fn(node_a->keys[index_a]);
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ result_a = sds_bptree_list_advance(&node_a, &index_a);
+ } else {
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ if (diff) {
+ void *key = binst_b->key_dup_fn(node_b->keys[index_b]);
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ result_b = sds_bptree_list_advance(&node_b, &index_b);
+ }
+ }
+
+ /* We have now exhausted a list. Which one? */
+ while (result_a == SDS_SUCCESS) {
+ int64_t cmp = binst_a->key_cmp_fn(node_a->keys[index_a],
node_b->keys[index_b]);
+ /* We have exhausted B. Finish iterating */
+ if (cmp == 0) {
+ if (both) {
+ void *key = binst_a->key_dup_fn(node_a->keys[index_a]);
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ } else if (cmp != 0) {
+ if (diff || alist) {
+ void *key = binst_a->key_dup_fn(node_a->keys[index_a]);
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ }
+ result_a = sds_bptree_list_advance(&node_a, &index_a);
+ }
+
+ while (result_b == SDS_SUCCESS) {
+ int64_t cmp = binst_a->key_cmp_fn(node_a->keys[index_a],
node_b->keys[index_b]);
+ if (cmp == 0) {
+ if (both) {
+ void *key = binst_a->key_dup_fn(node_b->keys[index_b]);
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ } else if (cmp != 0) {
+ if (diff) {
+ void *key = binst_a->key_dup_fn(node_b->keys[index_b]);
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ }
+ result_b = sds_bptree_list_advance(&node_b, &index_b);
+ }
+ } else {
+ /* One of the lists *is* empty from the start, so just shortcut
+ * as we can't do a comparison.
+ */
+ /* because one is empty the lists "always differ" */
+ while (result_a == SDS_SUCCESS) {
+ /* We have exhausted B. Finish iterating */
+ if (diff || alist) {
+ void *key = binst_a->key_dup_fn(node_a->keys[index_a]);
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ result_a = sds_bptree_list_advance(&node_a, &index_a);
+ }
+
+ while (result_b == SDS_SUCCESS) {
+ if (diff) {
+ void *key = binst_a->key_dup_fn(node_b->keys[index_b]);
+ /* !! WHAT ABOUT VALUE DUPLICATION!!! */
+ sds_bptree_node_list_append(&result_ptr, key, NULL);
+ }
+ result_b = sds_bptree_list_advance(&node_b, &index_b);
+ }
+ }
+
+ /* Do a tree build from the results */
+ /* All done! */
+ return sds_bptree_node_list_to_tree(*binst_out, result_root);
+}
+
+
+
+
+sds_result
+sds_bptree_difference(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_difference) {
+ return sds_bptree_set_operation(binst_a, binst_b, binst_difference, 0, 1, 0);
+}
+
+sds_result
+sds_bptree_union(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_union) {
+ return sds_bptree_set_operation(binst_a, binst_b, binst_union, 1, 1, 0);
+}
+
+sds_result
+sds_bptree_intersect(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_intersect) {
+ return sds_bptree_set_operation(binst_a, binst_b, binst_intersect, 1, 0, 0);
+}
+
+sds_result
+sds_bptree_compliment(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_compliment) {
+ return sds_bptree_set_operation(binst_a, binst_b, binst_compliment, 0, 0, 1);
+}
+
diff --git a/src/libsds/sds/bpt/verify.c b/src/libsds/sds/bpt/verify.c
new file mode 100644
index 0000000..b9ddc12
--- /dev/null
+++ b/src/libsds/sds/bpt/verify.c
@@ -0,0 +1,72 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt.h"
+
+/* Node checksumming functions. */
+#ifdef DEBUG
+
+sds_result
+sds_bptree_crc32c_verify_instance(sds_bptree_instance *binst) {
+ // This starts the check *after* the checksum in the struct
+ if (sds_crc32c(0, (const unsigned char *)binst + sizeof(uint32_t),
sizeof(sds_bptree_instance) - sizeof(uint32_t)) == binst->checksum) {
+ return SDS_SUCCESS;
+ } else {
+ return SDS_CHECKSUM_FAILURE;
+ }
+}
+
+void
+sds_bptree_crc32c_update_instance(sds_bptree_instance *binst) {
+ // This starts the check *after* the checksum in the struct
+ binst->checksum = sds_crc32c(0, (const unsigned char *)binst + sizeof(uint32_t),
sizeof(sds_bptree_instance) - sizeof(uint32_t));
+}
+
+sds_result
+sds_bptree_crc32c_verify_node(sds_bptree_node *node) {
+ // This starts the check *after* the checksum in the struct
+ uint32_t checksum = sds_crc32c(0, (const unsigned char *)node + sizeof(uint32_t),
sizeof(sds_bptree_node) - sizeof(uint32_t));
+ if(checksum == node->checksum) {
+ return SDS_SUCCESS;
+ } else {
+ return SDS_CHECKSUM_FAILURE;
+ }
+}
+
+void
+sds_bptree_crc32c_update_node(sds_bptree_node *node) {
+ // printf("sds_bptree_update_crc_node: node_%p\n", node);
+ node->checksum = sds_crc32c(0, (const unsigned char *)node + sizeof(uint32_t),
sizeof(sds_bptree_node) - sizeof(uint32_t));
+}
+
+/*
+sds_result
+sds_bptree_crc32c_verify_value(sds_bptree_value *value) {
+ // This starts the check *after* the checksum in the struct
+ if (sds_crc32c(0, (const unsigned char *)value + sizeof(uint32_t),
sizeof(sds_bptree_value) - sizeof(uint32_t)) == value->checksum) {
+ // Now we check the data itself.
+ if (sds_crc32c(0, (const unsigned char *)value->data, value->size) ==
value->data_checksum) {
+ return SDS_SUCCESS;
+ }
+ }
+ return SDS_CHECKSUM_FAILURE;
+}
+
+void
+sds_bptree_crc32c_update_value(sds_bptree_value *value) {
+ // This starts the check *after* the checksum in the struct
+ value->data_checksum = sds_crc32c(0, (const unsigned char *)value->data,
value->size);
+ value->checksum = sds_crc32c(0, (const unsigned char *)value + sizeof(uint32_t),
sizeof(sds_bptree_value) - sizeof(uint32_t));
+}
+*/
+
+
+#endif /* DEBUG */
+
+
+
diff --git a/src/libsds/sds/bpt_cow/atomic.c b/src/libsds/sds/bpt_cow/atomic.c
new file mode 100644
index 0000000..63bb645
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/atomic.c
@@ -0,0 +1,97 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt_cow.h"
+
+sds_result
+sds_bptree_cow_search_atomic(sds_bptree_cow_instance *binst, void *key)
+{
+ sds_bptree_transaction *ro_btxn = NULL;
+ sds_result result = sds_bptree_cow_rotxn_begin(binst, &ro_btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ sds_result search_result = sds_bptree_cow_search(ro_btxn, key);
+ result = sds_bptree_cow_rotxn_close(&ro_btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ return search_result;
+}
+
+sds_result
+sds_bptree_cow_retrieve_atomic(sds_bptree_cow_instance *binst, void *key, void **target)
+{
+ void *int_target = NULL;
+ sds_bptree_transaction *ro_btxn = NULL;
+ sds_result result = sds_bptree_cow_rotxn_begin(binst, &ro_btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ sds_result search_result = sds_bptree_cow_retrieve(ro_btxn, key, &int_target);
+ if (search_result == SDS_KEY_PRESENT) {
+ *target = binst->bi->value_dup_fn(int_target);
+ }
+ result = sds_bptree_cow_rotxn_close(&ro_btxn);
+ if (result != SDS_SUCCESS) {
+ if (*target != NULL) {
+ binst->bi->value_free_fn(*target);
+ }
+ return result;
+ }
+
+ return search_result;
+}
+
+sds_result
+sds_bptree_cow_delete_atomic(sds_bptree_cow_instance *binst, void *key)
+{
+ sds_bptree_transaction *wr_btxn = NULL;
+ sds_result result = sds_bptree_cow_wrtxn_begin(binst, &wr_btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ sds_result search_result = sds_bptree_cow_delete(wr_btxn, key);
+ if (search_result == SDS_SUCCESS) {
+ result = sds_bptree_cow_wrtxn_commit(&wr_btxn);
+ } else {
+ result = sds_bptree_cow_wrtxn_abort(&wr_btxn);
+ }
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ return search_result;
+}
+
+sds_result
+sds_bptree_cow_insert_atomic(sds_bptree_cow_instance *binst, void *key, void *value)
+{
+ sds_bptree_transaction *wr_btxn = NULL;
+ sds_result result = sds_bptree_cow_wrtxn_begin(binst, &wr_btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ sds_result search_result = sds_bptree_cow_insert(wr_btxn, key, value);
+ if (search_result == SDS_SUCCESS) {
+ result = sds_bptree_cow_wrtxn_commit(&wr_btxn);
+ } else {
+ result = sds_bptree_cow_wrtxn_abort(&wr_btxn);
+ }
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ return search_result;
+}
+
diff --git a/src/libsds/sds/bpt_cow/bpt_cow.c b/src/libsds/sds/bpt_cow/bpt_cow.c
new file mode 100644
index 0000000..1489239
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/bpt_cow.c
@@ -0,0 +1,582 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * _______________________________________
+ * < The ultimate in bovine datastructures >
+ * ---------------------------------------
+ * \ ^__^
+ * \ (oo)\_______
+ * (__)\ )\/\
+ * ||----w |
+ * || ||
+*/
+
+#include "bpt_cow.h"
+// Used by our printing code.
+static FILE *fp = NULL;
+static uint64_t print_iter = 0;
+
+sds_result sds_bptree_cow_init(sds_bptree_cow_instance **binst_ptr, uint16_t
checksumming, int64_t (*key_cmp_fn)(void *a, void *b), void (*value_free_fn)(void *value),
void *(*value_dup_fn)(void *key), void (*key_free_fn)(void *key), void *(*key_dup_fn)(void
*key)) {
+ if (binst_ptr == NULL) {
+#ifdef DEBUG
+ sds_log("sds_btree_init", "Invalid pointer");
+#endif
+ return SDS_NULL_POINTER;
+ }
+
+ *binst_ptr = sds_memalign(sizeof(sds_bptree_cow_instance), SDS_CACHE_ALIGNMENT);
+ (*binst_ptr)->bi = sds_memalign(sizeof(sds_bptree_instance),
SDS_CACHE_ALIGNMENT);
+
+ // Create our locks.
+ (*binst_ptr)->read_lock = sds_calloc(sizeof(pthread_rwlock_t));
+ pthread_rwlock_init((*binst_ptr)->read_lock, NULL);
+ (*binst_ptr)->write_lock = sds_calloc(sizeof(pthread_mutex_t));
+ pthread_mutex_init((*binst_ptr)->write_lock, NULL);
+ (*binst_ptr)->vacuum_lock = sds_calloc(sizeof(pthread_mutex_t));
+ pthread_mutex_init((*binst_ptr)->vacuum_lock, NULL);
+
+ /* Take both to be sure of barriers etc. */
+ pthread_mutex_lock( (*binst_ptr)->write_lock);
+ pthread_rwlock_wrlock( (*binst_ptr)->read_lock);
+
+ (*binst_ptr)->bi->print_iter = 0;
+ (*binst_ptr)->bi->offline_checksumming = checksumming;
+ (*binst_ptr)->bi->search_checksumming = checksumming;
+ (*binst_ptr)->bi->key_cmp_fn = key_cmp_fn;
+ (*binst_ptr)->bi->value_free_fn = value_free_fn;
+ (*binst_ptr)->bi->value_dup_fn = value_dup_fn;
+ (*binst_ptr)->bi->key_free_fn = key_free_fn;
+ (*binst_ptr)->bi->key_dup_fn = key_dup_fn;
+
+ // null the TXN to be sure.
+ (*binst_ptr)->txn = NULL;
+ // Make the first empty txn.
+ // The root node is populated by the transaction.
+ (*binst_ptr)->txn = sds_bptree_txn_create(*binst_ptr);
+ (*binst_ptr)->tail_txn = (*binst_ptr)->txn;
+ (*binst_ptr)->txn->state = SDS_TXN_READ;
+ (*binst_ptr)->txn->reference_count = 1;
+
+ // cow node create pushes a node to the created list, flush it.
+ // Dump our creation list, we are past the point of no return.
+ sds_bptree_node_list_release(&((*binst_ptr)->txn->created));
+
+ // Update our checksums.
+#ifdef DEBUG
+ if ((*binst_ptr)->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_instance((*binst_ptr)->bi);
+ sds_bptree_crc32c_update_cow_instance(*binst_ptr);
+ }
+#endif
+
+ pthread_rwlock_unlock((*binst_ptr)->read_lock);
+ pthread_mutex_unlock((*binst_ptr)->write_lock);
+
+ return SDS_SUCCESS;
+}
+
+// May block until all transactions are resolved?
+
+sds_result sds_bptree_cow_destroy(sds_bptree_cow_instance *binst) {
+ sds_result result = SDS_SUCCESS;
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_destroy", " Destroying instance %p",
binst);
+#endif
+
+ // This locks and destroys *everything* you love about this tree!!!
+ sds_bptree_cow_txn_destroy_all(binst);
+
+ // Destroy the locks.
+ pthread_rwlock_destroy(binst->read_lock);
+ sds_free(binst->read_lock);
+ pthread_mutex_destroy(binst->write_lock);
+ sds_free(binst->write_lock);
+ pthread_mutex_destroy(binst->vacuum_lock);
+ sds_free(binst->vacuum_lock);
+ sds_free(binst->bi);
+ sds_free(binst);
+
+ return result;
+
+}
+
+// Read only transactions (but write can use them)
+
+sds_result sds_bptree_cow_search(sds_bptree_transaction *btxn, void *key) {
+ if (btxn == NULL) {
+ return SDS_INVALID_TXN;
+ }
+ return sds_bptree_search_internal(btxn->bi, btxn->root, key);
+}
+
+sds_result sds_bptree_cow_retrieve(sds_bptree_transaction *btxn, void *key, void
**target) {
+ if (btxn == NULL) {
+ return SDS_INVALID_TXN;
+ }
+ return sds_bptree_retrieve_internal(btxn->bi, btxn->root, key, target);
+}
+
+// These are write transactions.
+
+sds_result sds_bptree_cow_delete(sds_bptree_transaction *btxn, void *key) {
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_node *cow_node = NULL;
+ sds_bptree_node *target_node = NULL;
+ sds_bptree_node *next_node = NULL;
+
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "==> Beginning delete of %d",
key);
+#endif
+ // Need to fail if txn is RO
+ if (btxn == NULL || btxn->state != SDS_TXN_WRITE) {
+ return SDS_INVALID_TXN;
+ }
+
+ /* Check for a valid key */
+ if (key == NULL) {
+ return SDS_INVALID_KEY;
+ }
+
+ // get the target node (which should update the paths to it.
+ result = sds_bptree_search_node_path(btxn, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ // Check the key exists
+ result = sds_bptree_node_contains_key(btxn->bi, target_node, key);
+ if (result != SDS_KEY_PRESENT) {
+ return result;
+ }
+
+ // Cow the node and the path to it.
+ cow_node = sds_bptree_cow_node_prepare(btxn, target_node);
+
+ next_node = cow_node;
+ target_node = cow_node->parent;
+
+ // then delete from the cowed node it.
+ sds_bptree_leaf_delete(btxn->bi, next_node, key);
+ result = SDS_KEY_PRESENT;
+
+ // Now check if the cowed node is below capacity.
+ // If it is below cap, decide on merge or borrow.
+ if (target_node != NULL && next_node->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ // -- To merge or borrow we need to cow LEFT or RIGHT node.
+ sds_bptree_node *deleted_node = NULL;
+ sds_bptree_node *left = NULL;
+ sds_bptree_node *right = NULL;
+ /* This updates the left and right parent paths, but does NOT cow!!! */
+ sds_bptree_cow_node_siblings(next_node, &left, &right);
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", " %p -> %p -> %p",
left, next_node, right);
+ sds_log("sds_bptree_cow_delete", " next_node->item_count =
%d", next_node->item_count);
+ if (right != NULL) {
+ sds_log("sds_bptree_cow_delete", " right->item_count =
%d", right->item_count);
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(right);
+ }
+ }
+ if (left != NULL) {
+ sds_log("sds_bptree_cow_delete", " left->item_count =
%d", left->item_count);
+ /* Update the csum, because sibling check altered the parent ref */
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ }
+ }
+#endif
+ if (right != NULL && right->item_count > SDS_BPTREE_HALF_CAPACITY)
{
+ /* Does right have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Right leaf borrow");
+#endif
+ cow_node = sds_bptree_cow_node_prepare(btxn, right);
+ sds_bptree_leaf_right_borrow(btxn->bi, next_node, cow_node);
+ /* We setup next_node to be right now, so that key fixing on the path works
*/
+ next_node = cow_node;
+ } else if (left != NULL && left->item_count >
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Left leaf borrow");
+#endif
+ cow_node = sds_bptree_cow_node_prepare(btxn, left);
+ sds_bptree_leaf_left_borrow(btxn->bi, cow_node, next_node);
+ /* This does NOT need to set next_node, because everthing is higher than us
*/
+ } else if (right != NULL && right->item_count <=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does right want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Right leaf contract");
+#endif
+ /* WARNING: DO NOT COW THE RIGHT NODE */
+ sds_bptree_cow_leaf_compact(btxn, next_node, right);
+ /* Setup the branch delete properly */
+ deleted_node = right;
+ /* Next node is correct, and ready for FIXUP */
+ } else if (left != NULL && left->item_count <=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Left leaf contract");
+#endif
+ cow_node = sds_bptree_cow_node_prepare(btxn, left);
+ sds_bptree_cow_leaf_compact(btxn, cow_node, next_node);
+ deleted_node = next_node;
+ /* Next node is correct, and ready for FIXUP */
+ } else {
+ /* Mate, if you get here you are fucked. */
+ return SDS_INVALID_NODE;
+ }
+
+ /************** Now start on the branches ****************/
+
+ while (target_node != NULL) {
+
+ /*
+ * We need to ensure that the key we are deleting is NOT in any other node
on
+ * the path. If it is, we must replace it, by the rules of the B+Tree.
+ */
+ if (deleted_node != NULL) {
+ /* Make sure we delete this value from our branch */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Should be removing %p
from branch %p here!", deleted_node, target_node);
+#endif
+ sds_bptree_branch_delete(btxn->bi, target_node, deleted_node);
+ /* The deed is done, remove the next reference */
+ deleted_node = NULL;
+ } else {
+ /* It means a borrow was probably done somewhere, so we need to fix the
path */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Should be fixing %p key
to child %p here!", target_node, next_node);
+#endif
+ sds_bptree_branch_key_fixup(btxn->bi, target_node, next_node);
+ }
+ /* Then, check the properties of the node are upheld. If not, work out
+ * what needs doing.
+ */
+ next_node = target_node;
+ target_node = target_node->parent;
+ /* Is the node less than half? */
+ if (target_node != NULL && next_node->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Now we do borrow and merge for branches. */
+
+ /* Get siblings */
+ sds_bptree_node *left = NULL;
+ sds_bptree_node *right = NULL;
+ /* This updates the left and right parent paths, but does NOT cow!!! */
+ sds_bptree_cow_node_siblings(next_node, &left, &right);
+ /* Note the conditions for HALF_CAPACITY change here due to
+ * space requirements. we only merge if < half. IE for 7 keys
+ * we have 8 links. If we have a node at 3 keys, and one at 4 keys
+ * they have 4 and 5 links each. Too many! So we have to delay
+ * the merge by a fraction, to allow space for 3 keys and 3 keys.
+ *
+ */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", " %p -> %p ->
%p", left, next_node, right);
+ sds_log("sds_bptree_cow_delete", "
next_node->item_count = %d", next_node->item_count);
+ if (right != NULL) {
+ sds_log("sds_bptree_cow_delete", "
right->item_count = %d", right->item_count);
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(right);
+ }
+ }
+ if (left != NULL) {
+ sds_log("sds_bptree_cow_delete", " left->item_count
= %d", left->item_count);
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ }
+ }
+#endif
+ if (right != NULL && right->item_count >=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does right have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Right branch
borrow");
+#endif
+ /* Since we are about to borrow, we need to cow RIGHT */
+ cow_node = sds_bptree_cow_node_prepare(btxn, right);
+
+ sds_bptree_branch_right_borrow(btxn->bi, next_node, cow_node);
+ /* We setup next_node to be right now, so that key fixing on the path
works */
+ next_node = cow_node;
+ } else if (left != NULL && left->item_count >=
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left have excess keys? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Left branch
borrow");
+#endif
+ /* Since we are about to borrow, we need to cow LEFT */
+ cow_node = sds_bptree_cow_node_prepare(btxn, left);
+
+ sds_bptree_branch_left_borrow(btxn->bi, cow_node, next_node);
+ /* Next node is still on right, key fix will work next loop. */
+ } else if (right != NULL && right->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does right want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Right branch
contract");
+#endif
+ /* WARNING: DO NOT COW THE RIGHT NODE */
+ sds_bptree_cow_branch_compact(btxn, next_node, right);
+ /* Setup the branch delete properly */
+ // next_node = right;
+ deleted_node = right;
+ } else if (left != NULL && left->item_count <
SDS_BPTREE_HALF_CAPACITY) {
+ /* Does left want to merge? */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Left branch
contract");
+#endif
+ /* Since we are about to merge, we need to cow left */
+ /* This is much, much easier than the inverse proposition ... */
+ cow_node = sds_bptree_cow_node_prepare(btxn, left);
+ sds_bptree_cow_branch_compact(btxn, cow_node, next_node);
+ deleted_node = next_node;
+ } else {
+ /* Mate, if you get here you are fucked. */
+ return SDS_INVALID_NODE;
+ }
+
+ } else if ( target_node == NULL && next_node->item_count == 0) {
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "Begin deleting the
root");
+#endif
+ if (btxn->root != next_node) {
+ sds_log("sds_bptree_cow_delete", "Transaction is
corrupted");
+ result = SDS_UNKNOWN_ERROR;
+ goto fail;
+ }
+ // Promote the root now.
+ sds_bptree_cow_root_promote(btxn, next_node);
+ }
+
+ } // End of branch "which target_node" loop
+ } // End of cow_node capacity check.
+
+fail:
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_delete", "<== finishing delete of %d",
key);
+#endif
+
+ return result;
+}
+
+sds_result sds_bptree_cow_insert(sds_bptree_transaction *btxn, void *key, void *value) {
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_node *cow_node = NULL;
+ sds_bptree_node *target_node = NULL;
+
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_insert", "==> Beginning insert of %d",
key);
+#endif
+ // Need to fail if txn is RO
+ if (btxn == NULL || btxn->state != SDS_TXN_WRITE) {
+ return SDS_INVALID_TXN;
+ }
+
+ // First check the key doesn't already exist.
+ if (key == NULL) {
+ return SDS_INVALID_KEY;
+ }
+
+ // This grabs the node, but updates the path parent pointers as we descend down.
+ result = sds_bptree_search_node_path(btxn, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ if (sds_bptree_node_contains_key(btxn->bi, target_node, key) == SDS_KEY_PRESENT)
{
+ // sds_bptree_node_list_release(&target_path);
+ return SDS_DUPLICATE_KEY;
+ }
+
+ // Prep the node for this.
+ // This will walk up the branch, and prepare the nodes, then the algo
+ // for the insert will "just work", even split (provided you use the
right)
+ // txn id.
+ //
+ // This will be harder for delete due to merging of nodes.
+ cow_node = sds_bptree_cow_node_prepare(btxn, target_node);
+
+ /* The insert will happen now. Get the key ready ... */
+ void *insert_key = btxn->bi->key_dup_fn(key);
+
+ // Insert to the leaf (if it is one)
+ if (cow_node->item_count < SDS_BPTREE_DEFAULT_CAPACITY) {
+ sds_bptree_leaf_insert(btxn->bi, cow_node, insert_key, value);
+ } else {
+ sds_bptree_cow_leaf_split_and_insert(btxn, cow_node, insert_key, value);
+ }
+ // Else, insert to leaf and let things happen.
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_insert", "<== Finishing insert of %p",
key);
+#endif
+
+ return SDS_SUCCESS;
+}
+
+sds_result sds_bptree_cow_update(sds_bptree_transaction *btxn, void *key, void *value) {
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_node *cow_node = NULL;
+ sds_bptree_node *target_node = NULL;
+
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_update", "==> Beginning update of %d",
key);
+#endif
+ // Need to fail if txn is RO
+ if (btxn == NULL || btxn->state != SDS_TXN_WRITE) {
+ return SDS_INVALID_TXN;
+ }
+
+ // First check the key doesn't already exist.
+ if (key == NULL) {
+ return SDS_INVALID_KEY;
+ }
+
+ // This grabs the node, but updates the path parent pointers as we descend down.
+ result = sds_bptree_search_node_path(btxn, key, &target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ if (sds_bptree_node_contains_key(btxn->bi, target_node, key) ==
SDS_KEY_NOT_PRESENT) {
+ // Call insert instead!
+ sds_bptree_cow_insert(btxn, key, value);
+ } else {
+
+ cow_node = sds_bptree_cow_node_prepare(btxn, target_node);
+
+ sds_bptree_cow_node_update(btxn, cow_node, key, value);
+ }
+
+ // Else, insert to leaf and let things happen.
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_update", "<== Finishing update of %d",
key);
+#endif
+ return SDS_SUCCESS;
+}
+
+// Does this need to work on a transaction perhaps to verify the tree is
"sane"?
+sds_result
+sds_bptree_cow_verify(sds_bptree_cow_instance *binst) {
+ sds_result result = SDS_SUCCESS;
+ // Verify the instance
+#ifdef DEBUG
+ if (binst->bi->offline_checksumming) {
+ result = sds_bptree_crc32c_verify_cow_instance(binst);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_instance(binst->bi);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+ // First, take an ro txn. We just verify this.
+ sds_bptree_transaction *btxn = NULL;
+ result = sds_bptree_cow_rotxn_begin(binst, &btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+
+#ifdef DEBUG
+ if (binst->bi->offline_checksumming) {
+ result = sds_bptree_crc32c_verify_btxn(btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+ /* Now verify the tree. */
+ result = sds_bptree_map_nodes(binst->bi, btxn->root,
sds_bptree_cow_verify_node);
+
+ /* Close the txn */
+ sds_bptree_cow_rotxn_close(&btxn);
+
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_verify", "--> Verification result
%d\n", result);
+#endif
+
+ return result;
+}
+
+static sds_result
+sds_node_to_dot(sds_bptree_instance *binst __attribute__((unused)), sds_bptree_node
*node) {
+ if (node == NULL) {
+ return SDS_INVALID_NODE;
+ }
+ // Given the node write it out as:
+ fprintf(fp, "subgraph c%"PRIu64" { \n
rank=\"same\";\n", node->level);
+ fprintf(fp, " node_%p [label =\" { node=%p items=%d
txn=%"PRIu64" parent=%p | { <f0> ", node, node, node->item_count,
node->txn_id, node->parent );
+ for (size_t i = 0; i < SDS_BPTREE_DEFAULT_CAPACITY; i++) {
+ fprintf(fp, "| %" PRIu64 " | <f%"PRIu64"> ",
(uint64_t)node->keys[i], i + 1 );
+ }
+ fprintf(fp, "}}\"]; \n}\n");
+ return SDS_SUCCESS;
+}
+
+static sds_result
+sds_node_ptrs_to_dot(sds_bptree_instance *binst __attribute__((unused)), sds_bptree_node
*node) {
+ // for a given node,
+ // printf("\"node0\":f0 -> \"node1\"");
+ if (node->level == 0) {
+ if (node->values[SDS_BPTREE_DEFAULT_CAPACITY] != NULL) {
+ // Work around a graphviz display issue, with Left and Right pointers
+ fprintf(fp, "\"node_%p\" -> \"node_%p\";
\n", node, node->values[SDS_BPTREE_DEFAULT_CAPACITY] );
+ }
+ } else {
+ for (size_t i = 0; i < SDS_BPTREE_BRANCH; i++) {
+ if (node->values[i] != NULL) {
+ if (i == SDS_BPTREE_DEFAULT_CAPACITY) {
+ // Work around a graphviz display issue, with Left and Right
pointers
+ fprintf(fp, "\"node_%p\" -> \"node_%p\";
\n", node, node->values[i] );
+ } else {
+ fprintf(fp, "\"node_%p\":f%"PRIu64" ->
\"node_%p\"; \n", node, i, node->values[i] );
+ }
+ }
+ }
+ }
+
+ return SDS_SUCCESS;
+}
+
+
+sds_result
+sds_bptree_cow_display(sds_bptree_transaction *btxn) {
+ sds_result result = SDS_SUCCESS;
+
+ char *path = malloc(sizeof(char) * 20);
+ print_iter += 1;
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_display", "Writing step %03d\n",
print_iter);
+#endif
+ sprintf(path, "/tmp/graph_%03"PRIu64".dot", print_iter);
+
+ /* Show the trees for all transactions. How can we do this atomically */
+ fp = fopen(path, "w+");
+
+ fprintf(fp, "digraph g {\n");
+ fprintf(fp, "node [shape = record,height=.1];\n");
+
+ fprintf(fp, "nodehdr[label = \"COW B+Tree txn_id=%"PRIu64"
\"];\n", btxn->txn_id);
+
+ // Problem here is that fp in sds_node_to_dot is not in this file ....
+ result = sds_bptree_map_nodes(btxn->bi, btxn->root, sds_node_to_dot);
+ // HANDLE THE RESULT
+ result = sds_bptree_map_nodes(btxn->bi, btxn->root, sds_node_ptrs_to_dot);
+ // HANDLE THE RESULT
+
+ // Should this write a dot file?
+ fprintf(fp, "}\n");
+ fclose(fp);
+ fp = NULL;
+ free(path);
+
+ return result;
+
+}
diff --git a/src/libsds/sds/bpt_cow/bpt_cow.h b/src/libsds/sds/bpt_cow/bpt_cow.h
new file mode 100644
index 0000000..4c0adc0
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/bpt_cow.h
@@ -0,0 +1,47 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#pragma once
+
+#include "../bpt/bpt.h"
+
+/* Transaction management */
+
+sds_bptree_transaction *sds_bptree_txn_create(sds_bptree_cow_instance *binst);
+sds_result sds_bptree_cow_txn_destroy_all(sds_bptree_cow_instance *binst);
+
+/* Verification */
+
+#ifdef DEBUG
+sds_result sds_bptree_crc32c_verify_cow_instance(sds_bptree_cow_instance *binst);
+void sds_bptree_crc32c_update_cow_instance(sds_bptree_cow_instance *binst);
+sds_result sds_bptree_crc32c_verify_btxn(sds_bptree_transaction *btxn);
+void sds_bptree_crc32c_update_btxn(sds_bptree_transaction *btxn);
+#endif
+sds_result sds_bptree_cow_verify_node(sds_bptree_instance *binst, sds_bptree_node
*node);
+
+/* txn node manipulation */
+
+sds_bptree_node * sds_bptree_cow_node_prepare(sds_bptree_transaction *btxn,
sds_bptree_node *node);
+sds_bptree_node * sds_bptree_cow_node_create(sds_bptree_transaction *btxn);
+void sds_bptree_cow_node_update(sds_bptree_transaction *btxn, sds_bptree_node *node, void
*key, void *value);
+void sds_bptree_cow_node_siblings(sds_bptree_node *target, sds_bptree_node **left,
sds_bptree_node **right);
+
+void sds_bptree_cow_leaf_compact(sds_bptree_transaction *btxn, sds_bptree_node *left,
sds_bptree_node *right);
+void sds_bptree_cow_root_promote(sds_bptree_transaction *btxn, sds_bptree_node *root);
+void sds_bptree_cow_branch_compact(sds_bptree_transaction *btxn, sds_bptree_node *left,
sds_bptree_node *right);
+
+/* Insert algo parts */
+
+void sds_bptree_cow_leaf_split_and_insert(sds_bptree_transaction *btxn, sds_bptree_node
*left_node, void *key, void *value);
+
+/* Misc */
+
+sds_result sds_bptree_cow_display(sds_bptree_transaction *btxn);
+
+sds_result sds_bptree_search_node_path(sds_bptree_transaction *btxn, void *key,
sds_bptree_node **target_out_node);
diff --git a/src/libsds/sds/bpt_cow/delete.c b/src/libsds/sds/bpt_cow/delete.c
new file mode 100644
index 0000000..d39a836
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/delete.c
@@ -0,0 +1,94 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt_cow.h"
+
+void
+sds_bptree_cow_leaf_compact(sds_bptree_transaction *btxn, sds_bptree_node *left,
sds_bptree_node *right) {
+ /* Append content of right to left, mark right for free. */
+
+ /*
+ * In cow this is a bit different.
+ * 1) We take an un-copied right node and clone the values to left.
+ * 2) We put right onto the owned list.
+ * 3) The cow_delete fn, puts right as the node to delete so it's cleared from
parent.
+ *
+ * WARNING: DO NOT FREE RIGHT NODE!!!! It's still alive in a past txn!!!
+ */
+
+ for (size_t i = 0; i < right->item_count; i++) {
+ left->keys[left->item_count] =
btxn->bi->key_dup_fn(right->keys[i]);
+ if (right->values[i] != NULL && btxn->bi->value_dup_fn != NULL)
{
+ left->values[left->item_count] =
btxn->bi->value_dup_fn(right->values[i]);
+ } else {
+ left->values[left->item_count] = NULL;
+ }
+ left->item_count++;
+ }
+ /*
+ * THIS IS THE KEY CHANGE!!!
+ * We can't delete right as it's refed in the past txn, but we need it to be
+ * cleaned after this txn is commit and all old ros are free. So mark it as owned
+ * and it should "just work". Also means that abort works correctly.
+ */
+ sds_bptree_node_list_push(&(btxn->owned), right);
+
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ // Update this becuase we are updating the owned node lists.
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+}
+
+void
+sds_bptree_cow_branch_compact(sds_bptree_transaction *btxn, sds_bptree_node *left,
sds_bptree_node *right) {
+ /* REMEMBER: Left is COWed, right is NOT */
+ /* Append content of right to left, mark right for deletion */
+
+ /* We have to recreate the missing intermediate key. */
+ left->keys[left->item_count] =
btxn->bi->key_dup_fn(sds_bptree_node_leftmost_child_key(right));
+ /* Node reference, so can just copy */
+ left->values[left->item_count + 1] = right->values[0];
+ left->item_count++;
+
+
+ /* Has about a 3 times impact */
+ for (size_t i = 0; i < right->item_count; i++) {
+ left->keys[left->item_count] =
btxn->bi->key_dup_fn(right->keys[i]);
+ /* These are all node pointers, so we can just copy them as is. */
+ left->values[left->item_count + 1] = right->values[i + 1];
+ left->item_count++;
+ }
+
+ sds_bptree_node_list_push(&(btxn->owned), right);
+
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left);
+ // Update this becuase we are updating the owned node lists.
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+}
+
+
+void
+sds_bptree_cow_root_promote(sds_bptree_transaction *btxn, sds_bptree_node *root) {
+ /* Current root is empty! We have one child, so we promote them. */
+ btxn->root = (sds_bptree_node *)root->values[0];
+ sds_bptree_node_list_push(&(btxn->owned), root);
+ btxn->root->parent = NULL;
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(btxn->root);
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+}
diff --git a/src/libsds/sds/bpt_cow/insert.c b/src/libsds/sds/bpt_cow/insert.c
new file mode 100644
index 0000000..912ab0a
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/insert.c
@@ -0,0 +1,155 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt_cow.h"
+
+void
+sds_bptree_cow_root_insert(sds_bptree_transaction *btxn, sds_bptree_node *left_node,
sds_bptree_node *right_node, void *key)
+{
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_root_insert", "left_node %p, key %d,
right_node %p", left_node, key, right_node);
+#endif
+ // Just make the new root, add the nodes, and update the root in the txn.
+ sds_bptree_node *root_node = sds_bptree_cow_node_create(btxn);
+ root_node->level = left_node->level + 1;
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_root_insert", "New root level %d",
root_node->level);
+#endif
+ root_node->keys[0] = key;
+ root_node->values[0] = (void *)left_node;
+ root_node->values[1] = (void *)right_node;
+ root_node->item_count = 1;
+ // Isn't this aready done in create?
+ /* root_node->parent = NULL;
+ left_node->parent = root_node;
+ right_node->parent = root_node; */
+ btxn->root = root_node;
+
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(root_node);
+ sds_bptree_crc32c_update_node(left_node);
+ sds_bptree_crc32c_update_node(right_node);
+ sds_bptree_crc32c_update_instance(btxn->bi);
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+}
+
+void
+sds_bptree_cow_leaf_node_insert(sds_bptree_transaction *btxn, sds_bptree_node *lnode,
sds_bptree_node *rnode, void *key)
+{
+
+ sds_bptree_node *left_node = lnode;
+ sds_bptree_node *right_node = NULL;
+ sds_bptree_node *next_right_node = rnode;
+ sds_bptree_node *parent_node = lnode->parent;
+ void *next_key = key;
+
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_leaf_node_insert", "parent_node %p, left_node
%p, key %d, right_node %p", parent_node, left_node, key, next_right_node);
+#endif
+
+ /*
+ * How does this magic work? Well, because we did a split and insert, we now
+ * have a new_node, but it's not in it's parent yet! IE
+ * [ left_node->parent ]
+ * /
+ * [ left_node ] -> [ next_right_node ]
+ * Were target node the root, we would just create the new root, stick the
+ * values in and be done. But we need to get this into parent.
+ * So by leaving new_node in that spot, the while loop triggers and we
+ * try again with the new_node->keys[0] as the key, and new_node as
+ * as the insertion target.
+ *
+ * If we don't have the space, we split and insert as:
+ * [ left_node->parent ] [ right_node ]
+ * / \
+ * [ left_node ] -> [ next_right_node ]
+ * Then right_node is new, so we shift it to the next_right_node, and left parent
+ * to the left as:
+ * [ left_node->parent ]
+ * /
+ * [ left_node ] [ next_right_node ] <<-- To be inserted.
+ * / \
+ * [ left_node ] ....... [ next_right_node ] // This layer is done ....
+ */
+
+ while (parent_node != NULL && next_right_node != NULL && next_key !=
NULL) {
+ left_node = parent_node;
+ parent_node = left_node->parent;
+ if (left_node->item_count < SDS_BPTREE_DEFAULT_CAPACITY) {
+ // We have space in the parent. Too easy!!
+ sds_bptree_branch_insert(btxn->bi, left_node, next_key, next_right_node);
+ next_right_node = NULL;
+ next_key = NULL;
+ } else {
+ // Do we create this in the next function?
+ right_node = sds_bptree_cow_node_create(btxn);
+ right_node->level = left_node->level;
+ right_node->parent = left_node->parent;
+ void *copy_key = next_key;
+ sds_bptree_branch_split_and_insert(btxn->bi, left_node, right_node,
copy_key, next_right_node, &next_key);
+ next_right_node = right_node;
+ }
+ }
+
+ if (next_right_node != NULL) {
+ sds_bptree_cow_root_insert(btxn, left_node, next_right_node, next_key);
+ }
+}
+
+void
+sds_bptree_cow_leaf_split_and_insert(sds_bptree_transaction *btxn, sds_bptree_node
*left_node, void *key, void *value)
+{
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_leaf_split_and_insert", "left_node %p, key %d,
right_node TBA", left_node, key);
+#endif
+ void *next_key = NULL;
+ // Make the new node for this txn.
+ sds_bptree_node *right_node = sds_bptree_cow_node_create(btxn);
+ right_node->level = left_node->level;
+ right_node->parent = left_node->parent;
+ // Split the contents up.
+
+ for (size_t i = 0; i < (SDS_BPTREE_HALF_CAPACITY - 1); i++) {
+ right_node->keys[i] = left_node->keys[i + SDS_BPTREE_HALF_CAPACITY];
+ right_node->values[i] = left_node->values[i + SDS_BPTREE_HALF_CAPACITY];
+ right_node->item_count += 1;
+ // Now clear the values out.
+ left_node->keys[i + SDS_BPTREE_HALF_CAPACITY] = NULL;
+ left_node->values[i + SDS_BPTREE_HALF_CAPACITY] = NULL;
+ left_node->item_count -= 1;
+ }
+
+ if (btxn->bi->key_cmp_fn(key, right_node->keys[0]) >= 1) {
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(left_node);
+ }
+#endif
+ sds_bptree_leaf_insert(btxn->bi, right_node, key, value);
+ } else {
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(right_node);
+ }
+#endif
+ sds_bptree_leaf_insert(btxn->bi, left_node, key, value);
+ }
+
+ // Now we need to push the new right_node to our parents. Remember,
+ // left_node is already clone preped, so we can just walk up the parents.
+
+ // Dup the right_node key[0] for next key.
+ next_key = btxn->bi->key_dup_fn(right_node->keys[0]);
+
+ sds_bptree_cow_leaf_node_insert(btxn, left_node, right_node, next_key);
+}
+
+
diff --git a/src/libsds/sds/bpt_cow/node.c b/src/libsds/sds/bpt_cow/node.c
new file mode 100644
index 0000000..3fec40b
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/node.c
@@ -0,0 +1,252 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt_cow.h"
+
+#ifdef DEBUG
+static sds_result
+sds_bptree_cow_node_path_verify(sds_bptree_transaction *btxn, sds_bptree_node *old_node,
sds_bptree_node *node) {
+ // Verify that the node has a valid parent path back to the root!
+ sds_bptree_node *boom = NULL;
+ uint64_t level = old_node->level;
+ sds_bptree_node *target_node = node;
+ while (target_node->parent != NULL) {
+ if (target_node->level != level) {
+ sds_log("sds_bptree_cow_node_path_verify", "node_%p should be
level %d, is %d", target_node, level, target_node->level);
+ boom->level = 0xffffffff;
+ }
+ level = level + 1;
+ target_node = target_node->parent;
+ }
+ if (btxn->root != target_node) {
+ return SDS_INVALID_POINTER;
+ }
+ return SDS_SUCCESS;
+}
+#endif
+
+void
+sds_bptree_cow_node_siblings(sds_bptree_node *target, sds_bptree_node **left,
sds_bptree_node **right) {
+ /*
+ * This has one difference to sds_bptree_node_siblings
+ * which is that we update the parent pointer of left and right after we get them.
+ * This is important, as without this, we can't walk back the tree to fix thetxn
+ * parts.
+ *
+ * Remember, node will have been COWed for this txn, so the parent path is already
+ * correct on the way back, so target->parent is valid as the parent to both left
+ * and right.
+ */
+ sds_bptree_node_siblings(target, left, right);
+ if (*left != NULL) {
+ (*left)->parent = target->parent;
+ }
+ if (*right != NULL) {
+ (*right)->parent = target->parent;
+ }
+}
+
+static sds_bptree_node *
+sds_bptree_cow_node_clone(sds_bptree_transaction *btxn, sds_bptree_node *node) {
+ sds_bptree_node *clone_node = sds_memalign(sizeof(sds_bptree_node),
SDS_CACHE_ALIGNMENT);
+
+ clone_node->level = node->level;
+ clone_node->item_count = node->item_count;
+ clone_node->txn_id = btxn->txn_id;
+ clone_node->parent = NULL;
+
+ // For each key / value, dup them.
+ if (node->level > 0) {
+ /* This is a branch, treat it as such. */
+ for (size_t i = 0; i < node->item_count; i++) {
+ clone_node->keys[i] = btxn->bi->key_dup_fn(node->keys[i]);
+ memcpy(&(clone_node->values), &(node->values), sizeof(void *) *
SDS_BPTREE_BRANCH);
+ }
+ } else {
+ /* This is a leaf, clone the values correctly. */
+ for (size_t i = 0; i < node->item_count; i++) {
+ clone_node->keys[i] = btxn->bi->key_dup_fn(node->keys[i]);
+ if (node->values[i] != NULL && btxn->bi->value_dup_fn !=
NULL) {
+ clone_node->values[i] =
btxn->bi->value_dup_fn(node->values[i]);
+ } else {
+ clone_node->values[i] = NULL;
+ }
+ }
+ }
+
+ // Now push the previous node to the txn list.
+ sds_bptree_node_list_push(&(btxn->owned), node);
+ sds_bptree_node_list_push(&(btxn->created), clone_node);
+
+#ifdef DEBUG
+ printf("sds_bptree_cow_node_clone: Cloning node_%p item_count=%d --> clone
node_%p\n", node, node->item_count, clone_node);
+ printf("sds_bptree_cow_node_clone: txn_%p tentatively owns node_%p for
cleaning\n", btxn->parent_txn, node);
+#endif
+
+ return clone_node;
+}
+
+
+/**
+ * This function is very important. When we are done commiting in the tree, we need
+ * to walk back up to the root, and clone all the branch nodes to make the copied
+ * path.
+ *
+ * At the end, we commit our new root to the txn. This relies on checking the txn_ids
+ * and such.
+ *
+ * HINT: This is the "COPY" part of the "COPY ON WRITE".
+ *
+ * IMPORTANT: This will change the target path to be the "copied" path now.
+ */
+
+static void
+sds_bptree_cow_branch_clone(sds_bptree_transaction *btxn, sds_bptree_node *origin_node,
sds_bptree_node *clone_node) {
+
+#ifdef DEBUG
+ printf("sds_bptree_cow_branch_clone: Cloning branch from node_%p\n",
clone_node);
+#endif
+ // Right, we have now cloned the node. We need to walk up the tree and clone
+ // all the branches that path to us.
+ sds_bptree_node *former_origin_node = origin_node;
+ sds_bptree_node *former_clone_node = clone_node;
+ sds_bptree_node *parent_node = NULL;
+ sds_bptree_node *parent_clone_node = NULL;
+
+ while (former_origin_node != NULL && former_origin_node->parent != NULL)
{
+ // Get the next parent node out then.
+ parent_node = former_origin_node->parent;
+
+ // This node has a parent, so we need to update it.
+ if (parent_node->txn_id == btxn->txn_id) {
+ // Then we are done, the whole path up to the root is already in this txn
now.
+ sds_bptree_node_node_replace(parent_node, former_origin_node,
former_clone_node);
+
+// TODO: This probably needs to update csums of the nodes along the branch.
+#ifdef DEBUG
+ printf("sds_bptree_cow_branch_clone: Branch parent node_%p already
within txn, finishing...\n", parent_node);
+ if (btxn->bi->offline_checksumming) {
+ // Update this becuase we are updating the owned node lists.
+ sds_bptree_crc32c_update_node(parent_node);
+ }
+#endif
+ // BAIL EARLY
+ return;
+ } else {
+ // Is the parent node NOT in this txn?
+ // We need to clone the parent, and then replace ourselves in it ....
+#ifdef DEBUG
+ printf("sds_bptree_cow_branch_clone: Branch parent node_%p NOT within
txn, cloning...\n", parent_node);
+#endif
+ // This is actually the important part!
+ parent_clone_node = sds_bptree_cow_node_clone(btxn, parent_node);
+ sds_bptree_node_node_replace(parent_clone_node, former_origin_node,
former_clone_node);
+
+ // TODO: This probably needs to update csums of the nodes along the branch.
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ // Update this becuase we are updating the owned node lists.
+ sds_bptree_crc32c_update_node(parent_clone_node);
+ sds_bptree_crc32c_update_node(former_clone_node);
+ }
+#endif
+
+ // Update origin_node with clone_node
+ former_origin_node = parent_node;
+ former_clone_node = parent_clone_node;
+ parent_clone_node = NULL;
+
+ }
+
+ }
+ // We have hit the root, update the root.
+ // Origin is root, update the txn root.
+#ifdef DEBUG
+ printf("sds_bptree_cow_branch_clone: Updating txn_%p root from node_%p to
node_%p\n", btxn, btxn->root, former_clone_node);
+#endif
+ btxn->root = former_clone_node;
+}
+
+sds_bptree_node *
+sds_bptree_cow_node_prepare(sds_bptree_transaction *btxn, sds_bptree_node *node) {
+#ifdef DEBUG
+ sds_log("sds_bptree_node_prepare", " --> prepare node %p for
txn_%p", node, btxn);
+ sds_log("sds_bptree_node_prepare", " prepare current tree root
node_%p", btxn->root);
+#endif
+ sds_bptree_node *result_node = NULL;
+ // Check if the btxn id matches the node txn id.
+ if (btxn->txn_id == node->txn_id) {
+ // If it does, return this node.
+ result_node = node;
+ } else {
+ // If not, clone the node and values, return new node.
+ // This will rebuild the target path to the node based on the copy
+ sds_bptree_node *clone_node = sds_bptree_cow_node_clone(btxn, node);
+ sds_bptree_cow_branch_clone(btxn, node, clone_node);
+
+ // TODO: Need to update the btxn checksum?
+
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ // Update this becuase we are updating the owned node lists.
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+
+ result_node = clone_node;
+ }
+#ifdef DEBUG
+ if (sds_bptree_cow_node_path_verify(btxn, node, result_node)!= SDS_SUCCESS) {
+ sds_log("sds_bptree_cow_node_prepare", "!!! Invalid path from
cow_node to root!");
+ return NULL;
+ }
+ sds_log("sds_bptree_node_prepare", " <-- prepared node %p for
txn_%p", node, btxn);
+#endif
+
+ return result_node;
+}
+
+sds_bptree_node *
+sds_bptree_cow_node_create(sds_bptree_transaction *btxn) {
+ sds_bptree_node *node = sds_memalign(sizeof(sds_bptree_node), SDS_CACHE_ALIGNMENT);
+ // Without memset, we need to null the max link in a value
+ node->values[SDS_BPTREE_DEFAULT_CAPACITY] = NULL;
+ node->level = 0xFFFFFFFF;
+ node->item_count = 0;
+ node->parent = NULL;
+ node->txn_id = btxn->txn_id;
+
+ sds_bptree_node_list_push(&(btxn->created), node);
+
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ // Update this becuase we are updating the created node lists.
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+ printf("sds_bptree_cow_node_create: Creating node_%p item_count=%d\n",
node, node->item_count);
+#endif
+
+ return node;
+}
+
+void
+sds_bptree_cow_node_update(sds_bptree_transaction *btxn, sds_bptree_node *node, void
*key, void *value) {
+ // find the key index.
+ size_t index = sds_bptree_node_key_eq_index(btxn->bi, node, key);
+ // value free
+ btxn->bi->value_free_fn(node->values[index]);
+ // insert.
+ node->values[index] = value;
+#ifdef DEBUG
+ if (btxn->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_node(node);
+ }
+#endif
+}
+
+
diff --git a/src/libsds/sds/bpt_cow/search.c b/src/libsds/sds/bpt_cow/search.c
new file mode 100644
index 0000000..1dc312b
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/search.c
@@ -0,0 +1,80 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt_cow.h"
+
+// This is almost the same as search node fast, but it returns the PATH that
+// we took to get there. This is really critical for COW operations, as we can't
+// use parent backrefs to determine this!
+
+sds_result
+sds_bptree_search_node_path(sds_bptree_transaction *btxn, void *key, sds_bptree_node
**target_out_node)
+{
+ sds_bptree_node *parent_node = NULL;
+ sds_bptree_node *target_node = btxn->root;
+ size_t i = 0;
+
+ /* We do this first, as we need the node to pass before we access it! */
+#ifdef DEBUG
+ if (btxn->bi->search_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_btxn(btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_instance(btxn->bi);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_node(target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+branch_loop:
+ while (target_node->level != 0) {
+ target_node->parent = parent_node;
+#ifdef DEBUG
+ if (btxn->bi->search_checksumming) {
+ sds_bptree_crc32c_update_node(target_node);
+ }
+#endif
+ while (i < target_node->item_count) {
+ if (btxn->bi->key_cmp_fn(key, (target_node)->keys[i]) < 0) {
+ parent_node = target_node;
+ target_node = (sds_bptree_node *)target_node->values[i];
+ i = 0;
+ goto branch_loop;
+ } else {
+ i++;
+ }
+ }
+ parent_node = target_node;
+ target_node = (sds_bptree_node
*)target_node->values[target_node->item_count];
+ i = 0;
+#ifdef DEBUG
+ if (btxn->bi->search_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_node(target_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+ }
+ target_node->parent = parent_node;
+#ifdef DEBUG
+ if (btxn->bi->search_checksumming) {
+ sds_bptree_crc32c_update_node(target_node);
+ }
+#endif
+ *target_out_node = target_node;
+ return SDS_SUCCESS;
+
+}
+
diff --git a/src/libsds/sds/bpt_cow/txn.c b/src/libsds/sds/bpt_cow/txn.c
new file mode 100644
index 0000000..676a41f
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/txn.c
@@ -0,0 +1,471 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+
+/* See also
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html */
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+#include "bpt_cow.h"
+
+sds_bptree_transaction *
+sds_bptree_txn_create(sds_bptree_cow_instance *binst) {
+ /* Allocate the transaction id
+ * The trick here is that when we do this, we do txn_id + 1 to the txn, but
+ * we don't alter the id of the binst. This is because when we commit the txn
+ * we push the id into the binst then.
+ */
+ sds_bptree_transaction *btxn = sds_memalign(sizeof(sds_bptree_transaction),
SDS_CACHE_ALIGNMENT);
+ // Reference our creator.
+ btxn->binst = binst;
+ btxn->bi = binst->bi;
+ // All transactions are created as writable
+ btxn->state = SDS_TXN_WRITE;
+ btxn->owned = NULL;
+ btxn->created = NULL;
+
+ if (binst->txn == NULL) {
+ // Starting a new tree!
+ btxn->txn_id = 1;
+ btxn->root = sds_bptree_cow_node_create(btxn);
+ btxn->root->level = 0;
+ } else {
+ // This means we have a txn, so use it!
+ btxn->txn_id = binst->txn->txn_id + 1;
+ btxn->root = binst->txn->root;
+ }
+ // We have no children yet as we aren't commited.
+ btxn->child_txn = NULL;
+ btxn->parent_txn = binst->txn;
+
+ // The initial ref count is 0, and we only up to 1 when we commit.
+ // Atomically set this to 0.
+ __atomic_and_fetch(&(btxn->reference_count), 0, __ATOMIC_SEQ_CST);
+
+#ifdef DEBUG
+ // Update our needed checksums
+ if (binst->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_btxn(btxn);
+ sds_bptree_crc32c_update_node(btxn->root);
+ }
+#endif
+
+ return btxn;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+// Should be caled by txn decrement.
+static void
+sds_bptree_txn_free(sds_bptree_transaction *btxn) {
+#ifdef DEBUG
+ sds_log("sds_bptree_txn_free", " Freeing READ txn_%p rc
%d", btxn, btxn->reference_count);
+#endif
+ sds_bptree_node *target_node = sds_bptree_node_list_pop(&(btxn->owned));
+ // This frees only the nodes *related* to this txn.
+ while (target_node != NULL) {
+#ifdef DEBUG
+ sds_log("sds_bptree_txn_free", " READ txn_%p owned node
node_%p", btxn, target_node);
+#endif
+ sds_bptree_node_destroy(btxn->binst->bi, target_node);
+ // Right, what do we do here about binst!!!
+ target_node = sds_bptree_node_list_pop(&(btxn->owned));
+ }
+ sds_free(btxn);
+#ifdef DEBUG
+ sds_log("sds_bptree_txn_free", " Freed READ txn");
+#endif
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+static void
+sds_bptree_txn_increment(sds_bptree_transaction *btxn) {
+ __atomic_add_fetch(&(btxn->reference_count), 1, __ATOMIC_SEQ_CST);
+
+ // PR_AtomicIncrement(&(btxn->reference_count));
+#ifdef DEBUG
+ if (btxn->binst->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+static void
+sds_bptree_txn_decrement(sds_bptree_transaction *btxn) {
+#ifdef DEBUG
+ sds_log("sds_bptree_txn_decrement", " txn_%p %d - 1", btxn,
btxn->reference_count );
+#endif
+ sds_bptree_cow_instance *binst = btxn->binst;
+
+ // Atomic dec the counter.
+ // PR_AtomicDecrement returns the set value.
+ uint32_t result = __atomic_sub_fetch(&(btxn->reference_count), 1,
__ATOMIC_SEQ_CST);
+ /* WARNING: After this point, another thread MAY free btxn under us.
+ * You MUST *not* deref btxn after this point.
+ */
+#ifdef DEBUG
+ sds_log("sds_bptree_txn_decrement", " ==
%d", result );
+ /* WARNING: This *may* in some cases trigger a HUAF ...
+ * Is this reason to ditch the checksum of the txn, or to make a txn lock?
+ */
+ if (result > 0 && binst->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_btxn(btxn);
+ }
+#endif
+ // If the counter is 0 && we are the tail transaction.
+ if (result == 0) {
+ pthread_mutex_lock(binst->vacuum_lock);
+ while (result == 0 && btxn != NULL && btxn == binst->tail_txn)
{
+#ifdef DEBUG
+ sds_log("sds_bptree_txn_decrement", " txn_%p has reached 0,
and is at the tail, vacuumming!", btxn);
+#endif
+ binst->tail_txn = btxn->child_txn;
+ sds_bptree_txn_free(btxn);
+ // Now, we need to check to see if the next txn is ready to free also ....
+ // I'm not sure if this is okay, as we may not have barriered properly.
+ btxn = binst->tail_txn;
+ // This isn't an atomic read, but the way that txns work is that
+ // this will always be >= 1 if this is the alive read, but never
+ // 0. So we may only "miss" freeing a now zerod txn in this case,
+ // but we never free "too much";
+ if (btxn != NULL) {
+ __atomic_load(&(btxn->reference_count), &result,
__ATOMIC_SEQ_CST);
+ }
+ }
+ pthread_mutex_unlock(binst->vacuum_lock);
+ }
+#ifdef DEBUG
+ if (btxn != NULL) {
+ sds_log("sds_bptree_txn_decrement", " txn_%p is at count %d,
and is at the tail, NOT vacuumming!", btxn, result);
+ } else {
+ sds_log("sds_bptree_txn_decrement", " txn tail is now NULL, NOT
vacuumming!", btxn, result);
+ }
+#endif
+
+#ifdef DEBUG
+ // Update our needed checksums
+ if (binst->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_cow_instance(binst);
+ }
+#endif
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+/* Public functions */
+
+sds_result
+sds_bptree_cow_rotxn_begin(sds_bptree_cow_instance *binst, sds_bptree_transaction **btxn)
{
+
+#ifdef DEBUG
+ sds_result result = SDS_SUCCESS;
+ if (binst->bi->offline_checksumming) {
+ result = sds_bptree_crc32c_verify_cow_instance(binst);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_instance(binst->bi);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+
+ /* Lock the read transaction. */
+ pthread_rwlock_rdlock(binst->read_lock);
+ // PR_Lock(binst->read_lock);
+ /* Take a copy */
+ *btxn = binst->txn;
+ /* Increment. */
+ if (*btxn != NULL) {
+ sds_bptree_txn_increment(*btxn);
+ }
+ /* Unlock */
+ pthread_rwlock_unlock(binst->read_lock);
+#ifdef DEBUG
+ if (*btxn != NULL) {
+ sds_log("sds_bptree_cow_rotxn_begin", "==> Beginning READ
txn_%p rc %d", *btxn, (*btxn)->reference_count);
+ } else {
+ sds_log("sds_bptree_cow_rotxn_begin", "==> Beginning READ txn
FAILED. Likely that we are SHUTTING DOWN.");
+ }
+#endif
+ if (*btxn == NULL) {
+ return SDS_INVALID_TXN;
+ }
+ return SDS_SUCCESS;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+sds_result
+sds_bptree_cow_rotxn_close(sds_bptree_transaction **btxn) {
+ if (btxn == NULL || *btxn == NULL) {
+ return SDS_INVALID_TXN;
+ }
+ /* Decrement the counter */
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_rotxn_close", "==> Closing READ txn_%p rc
%d - 1", *btxn, (*btxn)->reference_count);
+#endif
+ sds_bptree_txn_decrement(*btxn);
+ /* Remove the callers reference to us. */
+ *btxn = NULL;
+ return SDS_SUCCESS;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+sds_result sds_bptree_cow_wrtxn_begin(sds_bptree_cow_instance *binst,
sds_bptree_transaction **btxn) {
+#ifdef DEBUG
+ sds_result result = SDS_SUCCESS;
+ if (binst->bi->offline_checksumming) {
+ result = sds_bptree_crc32c_verify_cow_instance(binst);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_instance(binst->bi);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+ // Take the write lock.
+ pthread_mutex_lock(binst->write_lock);
+ // Create the txn
+ *btxn = sds_bptree_txn_create(binst);
+
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_wrtxn_begin", "==> Beginning WRITE
txn_%p", *btxn);
+#endif
+
+ return SDS_SUCCESS;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+sds_result sds_bptree_cow_wrtxn_abort(sds_bptree_transaction **btxn) {
+ if (btxn == NULL || *btxn == NULL) {
+ return SDS_INVALID_TXN;
+ }
+ // This transaction is defunct, mark it read only.
+ (*btxn)->state = SDS_TXN_READ;
+ // Unlock the write lock.
+ pthread_mutex_unlock((*btxn)->binst->write_lock);
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_wrtxn_abort", "==> Aborting WRITE
txn_%p", *btxn);
+#endif
+ // Free and *remove* the list of nodes that we created, they are irrelevant!
+ sds_bptree_node *node = sds_bptree_node_list_pop(&((*btxn)->created));
+ while (node != NULL) {
+ sds_bptree_node_destroy((*btxn)->binst->bi, node);
+ node = sds_bptree_node_list_pop(&((*btxn)->created));
+ }
+ // Destroy the ownership list, we never really took these nodes.
+ sds_bptree_node_list_release(&((*btxn)->owned));
+ (*btxn)->owned = NULL;
+ // Free the transaction.
+ sds_bptree_txn_free(*btxn);
+ /* Remove the callers reference to us */
+ *btxn = NULL;
+ return SDS_SUCCESS;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+sds_result sds_bptree_cow_wrtxn_commit(sds_bptree_transaction **btxn) {
+ if (btxn == NULL || *btxn == NULL) {
+ return SDS_INVALID_TXN;
+ }
+#ifdef DEBUG
+ sds_log("sds_bptree_cow_wrtxn_commit", "==> Committing WRITE
txn_%p", *btxn);
+#endif
+
+ // This prevents a huaf in decrement at the tail of this fn
+ sds_bptree_transaction *parent_txn = (*btxn)->parent_txn;
+
+#ifdef DEBUG
+ sds_result result = SDS_SUCCESS;
+ if ((*btxn)->binst->bi->offline_checksumming) {
+ result = sds_bptree_crc32c_verify_btxn(*btxn);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_cow_instance((*btxn)->binst);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_crc32c_verify_instance((*btxn)->binst->bi);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ result = sds_bptree_map_nodes((*btxn)->bi, (*btxn)->root,
sds_bptree_cow_verify_node);
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+ }
+#endif
+ // Take a reference to the last txn.
+ // sds_bptree_transaction *btxn_last = btxn->binst->txn;
+ // Mark the transaction read only now.
+ (*btxn)->state = SDS_TXN_READ;
+ // Give the ownership list to the last txn so it can cleanup correctly.
+ parent_txn->owned = (*btxn)->owned;
+ (*btxn)->owned = NULL;
+ // Dump our creation list, we are past the point of no return.
+ sds_bptree_node_list_release(&((*btxn)->created));
+
+ // Take the read lock now at the last possible moment.
+ pthread_rwlock_wrlock((*btxn)->binst->read_lock);
+ // Say we are alive and commited.
+ __atomic_add_fetch(&((*btxn)->reference_count), 1, __ATOMIC_SEQ_CST);
+ // Set it.
+ (*btxn)->binst->txn = *btxn;
+ // Update our parent to reference us.
+ parent_txn->child_txn = *btxn;
+#ifdef DEBUG
+ // Update our needed checksums
+ if ((*btxn)->binst->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_cow_instance((*btxn)->binst);
+ sds_bptree_crc32c_update_btxn(*btxn);
+ sds_bptree_crc32c_update_btxn((*btxn)->parent_txn);
+ }
+ sds_log("sds_bptree_cow_wrtxn_commit", "<== Commited WRITE txn_%p
rc %d", *btxn, (*btxn)->reference_count);
+#endif
+ // Unlock the read
+ pthread_rwlock_unlock((*btxn)->binst->read_lock);
+ // Unlock the write
+ pthread_mutex_unlock((*btxn)->binst->write_lock);
+ // Decrement the last transaction.
+ sds_bptree_txn_decrement(parent_txn);
+
+ /* Remove the callers reference to us */
+ *btxn = NULL;
+
+ return SDS_SUCCESS;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
+
+sds_result
+sds_bptree_cow_txn_destroy_all(sds_bptree_cow_instance *binst)
+{
+ sds_result result = SDS_SUCCESS;
+ // Take the read lock!!!
+ pthread_rwlock_wrlock(binst->read_lock);
+
+ // At this point, the last transaction should "own" a few nodes from past
trees.
+ // Free the whole tree now.
+ result = sds_bptree_map_nodes(binst->bi, binst->txn->root,
sds_bptree_node_destroy);
+
+ if (result != SDS_SUCCESS) {
+ return result;
+ }
+
+ // !!! do I need to take the read and write lock here to block out issues?
+ // Or do Ineed a condvar or other flag?
+
+ while (binst->tail_txn != NULL) {
+ // Does this need to be an atomic set?
+ // Perhaps we need a lock on the txn?
+ binst->tail_txn->reference_count = 1;
+ // Set the ref count to 1, and just do a decrement. Vacuum should catch
+ sds_bptree_txn_decrement(binst->tail_txn);
+ // these pretty quickly.
+ }
+ pthread_rwlock_unlock(binst->read_lock);
+
+#ifdef DEBUG
+ // Update our needed checksums
+ if (binst->bi->offline_checksumming) {
+ sds_bptree_crc32c_update_cow_instance(binst);
+ }
+#endif
+
+ return result;
+}
+
+/* ========================= WARNING ============================
+ * UNLESS YOU HAVE READ:
+ *
https://www.kernel.org/doc/Documentation/memory-barriers.txt
+ * and SERIOUSLY understand it, and how it works you *MUST* not
+ * edit this file. This section of the code relies on a deep
+ * understanding of locking and memory barriers.
+ * ==============================================================
+ */
diff --git a/src/libsds/sds/bpt_cow/verify.c b/src/libsds/sds/bpt_cow/verify.c
new file mode 100644
index 0000000..b6eba51
--- /dev/null
+++ b/src/libsds/sds/bpt_cow/verify.c
@@ -0,0 +1,228 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "bpt_cow.h"
+
+/* Node checksumming functions. */
+#ifdef DEBUG
+sds_result
+sds_bptree_crc32c_verify_btxn(sds_bptree_transaction *btxn) {
+ uint32_t checksum = sds_crc32c(0, (const unsigned char *)btxn + sizeof(uint32_t),
sizeof(sds_bptree_transaction) - sizeof(uint32_t));
+ // Iterate over the node list, and add the checksums.
+ sds_bptree_node_list *cur_list = btxn->owned;
+ while (cur_list != NULL) {
+ checksum = sds_crc32c(checksum, (const unsigned char *)cur_list +
sizeof(uint32_t), sizeof(sds_bptree_node_list) - sizeof(uint32_t));
+ cur_list = cur_list->next;
+ }
+
+ if (btxn->checksum == checksum) {
+ return SDS_SUCCESS;
+ } else {
+ return SDS_CHECKSUM_FAILURE;
+ }
+}
+
+void
+sds_bptree_crc32c_update_btxn(sds_bptree_transaction *btxn) {
+
+ uint32_t checksum = sds_crc32c(0, (const unsigned char *)btxn + sizeof(uint32_t),
sizeof(sds_bptree_transaction) - sizeof(uint32_t));
+ // Iterate over the node list, and add the checksums.
+ sds_bptree_node_list *cur_list = btxn->owned;
+ while (cur_list != NULL) {
+ checksum = sds_crc32c(checksum, (const unsigned char *)cur_list +
sizeof(uint32_t), sizeof(sds_bptree_node_list) - sizeof(uint32_t));
+ cur_list = cur_list->next;
+ }
+ // We can only do this because we know the internal structure of this list.
+
+ btxn->checksum = checksum;
+}
+
+
+sds_result
+sds_bptree_crc32c_verify_cow_instance(sds_bptree_cow_instance *binst) {
+ // This starts the check *after* the checksum in the struct
+ if (sds_crc32c(0, (const unsigned char *)binst + sizeof(uint32_t),
sizeof(sds_bptree_cow_instance) - sizeof(uint32_t)) == binst->checksum) {
+ return SDS_SUCCESS;
+ } else {
+ return SDS_CHECKSUM_FAILURE;
+ }
+}
+
+void
+sds_bptree_crc32c_update_cow_instance(sds_bptree_cow_instance *binst) {
+ // This starts the check *after* the checksum in the struct
+ binst->checksum = sds_crc32c(0, (const unsigned char *)binst + sizeof(uint32_t),
sizeof(sds_bptree_cow_instance) - sizeof(uint32_t));
+}
+#endif /* DEBUG */
+
+sds_result
+sds_bptree_cow_verify_node(sds_bptree_instance *binst, sds_bptree_node *node) {
+ // - verify the hash of the node metadata
+#ifdef DEBUG
+ if (binst->offline_checksumming) {
+ sds_result result = sds_bptree_crc32c_verify_node(node);
+ if (result != SDS_SUCCESS) {
+ sds_log("sds_bptree_cow_verify_node", "node_%p failed checksum
validation", node);
+ return result;
+ }
+ }
+#endif
+
+ // - item_count matches number of non-null keys.
+ for (size_t i = 0; i < node->item_count; i++) {
+ if (node->keys[i] == NULL)
+ {
+ printf("%d \n", node->item_count);
+ return SDS_INVALID_KEY;
+ }
+
+ }
+
+
+ // Check that our keys are in order in the node.
+ // We only need to compare left to right, because if we have:
+ // A B C D
+ // If A is < B, C, D, then when we check B, we only need assert B < C, D, as A
< B is already true
+ for (size_t i = 0; i < node->item_count; i++) {
+ for (size_t j = 0; j < i && i != j; j++) {
+ // printf("cmp: ! node->keys[%d] %d > node->keys[%d]
%d\n", j, node->keys[j], i, node->keys[i]);
+ int64_t result = binst->key_cmp_fn(node->keys[i], node->keys[j]);
+ if (result == 0) {
+ return SDS_DUPLICATE_KEY;
+ } else if (result < 0) {
+ // If the key on the right is less than the key on the left
+ return SDS_INVALID_KEY;
+ }
+ }
+ }
+
+ if (node->level == 0) {
+
+ /* Verify the values in the node if we are a leaf. */
+ /* Leaves must always adhere to the properties of the tree. IE half size */
+ // WE CAN'T do this check, because we can't assert if we are the root or
not ...
+ /*
+ if (node->level > 0 && node->item_count <
(SDS_BPTREE_HALF_CAPACITY - 1)) {
+ return SDS_INVALID_NODE;
+ }
+ */
+
+
+ // Check that our highest element is smaller than binst->branch_factor[0]
+ // Because of the above assertion, we now know that our node is in order
+ // And given the next is also, we can assert ALL nodes are in key order.
+ /* This doesn't work because we don't maintain node links. */
+ // Is there a way to assert ordering?
+ /*
+ sds_bptree_node *rnode = (sds_bptree_node
*)node->values[SDS_BPTREE_DEFAULT_CAPACITY];
+
+ if (rnode != NULL) {
+ int64_t result = binst->key_cmp_fn(node->keys[node->item_count - 1],
rnode->keys[0]);
+ if (result > 0) {
+ return SDS_INVALID_KEY;
+ } else if (result == 0) {
+ return SDS_DUPLICATE_KEY;
+ }
+ }
+ */
+
+ // Is there an effective way to do a duplicate key check?
+ // I think the above checks already assert there are no dupes in the tree.
+
+ } else {
+ // As a branch, to be valid we must have at LEAST 1 item and 2 links
+ // The later check will validate the links for us :)
+ if (node->item_count == 0) {
+ return SDS_INVALID_NODE;
+ }
+
+ // Check that all our childrens parent refs are to us.
+ // We can't do backref checks, because of the way that COW works
+ /*
+ for (int i = 0; i <= node->item_count; i++) {
+ sds_bptree_node *cnode = (sds_bptree_node *)node->values[i];
+ if (cnode->parent != node) {
+ return SDS_INVALID_POINTER;
+ }
+ }
+
+ if (binst->root == node) {
+ if (node->parent != NULL) {
+ return SDS_INVALID_POINTER;
+ }
+ }
+ */
+
+ // Check that for key at index i, link i key[0] is < key.
+ // For key at index i, link i + 1, is > key
+ // For one key, there will always be a left and right link. That's the way it
is :)
+ // So we can fail if one is NULL
+ for (uint16_t i = 0; i < node->item_count; i++) {
+ if (node->keys[i] != NULL) {
+ sds_bptree_node *lnode = (sds_bptree_node *)node->values[i];
+ sds_bptree_node *rnode = (sds_bptree_node *)node->values[i + 1];
+
+ if (lnode == NULL || rnode == NULL) {
+ // We should have two children!
+ return SDS_INVALID_POINTER;
+ }
+
+ if (lnode->level != node->level - 1 || rnode->level !=
node->level - 1) {
+ return SDS_INVALID_NODE;
+ }
+ // Check that all left keys are LESS.
+ sds_bptree_node_list *path = NULL;
+ size_t j = 0;
+
+ while (lnode != NULL) {
+ for (j = 0; j < lnode->item_count; j++) {
+ /* This checks that all left keys *and* their children are less
*/
+ int64_t result = binst->key_cmp_fn(lnode->keys[j],
node->keys[i]);
+ if (result >= 0) {
+#ifdef DEBUG
+ sds_log("sds_bptree_verify_node", " fault
is in node %p with left child %p", node, lnode);
+#endif
+ return SDS_INVALID_KEY_ORDER;
+ }
+ if (lnode->level > 0) {
+ sds_bptree_node_list_push(&path, lnode->values[j]);
+ }
+ }
+ if (lnode->level > 0) {
+ sds_bptree_node_list_push(&path, lnode->values[j]);
+ }
+ lnode = sds_bptree_node_list_pop(&path);
+ }
+ // All right keys are greater or equal
+ while (rnode != NULL) {
+ for (j = 0; j < rnode->item_count; j++) {
+ /* This checks that all right keys are greatr or equal and their
children */
+ int64_t result = binst->key_cmp_fn(rnode->keys[j],
node->keys[i]);
+ if (result < 0) {
+#ifdef DEBUG
+ sds_log("sds_bptree_verify_node", " fault
is in node %p with right child %p", node, rnode);
+#endif
+ return SDS_INVALID_KEY_ORDER;
+ }
+ if (rnode->level > 0) {
+ sds_bptree_node_list_push(&path, rnode->values[j]);
+ }
+ }
+ if (rnode->level > 0) {
+ sds_bptree_node_list_push(&path, rnode->values[j]);
+ }
+ rnode = sds_bptree_node_list_pop(&path);
+ } // While rnode
+ }
+ } // end for
+
+ } // end else is_leaf
+
+ return SDS_SUCCESS;
+}
+
diff --git a/src/libsds/sds/core/crc32c.c b/src/libsds/sds/core/crc32c.c
new file mode 100644
index 0000000..0c9f26d
--- /dev/null
+++ b/src/libsds/sds/core/crc32c.c
@@ -0,0 +1,151 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+// For now, we define these manually....
+// Is there a way to detect this!
+#define ARCH_HAVE_SSE4_2
+#define BITS_PER_LONG 64
+
+
+#ifdef ARCH_HAVE_SSE4_2
+
+#if BITS_PER_LONG == 64
+#define REX_PRE "0x48, "
+#define SCALE_F 8
+#else
+#define REX_PRE
+#define SCALE_F 4
+#endif
+
+uint32_t __attribute__ ((pure))
+crc32c_intel_le_hw_byte(uint32_t crc, const unsigned char *data, size_t length)
+{
+
+ while (length--) {
+ __asm__ __volatile__(
+ ".byte 0xf2, 0xf, 0x38, 0xf0, 0xf1"
+ :"=S"(crc)
+ :"0"(crc), "c"(*data)
+ );
+ data++;
+ }
+
+ return crc;
+}
+
+uint32_t sds_crc32c(uint32_t crc, unsigned char const *data, uint64_t length)
+{
+ uint64_t iquotient = length / SCALE_F;
+ uint64_t iremainder = length % SCALE_F;
+#if BITS_PER_LONG == 64
+ uint64_t *ptmp = (uint64_t *) data;
+#else
+ uint32_t *ptmp = (uint32_t *) data;
+#endif
+
+ // uint32_t crc = ~0;
+
+ while (iquotient--) {
+ __asm__ __volatile__(
+ ".byte 0xf2, " REX_PRE "0xf, 0x38, 0xf1, 0xf1;"
+ :"=S"(crc)
+ :"0"(crc), "c"(*ptmp)
+ );
+ ptmp++;
+ }
+ if (iremainder) {
+ crc = crc32c_intel_le_hw_byte(crc, (unsigned char *)ptmp, iremainder);
+ }
+ return crc;
+}
+
+#else
+
+static const uint32_t crc32c_table[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+uint32_t __attribute_pure__
+sds_crc32c(uint32_t crc, const unsigned char *data, size_t length)
+{
+ while (length--) {
+ crc = crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);
+ }
+
+ return crc;
+}
+
+#endif
+
diff --git a/src/libsds/sds/core/utils.c b/src/libsds/sds/core/utils.c
new file mode 100644
index 0000000..85ad9bc
--- /dev/null
+++ b/src/libsds/sds/core/utils.c
@@ -0,0 +1,124 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "../sds_internal.h"
+#include <sds.h>
+
+/* uint64_t as a key functions. */
+
+int64_t
+sds_uint64_t_compare(void *a, void *b) {
+ uint64_t ua = *(uint64_t *)a;
+ uint64_t ub = *(uint64_t *)b;
+ if (ua > ub) {
+ return 1;
+ } else if (ua < ub) {
+ return -1;
+ }
+ return 0;
+}
+
+void *
+sds_uint64_t_dup(void *key) {
+#ifdef DEBUG
+ sds_log("sds_uint64_t_dup", "dup %" PRIu64" ", key);
+#endif
+ uint64_t *newkey = sds_malloc(sizeof(uint64_t));
+ *newkey = *(uint64_t *)key;
+ return (void *)newkey;
+}
+
+void
+sds_uint64_t_free(void *key) {
+ uint64_t *ukey = key;
+#ifdef DEBUG
+ sds_log("sds_uint64_t_free", "freeing %" PRIu64" @
%p", *ukey, key);
+#endif
+ sds_free(ukey);
+ return;
+}
+
+/* We have to provide some wrappers to strcmp and such for casting */
+int64_t
+sds_strcmp(void *a, void *b) {
+ return (int64_t)strcmp((const char *)a, (const char *)b);
+}
+
+void *
+sds_strdup(void *key) {
+ return (void *)strdup((const char *)key);
+}
+
+
+/*
+ * sds_log
+ *
+ * This allows us to write a log message to an output.
+ * Similar to malloc, by defining this, we can change the impl later.
+ */
+void
+sds_log(char *id, char *msg, ...) {
+ printf("%s: ", id);
+ va_list subs;
+ va_start(subs, msg);
+ vprintf(msg, subs);
+ va_end(subs);
+ printf("\n");
+ return;
+}
+
+/*
+ * sds_malloc
+ *
+ * By wrapping our malloc call, we can forcefully check for malloc errors
+ * so that we don't need to in our callers.
+ *
+ * It also will allow a change to the allocator in the future if required.
+ */
+void *
+sds_malloc(size_t size) {
+ void *ptr = NULL;
+ ptr = malloc(size);
+ if (ptr == NULL) {
+ sds_log("sds_malloc", "CRITICAL: Unable to allocate
memory!");
+ exit (1);
+ }
+ return ptr;
+}
+
+void *
+sds_calloc(size_t size) {
+ void *ptr = NULL;
+ ptr = calloc(1, size);
+ if (ptr == NULL) {
+ sds_log("sds_calloc", "CRITICAL: Unable to allocate
memory!");
+ exit (1);
+ }
+ return ptr;
+}
+
+void *
+sds_memalign(size_t size, size_t alignment)
+{
+ void *ptr = NULL;
+ if (posix_memalign(&ptr, alignment, size) != 0) {
+ sds_log("sds_memalign", "CRITICAL: Unable to allocate
memory!");
+ exit (1);
+ }
+ return ptr;
+}
+
+void
+sds_free(void *ptr) {
+ if (ptr != NULL) {
+ free(ptr);
+ }
+}
+
+
+
diff --git a/src/libsds/sds/queue/lqueue.c b/src/libsds/sds/queue/lqueue.c
new file mode 100644
index 0000000..1b0253c
--- /dev/null
+++ b/src/libsds/sds/queue/lqueue.c
@@ -0,0 +1,238 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "queue.h"
+/*
+ * The tqueue, or "Thread safe queue" attempts to use a lock free queue
+ * if we have platform support, or it will us the mutex locked variant of queue.
+ */
+
+#ifdef ATOMIC_QUEUE_OPERATIONS
+
+#include <liblfds710.h>
+
+#define GC_HISTORY 32
+
+/* Due to the masking in sds.h, you don't need to typedef this */
+struct _sds_lqueue {
+ /**
+ * The lfds queue state.
+ */
+ struct lfds710_queue_umm_state queue;
+ /**
+ * Function pointer that can free enqueued values.
+ */
+ void (*value_free_fn)(void *value);
+ /**
+ * NSPR thread private data index for this queue to allow garbage collection.
+ * without conflicting other running queues.
+ */
+ PRUintn gc_index;
+};
+
+typedef struct _sds_lqueue_gc {
+ uint64_t current_generation;
+ struct lfds710_queue_umm_element *garbage[GC_HISTORY];
+} sds_lqueue_gc;
+
+/* Thread local storage for last dequeued element */
+/*
+ * How and why does this work?
+ * LFDS710 claims:
+ * "Unusually, once a queue element has been dequeued, it can be deallocated.
"
+ *
http://liblfds.org/mediawiki/index.php?title=r7.1.0:Queue_%28unbounded,_m...
+ *
+ * However, during testing, this is not the case:
+ * AddressSanitizer: heap-use-after-free on address 0x60300006cc58
+ * ...
+ * #4 0x7fcf7e00bfc4 in job_queue_dequeue
/home/william/development/389ds/nunc-stans/ns_thrpool.c:248
+ *
+ * We have the following issue.
+ * Thread A will attempt to dequeue the pointer at HEAD. Thread B is *also*
+ * about to attempt the same dequeue. When Thread B issues the load barrier
+ * Thread A has not yet dequeue, so element E1 is still the HEAD.
+ *
+ * Thread A now atomically dequeues the item E1. However, Thread B *still*
+ * has not checked the value. Thread A now frees E1. Thread B in umpmc at line
+ * 99 now does the check of next -> value. This causes the heap use after free
+ *
+ * This means that lfds isn't quite safe for this!
+ *
+ * The queue itself is stable and correct, the element isn't dequeued twice,
it's
+ * just that it's possible for a dequeued and freed value to be reffered to by
+ * another thread. The solution I have for this issue is somewhat inspired by
+ *
https://aturon.github.io/blog/2015/08/27/epoch/
+ *
+ * In thread local storage, we keep the last few elements we have dequeued
+ * with a flag to point between A/B. This gives us a small generation
+ * ring buffer in essence. When we dequeue an element E1, we store the ptr
+ * into the TLS. Then, we dequeue E2, and store it also into TLS. When we dequeue
+ * E3, we now free E1, and store E3 in the TLS. E4 will free E2 and store E4.
+ *
+ * The benefit of this is that when we go to free E1, we can *guarantee* that
+ * Nothing should still be using it, or reffering to it, since we have taken
+ * The next element out of the queue also!
+ */
+
+static void
+sds_lqueue_tprivate_cleanup(void *priv) {
+#ifdef DEBUG
+ sds_log("sds_lqueue_tprivate_cleanup", "Closing thread GC");
+#endif
+ sds_lqueue_gc *gc = (sds_lqueue_gc *)priv;
+ /* For each remaining element. */
+ for (size_t i = 0; i < GC_HISTORY; i++) {
+ /* Do a GC on them. */
+ if (gc->garbage[i] != NULL) {
+ sds_free(gc->garbage[i]);
+ }
+ }
+ /* Free the struct */
+ sds_free(gc);
+}
+
+sds_result
+sds_lqueue_init(sds_lqueue **q_ptr, void (*value_free_fn)(void *value)) {
+#ifdef DEBUG
+ sds_log("sds_lqueue_init", "Creating lock free queue");
+#endif
+ if (q_ptr == NULL) {
+#ifdef DEBUG
+ sds_log("sds_lqueue_init", "Invalid q_ptr");
+#endif
+ return SDS_NULL_POINTER;
+ }
+ struct lfds710_queue_umm_element *qe_dummy = sds_malloc(sizeof(struct
lfds710_queue_umm_element));
+ *q_ptr = sds_memalign(sizeof(sds_lqueue), LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES);
+ lfds710_queue_umm_init_valid_on_current_logical_core(&((*q_ptr)->queue),
qe_dummy, NULL);
+
+ /* Create the thread local storage for GC */
+ if (PR_NewThreadPrivateIndex(&((*q_ptr)->gc_index),
sds_lqueue_tprivate_cleanup) != PR_SUCCESS) {
+#ifdef DEBUG
+ sds_log("sds_lqueue_init", "Unable to create private
index");
+#endif
+ sds_free(*q_ptr);
+ return SDS_UNKNOWN_ERROR;
+ }
+ (*q_ptr)->value_free_fn = value_free_fn;
+
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_lqueue_tprep(sds_lqueue *q) {
+ /* Get ready to run on this core. It's essentially a load barrier or full barrier
*/
+
LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
+ sds_lqueue_gc *gc = NULL;
+ /* Check if we have been initialised. */
+ gc = PR_GetThreadPrivate(q->gc_index);
+ if (gc != NULL) {
+ return SDS_SUCCESS;
+ }
+ /* Prepare the garbage collection. It is important to use Calloc here! */
+ gc = sds_calloc(sizeof(sds_lqueue_gc));
+ /* Set the current generation */
+ gc->current_generation = 0;
+ /* Store the GC into the thread local. */
+ if (PR_SetThreadPrivate(q->gc_index, (void *)gc) != PR_SUCCESS) {
+ return SDS_UNKNOWN_ERROR;
+ }
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_lqueue_enqueue(sds_lqueue *q, void *elem) {
+#ifdef DEBUG
+ sds_log("sds_lqueue_enqueue", "<== lf Queue %p elem %p",
q, elem);
+#endif
+ struct lfds710_queue_umm_element *qe = sds_malloc(sizeof(struct
lfds710_queue_umm_element));
+ LFDS710_QUEUE_UMM_SET_VALUE_IN_ELEMENT(*qe, elem);
+ lfds710_queue_umm_enqueue(&(q->queue), qe);
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_lqueue_dequeue(sds_lqueue *q, void **elem) {
+#ifdef DEBUG
+ sds_log("sds_lqueue_dequeue", "==> lf Queue %p elem %p",
q, elem);
+#endif
+ if (elem == NULL) {
+ return SDS_NULL_POINTER;
+ }
+ struct lfds710_queue_umm_element *qe = NULL;
+ sds_lqueue_gc *gc = PR_GetThreadPrivate(q->gc_index);
+ if (gc == NULL) {
+ return SDS_UNKNOWN_ERROR;
+ }
+ if (lfds710_queue_umm_dequeue(&(q->queue), &qe) && qe) {
+ *elem = LFDS710_QUEUE_UMM_GET_VALUE_FROM_ELEMENT( *qe );
+ if (gc->garbage[gc->current_generation] != NULL) {
+ sds_free(gc->garbage[gc->current_generation]);
+ }
+ gc->garbage[gc->current_generation] = qe;
+ gc->current_generation = (gc->current_generation + 1) % GC_HISTORY;
+ return SDS_SUCCESS;
+ }
+ return SDS_LIST_EXHAUSTED;
+}
+
+void
+sds_lqueue_dummy_cleanup(struct lfds710_queue_umm_state *qs __attribute__((unused)),
struct lfds710_queue_umm_element *qe, enum lfds710_misc_flag dummy_element_flag
__attribute__((unused))) {
+ /* Should only be the dummy! */
+ sds_free(qe);
+}
+
+sds_result
+sds_lqueue_destroy(sds_lqueue *q) {
+ /* All the threads should be shutdown, so they GC themself. */
+ /* There is no guarantee we have been thread preped, so setup GC now if needed. */
+ sds_lqueue_tprep(q);
+ /* We just need to dequeue everything, and free it. */
+ void *ptr = NULL;
+
+ while (sds_lqueue_dequeue(q, &ptr) == SDS_SUCCESS) {
+ if (q->value_free_fn != NULL) {
+ q->value_free_fn(ptr);
+ }
+ }
+ /* The final GC will be triggered on thread delete */
+ /* Finally, use the lfds cleanup to free the dummy */
+ lfds710_queue_umm_cleanup(&(q->queue), sds_lqueue_dummy_cleanup);
+ sds_free(q);
+
+ return SDS_SUCCESS;
+}
+#else
+/* Fall back to our tqueue implementation. */
+sds_result
+sds_lqueue_init(sds_lqueue **q_ptr, void (*value_free_fn)(void *value)) {
+ return sds_tqueue_init(q_ptr, value_free_fn);
+}
+
+sds_result
+sds_lqueue_tprep(sds_lqueue *q __attribute__((unused))) {
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_lqueue_enqueue(sds_lqueue *q, void *elem) {
+ return sds_tqueue_enqueue(q, elem);
+}
+
+sds_result
+sds_lqueue_dequeue(sds_lqueue *q, void **elem) {
+ return sds_tqueue_dequeue(q, elem);
+}
+
+sds_result
+sds_lqueue_destroy(sds_lqueue *q) {
+ return sds_tqueue_destroy(q);
+}
+#endif
+
+
diff --git a/src/libsds/sds/queue/queue.c b/src/libsds/sds/queue/queue.c
new file mode 100644
index 0000000..5cd280e
--- /dev/null
+++ b/src/libsds/sds/queue/queue.c
@@ -0,0 +1,120 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "queue.h"
+
+/* Create */
+//__attribute__ ((visibility ("hidden")))
+
+sds_result
+sds_queue_init(sds_queue **q_ptr, void (*value_free_fn)(void *value)) {
+ if (q_ptr == NULL) {
+#ifdef DEBUG
+ sds_log("sds_queue_init", "Invalid p_ptr");
+#endif
+ return SDS_NULL_POINTER;
+ }
+ *q_ptr = sds_malloc(sizeof(sds_queue));
+ (*q_ptr)->head = NULL;
+ (*q_ptr)->tail = NULL;
+ (*q_ptr)->value_free_fn = value_free_fn;
+ return SDS_SUCCESS;
+}
+
+/* Enqueue */
+
+sds_result
+sds_queue_enqueue(sds_queue *q, void *elem) {
+#ifdef DEBUG
+ sds_log("sds_queue_dequeue", "Queue %p - <== enqueuing", q);
+#endif
+ sds_queue_node *node = sds_malloc(sizeof(sds_queue_node));
+#ifdef DEBUG
+ sds_log("sds_queue_enqueue", "Queue %p - Queueing ptr %p to %p",
q, elem, node);
+#endif
+ node->element = elem;
+ node->prev = NULL;
+ node->next = q->tail;
+ if (q->tail != NULL) {
+ q->tail->prev = node;
+ } else {
+ /* If tail is null, head must ALSO be null. */
+#ifdef DEBUG
+ sds_log("sds_queue_enqueue", "Queue %p - empty, adding %p to head
and tail", q, node);
+#endif
+ q->head = node;
+ }
+ q->tail = node;
+#ifdef DEBUG
+ sds_log("sds_queue_enqueue", "Queue %p - complete head: %p tail:
%p", q, q->head, q->tail);
+#endif
+ return SDS_SUCCESS;
+}
+
+/* Dequeue */
+
+sds_result
+sds_queue_dequeue(sds_queue *q, void **elem) {
+#ifdef DEBUG
+ sds_log("sds_queue_dequeue", "Queue %p - ==> dequeuing", q);
+#endif
+ if (elem == NULL) {
+#ifdef DEBUG
+ sds_log("sds_queue_dequeue", "Queue %p - NULL pointer for
**elem", q);
+#endif
+ return SDS_NULL_POINTER;
+ }
+ if (q->head == NULL) {
+#ifdef DEBUG
+ sds_log("sds_queue_dequeue", "Queue %p - queue exhausted.",
q);
+#endif
+ return SDS_LIST_EXHAUSTED;
+ }
+ sds_queue_node *node = q->head;
+ *elem = node->element;
+ q->head = node->prev;
+ sds_free(node);
+ if (q->head == NULL) {
+ // If we have no head node, we also have no tail.
+ q->tail = NULL;
+ }
+#ifdef DEBUG
+ sds_log("sds_queue_dequeue", "Queue %p - complete head: %p tail:
%p", q, q->head, q->tail);
+#endif
+ return SDS_SUCCESS;
+}
+
+
+/* Map, filter, reduce? */
+
+/* Destroy */
+
+sds_result
+sds_queue_destroy(sds_queue *q) {
+#ifdef DEBUG
+ sds_log("sds_queue_destroy", "Queue %p - destroying", q);
+#endif
+ /* Map over the queue and free the elements. */
+ sds_queue_node *node = q->head;
+ sds_queue_node *prev = NULL;
+ while (node != NULL) {
+ prev = node->prev;
+ if (q->value_free_fn != NULL) {
+#ifdef DEBUG
+ sds_log("sds_queue_destroy", "Queue %p - implicitly freeing
%p", q, node->element);
+#endif
+ q->value_free_fn(node->element);
+ }
+ sds_free(node);
+ node = prev;
+ }
+ sds_free(q);
+ return SDS_SUCCESS;
+}
+
+
diff --git a/src/libsds/sds/queue/queue.h b/src/libsds/sds/queue/queue.h
new file mode 100644
index 0000000..87d31ce
--- /dev/null
+++ b/src/libsds/sds/queue/queue.h
@@ -0,0 +1,13 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#pragma once
+
+#include "../sds_internal.h"
+#include <sds.h>
+
diff --git a/src/libsds/sds/queue/tqueue.c b/src/libsds/sds/queue/tqueue.c
new file mode 100644
index 0000000..ee6ee55
--- /dev/null
+++ b/src/libsds/sds/queue/tqueue.c
@@ -0,0 +1,64 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "queue.h"
+
+/* Keep's your threads safer for longer! */
+/* Want longer lasting threads? */
+
+
+sds_result
+sds_tqueue_init(sds_tqueue **q_ptr, void (*value_free_fn)(void *value)) {
+#ifdef DEBUG
+ sds_log("sds_tqueue_init", "Createing mutex locked queue");
+#endif
+ if (q_ptr == NULL) {
+#ifdef DEBUG
+ sds_log("sds_tqueue_init", "Invalid p_ptr");
+#endif
+ return SDS_NULL_POINTER;
+ }
+ *q_ptr = sds_malloc(sizeof(sds_tqueue));
+ sds_result result = sds_queue_init(&(*q_ptr)->uq, value_free_fn);
+ if (result != SDS_SUCCESS) {
+ sds_free(*q_ptr);
+ return result;
+ }
+ (*q_ptr)->trust_e_threadz = PR_NewLock();
+ return SDS_SUCCESS;
+}
+
+sds_result
+sds_tqueue_enqueue(sds_tqueue *q, void *elem) {
+ PR_Lock(q->trust_e_threadz);
+ sds_result result = sds_queue_enqueue(q->uq, elem);
+ PR_Unlock(q->trust_e_threadz);
+ return result;
+}
+
+sds_result
+sds_tqueue_dequeue(sds_tqueue *q, void **elem) {
+ PR_Lock(q->trust_e_threadz);
+ sds_result result = sds_queue_dequeue(q->uq, elem);
+ PR_Unlock(q->trust_e_threadz);
+ return result;
+}
+
+sds_result
+sds_tqueue_destroy(sds_tqueue *q) {
+ PR_Lock(q->trust_e_threadz);
+ sds_result result = sds_queue_destroy(q->uq);
+ PR_Unlock(q->trust_e_threadz);
+ PR_DestroyLock(q->trust_e_threadz);
+ sds_free(q);
+ return result;
+}
+
+
+
+
diff --git a/src/libsds/sds/sds_internal.h b/src/libsds/sds/sds_internal.h
new file mode 100644
index 0000000..8773d07
--- /dev/null
+++ b/src/libsds/sds/sds_internal.h
@@ -0,0 +1,33 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#pragma once
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h> // for uint64_t
+#include <stdarg.h> // For va_start / va_end
+#include <inttypes.h> // For PRI*
+#include <string.h> // for memset
+// From NSPR
+#include <prlock.h> // For locking in structs
+// #include <pratom.h> // For atomic increments.
+// We use gcc atomic operations instead.
+#include <prlog.h> // For pr_assert
+#include <prthread.h>
+
+
+#include <assert.h> // For assertions.
+#include <pthread.h> // for threads
+
+#define SDS_CACHE_ALIGNMENT 64
+
+void sds_log(char *id, char *msg, ...);
+
+
diff --git a/src/libsds/test/benchmark.c b/src/libsds/test/benchmark.c
new file mode 100644
index 0000000..621501f
--- /dev/null
+++ b/src/libsds/test/benchmark.c
@@ -0,0 +1,591 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+#include "benchmark.h"
+#include <avl.h>
+#include <plhash.h>
+#include <stdio.h>
+
+/* Implement a set of generic benchmarks that use callbacks to get to our tree */
+
+/* AVL wrappers */
+
+int
+avl_intcmp(uint64_t *a, uint64_t *b) {
+ if (*a < *b) {
+ return -1;
+ } else if (*a > *b) {
+ return 1;
+ }
+ return 0;
+}
+
+uint64_t *
+avl_intdup(uint64_t a) {
+ uint64_t *b = malloc(sizeof(uint64_t));
+ *b = a;
+ return b;
+}
+
+int64_t
+avl_init_wrapper(void **inst) {
+ Avlnode **tree = (Avlnode **)inst;
+ *tree = NULLAVL;
+ return 0;
+}
+
+int64_t
+avl_add_wrapper(void **inst, void *key, void *value __attribute__((unused))) {
+ Avlnode **tree = (Avlnode **)inst;
+ uint64_t ukey = (uint64_t)key;
+ if (ukey == 0) {
+ return 1;
+ }
+ return avl_insert(tree, avl_intdup(ukey), avl_intcmp, avl_dup_error);
+}
+
+int64_t
+avl_search_wrapper(void **inst, void *key, void **value_out) {
+ Avlnode **tree = (Avlnode **)inst;
+ uint64_t *ukey = avl_intdup((uint64_t)key);
+ if (ukey == 0) {
+ return 1;
+ }
+ *value_out = avl_find(*tree, ukey, avl_intcmp);
+ free(ukey);
+ if (*value_out == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t
+avl_delete_wrapper(void **inst, void *key) {
+ void *value_out = NULL;
+ Avlnode **tree = (Avlnode **)inst;
+ uint64_t *ukey = avl_intdup((uint64_t)key);
+ if (ukey == 0) {
+ return 1;
+ }
+ /* delete is broken :( */
+ value_out = avl_find(*tree, ukey, avl_intcmp);
+ free(ukey);
+ if (value_out == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t
+avl_destroy_wrapper(void **inst) {
+ Avlnode **tree = (Avlnode **)inst;
+ ( void ) avl_free( *tree, free );
+ return 0;
+}
+
+/* NSPR PLHash wrappers */
+
+/*
+ * This number is chosen because it's the amount used by the NDN cache in
+ * Directory Server. Most of the other caches are 2047 or lower. The exception
+ * is the entry cache, which is often upwards of 20483 buckets. Even with
+ * 20483 buckets, at 1,000,000, we still with perfect hashing devolve to
+ * linked lists of length 48.
+ * If you increase the DS cache to 120MB, you get 245761 slots, which is only
+ * 4 long.
+ * There is a quirk! DS cache only allows 1 item, so the moment the cache
+ * hash matches it's overriden! Uh oh!
+ * With this value of 2053, we are looking at lists of 487 at a maximum!
+ */
+
+#define HASH_BUCKETS_SMALL 2053
+#define HASH_BUCKETS_MED 20483
+#define HASH_BUCKETS_LARGE 245761
+
+PLHashNumber
+hash_func_large(const void *key) {
+ uint64_t ik = (uint64_t)key;
+ return ik % HASH_BUCKETS_LARGE;
+}
+
+PLHashNumber
+hash_func_med(const void *key) {
+ uint64_t ik = (uint64_t)key;
+ return ik % HASH_BUCKETS_MED;
+}
+
+PLHashNumber
+hash_func_small(const void *key) {
+ uint64_t ik = (uint64_t)key;
+ return ik % HASH_BUCKETS_SMALL;
+}
+
+PRIntn
+hash_key_compare (const void *a, const void *b) {
+ uint64_t ia = (uint64_t)a;
+ uint64_t ib = (uint64_t)b;
+ return ia == ib;
+}
+
+PRIntn
+hash_value_compare(const void *a __attribute__((unused)), const void *b
__attribute__((unused))) {
+ // This cheats and says they are always differnt, but I don't think I use this
....
+ return 1;
+}
+
+int64_t
+hash_small_init_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+
+ *table = PL_NewHashTable(HASH_BUCKETS_SMALL, hash_func_small, hash_key_compare,
hash_value_compare, NULL, NULL);
+ return 0;
+}
+
+int64_t
+hash_med_init_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+
+ *table = PL_NewHashTable(HASH_BUCKETS_MED, hash_func_med, hash_key_compare,
hash_value_compare, NULL, NULL);
+ return 0;
+}
+
+int64_t
+hash_large_init_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+
+ *table = PL_NewHashTable(HASH_BUCKETS_LARGE, hash_func_large, hash_key_compare,
hash_value_compare, NULL, NULL);
+ return 0;
+}
+
+int64_t
+hash_add_wrapper(void **inst, void *key, void *value __attribute__((unused))) {
+ PLHashTable **table = (PLHashTable **)inst;
+ // WARNING: We have to add key as value too else hashmap won't add it!!!
+ PL_HashTableAdd(*table, key, key);
+ return 0;
+}
+
+int64_t
+hash_search_wrapper(void **inst, void *key, void **value_out) {
+ PLHashTable **table = (PLHashTable **)inst;
+ *value_out = PL_HashTableLookup(*table, key);
+ if (*value_out == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t
+hash_delete_wrapper(void **inst, void *key) {
+ PLHashTable **table = (PLHashTable **)inst;
+ PL_HashTableRemove(*table, key);
+ return 0;
+}
+
+int64_t
+hash_destroy_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+ PL_HashTableDestroy(*table);
+ return 0;
+}
+
+/* The sds b+ tree wrappers */
+
+int64_t bptree_csum_init_wrapper(void **inst) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_bptree_init(binst, 1, sds_uint64_t_compare, sds_free, sds_uint64_t_free,
sds_uint64_t_dup);
+ return 0;
+}
+
+int64_t bptree_init_wrapper(void **inst) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_bptree_init(binst, 0, sds_uint64_t_compare, sds_free, sds_uint64_t_free,
sds_uint64_t_dup);
+ return 0;
+}
+
+int64_t bptree_add_wrapper(void **inst, void *key, void *value) {
+ // THIS WILL BREAK SOMETIME!
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_bptree_insert(*binst, key, value);
+ return 0;
+}
+
+int64_t bptree_search_wrapper(void **inst, void *key, void **value_out
__attribute__((unused))) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_result result = sds_bptree_search(*binst, key);
+ if (result != SDS_KEY_PRESENT) {
+ // printf("search result is %d\n", result);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_delete_wrapper(void **inst, void *key) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_result result = sds_bptree_delete(*binst, key);
+
+ if (result != SDS_KEY_PRESENT) {
+ // printf("delete result is %d\n", result);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_destroy_wrapper(void **inst) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ // sds_bptree_display(*binst);
+ sds_bptree_destroy(*binst);
+ return 0;
+}
+
+/* sds bptree cow wrapper */
+
+int64_t bptree_cow_init_wrapper(void **inst) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_cow_init(binst, 0, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_dup, sds_uint64_t_free, sds_uint64_t_dup);
+ return 0;
+}
+
+int64_t bptree_cow_add_wrapper(void **inst, void *key, void *value) {
+ // THIS WILL BREAK SOMETIME!
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_cow_insert_atomic(*binst, key, value);
+ return 0;
+}
+
+int64_t bptree_cow_search_wrapper(void **inst, void *key, void **value_out
__attribute__((unused))) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_result result = sds_bptree_cow_search_atomic(*binst, key);
+ if (result != SDS_KEY_PRESENT) {
+ // printf("search result is %d\n", result);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_cow_delete_wrapper(void **inst, void *key) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_result result = sds_bptree_cow_delete_atomic(*binst, key);
+ if (result != SDS_KEY_PRESENT) {
+ // printf("delete result is %d\n", result);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_cow_destroy_wrapper(void **inst) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ // sds_bptree_display(*binst);
+ sds_bptree_cow_destroy(*binst);
+ return 0;
+}
+
+/* The benchmarks */
+
+void
+bench_1_insert_seq(struct b_tree_cb *ds, uint64_t iter) {
+
+ struct timespec start_time;
+ struct timespec finish_time;
+
+ ds->init(&(ds->inst));
+
+ printf("BENCH: Start ...\n");
+
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+ for (uint64_t i = 1; i < iter ; i++) {
+ if (ds->add(&(ds->inst), (void *)(i + 1), NULL) != 0) {
+ printf("FAIL: Error inserting %" PRIu64 " to %s\n", i,
ds->name);
+ break;
+ }
+ }
+
+ // stop time
+ clock_gettime(CLOCK_MONOTONIC, &finish_time);
+
+ printf("BENCH: Complete ...\n");
+
+ // diff time
+ time_t sec = finish_time.tv_sec - start_time.tv_sec;
+ long nsec = finish_time.tv_nsec - start_time.tv_nsec;
+
+ if (nsec < 0) {
+ // It's negative so take one second
+ sec -= 1;
+ // And set nsec to to a whole value
+ nsec = 1000000000 - nsec;
+ }
+
+ printf("BENCH: %s bench_1_insert_seq %"PRIu64" time %" PRId64
".%010" PRId64 "\n", ds->name, iter, sec, nsec );
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_2_search_seq(struct b_tree_cb *ds, uint64_t iter) {
+
+
+ struct timespec start_time;
+ struct timespec finish_time;
+
+ ds->init(&(ds->inst));
+
+ printf("BENCH: Start ...\n");
+
+ for (uint64_t i = 1; i < iter ; i++) {
+ if (ds->add(&(ds->inst), (void *)(i + 1), NULL) != 0) {
+ printf("FAIL: Error inserting %" PRIu64 " to %s\n", i,
ds->name);
+ break;
+ }
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+ void *output;
+ for (uint64_t j = 0; j < 100; j++) {
+ for (uint64_t i = 1; i < iter ; i++) {
+ if (ds->search(&(ds->inst), (void *)(i + 1), &output) != 0) {
+ printf("FAIL: Error finding %" PRIu64 " in %s\n", i,
ds->name);
+ break;
+ }
+ }
+ }
+
+ // stop time
+ clock_gettime(CLOCK_MONOTONIC, &finish_time);
+
+ printf("BENCH: Complete ...\n");
+
+ // diff time
+ time_t sec = finish_time.tv_sec - start_time.tv_sec;
+ long nsec = finish_time.tv_nsec - start_time.tv_nsec;
+
+ if (nsec < 0) {
+ // It's negative so take one second
+ sec -= 1;
+ // And set nsec to to a whole value
+ nsec = 1000000000 - nsec;
+ }
+
+ printf("BENCH: %s bench_2_search_seq %"PRIu64" time %" PRId64
".%010" PRId64 "\n", ds->name, iter, sec, nsec );
+
+ ds->destroy(&(ds->inst));
+}
+
+
+void
+bench_3_delete_seq(struct b_tree_cb *ds, uint64_t iter) {
+
+ struct timespec start_time;
+ struct timespec finish_time;
+
+ ds->init(&(ds->inst));
+
+
+ for (uint64_t i = 1; i < iter ; i++) {
+ if (ds->add(&(ds->inst), (void *)(i + 1), NULL) != 0) {
+ printf("FAIL: Error inserting %" PRIu64 " to %s\n", i,
ds->name);
+ break;
+ }
+ }
+
+ printf("BENCH: Start ...\n");
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+ for (uint64_t i = 1; i < iter ; i++) {
+ if (ds->delete(&(ds->inst), (void *)(i + 1)) != 0) {
+ printf("FAIL: Error deleting %" PRIu64 " from %s\n", i,
ds->name);
+ break;
+ }
+ }
+
+ // stop time
+ clock_gettime(CLOCK_MONOTONIC, &finish_time);
+
+ printf("BENCH: Complete ...\n");
+
+ // diff time
+ time_t sec = finish_time.tv_sec - start_time.tv_sec;
+ long nsec = finish_time.tv_nsec - start_time.tv_nsec;
+
+ if (nsec < 0) {
+ // It's negative so take one second
+ sec -= 1;
+ // And set nsec to to a whole value
+ nsec = 1000000000 - nsec;
+ }
+
+ printf("BENCH: %s bench_3_delete_seq %"PRIu64" time %" PRId64
".%010" PRId64 "\n", ds->name, iter, sec, nsec );
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_4_insert_search_delete_random(struct b_tree_cb *ds, uint64_t iter) {
+ struct timespec start_time;
+ struct timespec finish_time;
+
+ size_t max_factors = iter / 2048;
+
+ size_t step = 0;
+ size_t cf = 0;
+ uint64_t current_step;
+ void *output;
+
+ printf("BENCH: Start ...\n");
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+ ds->init(&(ds->inst));
+
+ /* First, load part of our tree in. */
+
+ for (size_t j = 0; j < max_factors; j++) {
+ for (size_t i = 0; i < 2048 ; i++) {
+ ds->add(&(ds->inst), (void *)(fill_pattern[i] + (2048 << j)),
NULL);
+ }
+ }
+
+ /* Now start stepping, and randomly deleting */
+ for (step = 0; step < (iter * 10); step++) {
+ current_step = step + fill_pattern[step % 2048];
+ int mod = current_step % 10;
+ cf = step % max_factors;
+
+ if (mod >= 8) {
+ ds->delete(&(ds->inst), (void *)(fill_pattern[step % 2048] + (2048
<< cf) ));
+ } else if (mod >= 6) {
+ ds->add(&(ds->inst), (void *)(fill_pattern[step % 2048] + (2048
<< cf)), NULL);
+ } else {
+ ds->search(&(ds->inst), (void *)(fill_pattern[step % 2048] + (2048
<< cf)), &output);
+ }
+
+ }
+
+ // stop time
+ clock_gettime(CLOCK_MONOTONIC, &finish_time);
+
+ printf("BENCH: Complete ... \n");
+
+ // diff time
+ time_t sec = finish_time.tv_sec - start_time.tv_sec;
+ long nsec = finish_time.tv_nsec - start_time.tv_nsec;
+
+ if (nsec < 0) {
+ // It's negative so take one second
+ sec -= 1;
+ // And set nsec to to a whole value
+ nsec = 1000000000 - nsec;
+ }
+
+ printf("BENCH: %s bench_4_insert_search_delete_random %"PRIu64" time
%" PRId64 ".%010" PRId64 "\n", ds->name, iter, sec, nsec );
+
+ ds->destroy(&(ds->inst));
+}
+
+int
+main (int argc __attribute__((unused)), char **argv __attribute__((unused))) {
+ /* Setup the avl tree. */
+ struct b_tree_cb avl_test = {0};
+ avl_test.name = "avl tree";
+ avl_test.inst = NULL;
+ avl_test.init = avl_init_wrapper;
+ avl_test.add = avl_add_wrapper;
+ avl_test.search = avl_search_wrapper;
+ avl_test.delete = avl_delete_wrapper;
+ avl_test.destroy = avl_destroy_wrapper;
+
+ struct b_tree_cb hash_large_test = {0};
+ hash_large_test.name = "pl hashmap large";
+ hash_large_test.inst = NULL;
+ hash_large_test.init = hash_large_init_wrapper;
+ hash_large_test.add = hash_add_wrapper;
+ hash_large_test.search = hash_search_wrapper;
+ hash_large_test.delete = hash_delete_wrapper;
+ hash_large_test.destroy = hash_destroy_wrapper;
+
+ struct b_tree_cb hash_med_test = {0};
+ hash_med_test.name = "pl hashmap medium";
+ hash_med_test.inst = NULL;
+ hash_med_test.init = hash_med_init_wrapper;
+ hash_med_test.add = hash_add_wrapper;
+ hash_med_test.search = hash_search_wrapper;
+ hash_med_test.delete = hash_delete_wrapper;
+ hash_med_test.destroy = hash_destroy_wrapper;
+
+ struct b_tree_cb hash_small_test = {0};
+ hash_small_test.name = "pl hashmap small";
+ hash_small_test.inst = NULL;
+ hash_small_test.init = hash_small_init_wrapper;
+ hash_small_test.add = hash_add_wrapper;
+ hash_small_test.search = hash_search_wrapper;
+ hash_small_test.delete = hash_delete_wrapper;
+ hash_small_test.destroy = hash_destroy_wrapper;
+
+ struct b_tree_cb bptree_test = {0};
+ bptree_test.name = "sds b+tree without search csum";
+ bptree_test.inst = NULL;
+ bptree_test.init = bptree_init_wrapper;
+ bptree_test.add = bptree_add_wrapper;
+ bptree_test.search = bptree_search_wrapper;
+ bptree_test.delete = bptree_delete_wrapper;
+ bptree_test.destroy = bptree_destroy_wrapper;
+
+ struct b_tree_cb bptree_cow_test = {0};
+ bptree_cow_test.name = "sds b+tree with copy on write";
+ bptree_cow_test.inst = NULL;
+ bptree_cow_test.init = bptree_cow_init_wrapper;
+ bptree_cow_test.add = bptree_cow_add_wrapper;
+ bptree_cow_test.search = bptree_cow_search_wrapper;
+ bptree_cow_test.delete = bptree_cow_delete_wrapper;
+ bptree_cow_test.destroy = bptree_cow_destroy_wrapper;
+
+ uint64_t test_arrays[] = {5000, 10000, 100000, 500000, 1000000, 2500000, 5000000,
10000000};
+
+ for (size_t i = 0; i < 2; i++) {
+ bench_1_insert_seq(&avl_test, test_arrays[i]);
+ bench_1_insert_seq(&hash_small_test, test_arrays[i]);
+ bench_1_insert_seq(&hash_med_test, test_arrays[i]);
+ bench_1_insert_seq(&hash_large_test, test_arrays[i]);
+ bench_1_insert_seq(&bptree_test, test_arrays[i]);
+ bench_1_insert_seq(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ bench_2_search_seq(&avl_test, test_arrays[i]);
+ if (test_arrays[i] < 5000000) {
+ bench_2_search_seq(&hash_small_test, test_arrays[i]);
+ }
+ bench_2_search_seq(&hash_med_test, test_arrays[i]);
+ bench_2_search_seq(&hash_large_test, test_arrays[i]);
+ bench_2_search_seq(&bptree_test, test_arrays[i]);
+ bench_2_search_seq(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ bench_3_delete_seq(&avl_test, test_arrays[i]);
+ bench_3_delete_seq(&hash_small_test, test_arrays[i]);
+ bench_3_delete_seq(&hash_med_test, test_arrays[i]);
+ bench_3_delete_seq(&hash_large_test, test_arrays[i]);
+ bench_3_delete_seq(&bptree_test, test_arrays[i]);
+ bench_3_delete_seq(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ bench_4_insert_search_delete_random(&avl_test, test_arrays[i]);
+ bench_4_insert_search_delete_random(&hash_small_test, test_arrays[i]);
+ bench_4_insert_search_delete_random(&hash_med_test, test_arrays[i]);
+ bench_4_insert_search_delete_random(&hash_large_test, test_arrays[i]);
+ bench_4_insert_search_delete_random(&bptree_test, test_arrays[i]);
+ bench_4_insert_search_delete_random(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ printf("======\n");
+ }
+
+}
+
+
diff --git a/src/libsds/test/benchmark.h b/src/libsds/test/benchmark.h
new file mode 100644
index 0000000..f40dbc7
--- /dev/null
+++ b/src/libsds/test/benchmark.h
@@ -0,0 +1,21 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include <time.h>
+#include <pthread.h>
+
+struct b_tree_cb {
+ char *name;
+ void *inst;
+ int64_t (*init)(void **inst);
+ int64_t (*add)(void **inst, void *key, void *value);
+ int64_t (*search)(void **inst, void *key, void **value_out);
+ int64_t (*delete)(void **inst, void *key);
+ int64_t (*destroy)(void **inst);
+};
+
diff --git a/src/libsds/test/benchmark_par.c b/src/libsds/test/benchmark_par.c
new file mode 100644
index 0000000..8fa4ff7
--- /dev/null
+++ b/src/libsds/test/benchmark_par.c
@@ -0,0 +1,644 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+#include "benchmark_par.h"
+#include <plhash.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/* Wrappers */
+
+struct b_tree_cow_cb bptree_test = {
+ "sds b+tree without search csum", /* name */
+ NULL, /* inst */
+ bptree_init_wrapper, /* init */
+ generic_read_begin, /* read_begin */
+ generic_read_complete, /* read_complete */
+ generic_write_begin, /* write_begin */
+ generic_write_commit, /* write commit */
+ bptree_add_wrapper, /* add */
+ bptree_search_wrapper, /* search */
+ bptree_delete_wrapper, /* delete */
+ bptree_destroy_wrapper, /* destroy inst */
+};
+
+struct b_tree_cow_cb bptree_cow_test = {
+ "sds b+tree with copy on write", /* name */
+ NULL, /* inst */
+ bptree_cow_init_wrapper, /* init */
+ bptree_cow_read_begin, /* read_begin */
+ bptree_cow_read_complete, /* read_complete */
+ bptree_cow_write_begin, /* write_begin */
+ bptree_cow_write_commit, /* write commit */
+ bptree_cow_add_wrapper, /* add */
+ bptree_cow_search_wrapper, /* search */
+ bptree_cow_delete_wrapper, /* delete */
+ bptree_cow_destroy_wrapper, /* destroy inst */
+};
+
+struct b_tree_cow_cb hash_small_cb_test = {
+ "pl hashmap small",
+ NULL,
+ hash_small_init_wrapper,
+ generic_read_begin, /* read_begin */
+ generic_read_complete, /* read_complete */
+ generic_write_begin, /* write_begin */
+ generic_write_commit, /* write commit */
+ hash_add_wrapper,
+ hash_search_wrapper,
+ hash_delete_wrapper,
+ hash_destroy_wrapper,
+};
+
+struct b_tree_cow_cb hash_med_cb_test = {
+ "pl hashmap med",
+ NULL,
+ hash_med_init_wrapper,
+ generic_read_begin, /* read_begin */
+ generic_read_complete, /* read_complete */
+ generic_write_begin, /* write_begin */
+ generic_write_commit, /* write commit */
+ hash_add_wrapper,
+ hash_search_wrapper,
+ hash_delete_wrapper,
+ hash_destroy_wrapper,
+};
+
+struct b_tree_cow_cb hash_large_cb_test = {
+ "pl hashmap large",
+ NULL,
+ hash_large_init_wrapper,
+ generic_read_begin, /* read_begin */
+ generic_read_complete, /* read_complete */
+ generic_write_begin, /* write_begin */
+ generic_write_commit, /* write commit */
+ hash_add_wrapper,
+ hash_search_wrapper,
+ hash_delete_wrapper,
+ hash_destroy_wrapper,
+};
+
+/* The benchmarks */
+
+/* == Random and contended == */
+
+void
+batch_random(struct thread_info *info) {
+ // printf("tid %d\n", info->tid);
+
+ void *read_txn = NULL;
+ void *write_txn = NULL;
+
+ size_t cf = 0;
+ size_t step = 0;
+ size_t current_step = 0;
+ size_t max_factors = info->iter / 2048;
+ size_t baseid;
+ void *output;
+ /* Give ourselves a unique base id */
+ for (size_t i = 0; i < info->tid; i++) {
+ baseid += 50000;
+ }
+
+ /* Now start stepping, and randomly deleting */
+ for (step = 0; step < (info->iter * 50); step++) {
+ current_step = step + fill_pattern[step % 2048];
+ size_t mod = current_step % 10;
+ cf = step % max_factors;
+ void *target = (void *)(fill_pattern[step % 2048] + (2048 << cf) +
baseid);
+
+ if (mod >= 8) {
+ info->ds->write_begin(&(info->ds->inst), &write_txn);
+ info->ds->delete(&(info->ds->inst), write_txn, target);
+ if (info->write_delay != 0) {
+ usleep(info->write_delay);
+ }
+ info->ds->write_commit(&(info->ds->inst), write_txn);
+ } else if (mod >= 6) {
+ info->ds->write_begin(&(info->ds->inst), &write_txn);
+ info->ds->add(&(info->ds->inst), write_txn, target, NULL);
+ if (info->write_delay != 0) {
+ usleep(info->write_delay);
+ }
+ info->ds->write_commit(&(info->ds->inst), write_txn);
+ } else {
+ info->ds->read_begin(&(info->ds->inst), &read_txn);
+ info->ds->search(&(info->ds->inst), read_txn, target,
&output);
+ if (info->read_delay != 0) {
+ usleep(info->read_delay);
+ }
+ info->ds->read_complete(&(info->ds->inst), read_txn);
+ }
+
+ }
+
+ // printf("tid %d complete\n", info->tid);
+}
+
+void
+batch_search(struct thread_info *info) {
+ void *read_txn = NULL;
+ size_t cf = 0;
+ size_t step = 0;
+ void *output;
+
+ for (step = 0; step < (info->iter * 100); step++) {
+ info->ds->read_begin(&(info->ds->inst), &read_txn);
+ for (size_t j = 0; j < info->batchsize; j++) {
+ cf = (step * info->tid) % 2048;
+ void *target = (void *)(fill_pattern[cf] + j);
+
+ info->ds->search(&(info->ds->inst), read_txn, target,
&output);
+ }
+ if (info->read_delay != 0) {
+ usleep(info->read_delay);
+ }
+ info->ds->read_complete(&(info->ds->inst), read_txn);
+ }
+}
+
+
+void
+batch_insert(struct thread_info *info) {
+ void *write_txn = NULL;
+
+ size_t cf = 0;
+ size_t step = 0;
+ size_t max_factors = info->iter / 2048;
+ size_t baseid;
+ /* Give ourselves a unique base id */
+ for (size_t i = 0; i < info->tid; i++) {
+ baseid += 50000;
+ }
+ for (step = 0; step < (info->iter * 50); step++) {
+ info->ds->write_begin(&(info->ds->inst), &write_txn);
+ for (size_t j = 0; j < 10; j++) {
+ cf = step % max_factors;
+ void *target = (void *)(fill_pattern[step % 2048] + (2048 << cf) +
baseid);
+ info->ds->add(&(info->ds->inst), write_txn, target, NULL);
+
+ }
+ if (info->write_delay != 0) {
+ usleep(info->write_delay);
+ }
+ info->ds->write_commit(&(info->ds->inst), write_txn);
+ }
+}
+
+void
+batch_delete(struct thread_info *info) {
+ void *write_txn = NULL;
+
+ size_t cf = 0;
+ size_t step = 0;
+ size_t max_factors = info->iter / 2048;
+ size_t baseid;
+ /* Give ourselves a unique base id */
+ for (size_t i = 0; i < info->tid; i++) {
+ baseid += 50000;
+ }
+ /* Now start stepping, and randomly deleting */
+ for (step = 0; step < (info->iter * 50); step++) {
+ info->ds->write_begin(&(info->ds->inst), &write_txn);
+ for (size_t j = 0; j < 10; j++) {
+ cf = step % max_factors;
+ void *target = (void *)(fill_pattern[step % 2048] + (2048 << cf) +
baseid);
+ info->ds->delete(&(info->ds->inst), write_txn, target);
+
+ }
+ if (info->write_delay != 0) {
+ usleep(info->write_delay);
+ }
+ info->ds->write_commit(&(info->ds->inst), write_txn);
+ }
+}
+
+void
+bench_thread_batch(void *arg) {
+ struct thread_info *info = (struct thread_info *)arg;
+ // printf("tid %d\n", info->tid);
+ struct timespec start_time;
+ struct timespec finish_time;
+
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+ info->op(info);
+
+ // stop time
+ clock_gettime(CLOCK_MONOTONIC, &finish_time);
+ // printf("tid %d complete\n", info->tid);
+ info->sec = finish_time.tv_sec - start_time.tv_sec;
+ info->nsec = finish_time.tv_nsec - start_time.tv_nsec;
+
+ if (info->nsec < 0) {
+ // It's negative so take one second
+ info->sec -= 1;
+ // And set nsec to to a whole value
+ info->nsec = 1000000000 - info->nsec;
+ }
+}
+
+void
+batch_test_run(struct b_tree_cow_cb *ds, uint64_t iter, char *name, struct thread_info
*info, size_t info_count) {
+ PRThread *t[80] = {0};
+
+
+ printf("BENCH: Start ...\n");
+
+ // This launches a number of threads, and waits for them to join.
+ // Need some kind of thread struct to pass to it?
+
+ for (size_t i = 0; i < info_count; i++) {
+ t[i] = PR_CreateThread(PR_USER_THREAD, bench_thread_batch, (void
*)&(info[i]),
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
0);
+ assert_ptr_not_equal(t[i], NULL);
+ }
+
+ for (size_t i = 0; i < info_count; i++) {
+ assert_int_equal(PR_JoinThread(t[i]), PR_SUCCESS);
+ }
+
+ printf("BENCH: Complete ... \n");
+
+ printf("BENCH: %s %s %"PRIu64" times\n", ds->name, name,
iter);
+ for (size_t i = 0; i < info_count; i++) {
+ printf(" tid %"PRIu64": %" PRId64 ".%010"
PRId64 "\n", info[i].tid, info[i].sec, info[i].nsec);
+ }
+}
+
+/* Batch running */
+
+static void
+populate_ds(struct b_tree_cow_cb *ds, uint64_t iter) {
+ void *write_txn;
+
+ size_t max_factors = iter / 2048;
+
+ size_t count = 0;
+
+ // Inject a number of elements first, just so that search and delete have
+ // something to work with. we won't bench this.
+ ds->write_begin(&(ds->inst), &write_txn);
+ for (size_t j = 0; j < max_factors; j++) {
+ for (size_t i = 0; i < 2048 ; i++) {
+ ds->add(&(ds->inst), write_txn, (void *)(fill_pattern[i] + (2048 *
j)), NULL);
+ count++;
+ }
+ }
+ printf("Added %"PRIu64"\n", count);
+ ds->write_commit(&(ds->inst), write_txn);
+}
+
+/* Now we can just construct some generic tests. */
+
+void
+bench_insert_search_delete_batch(struct b_tree_cow_cb *ds, uint64_t iter) {
+ struct thread_info info[10] = {{0}};
+
+ ds->init(&(ds->inst));
+
+ populate_ds(ds, iter);
+
+ size_t i = 0;
+ for (; i < 3; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 10;
+ }
+ for (; i < 2; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 10;
+ }
+ for (; i < 10; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 10;
+ }
+
+ batch_test_run(ds, iter, "bench_insert_search_delete_batch", info, 10);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_insert_search_delete_random(struct b_tree_cow_cb *ds, uint64_t iter) {
+ struct thread_info info[10] = {{0}};
+ ds->init(&(ds->inst));
+
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 10; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_random;
+ info[i].batchsize = 10;
+ }
+ batch_test_run(ds, iter, "bench_insert_search_delete_random", info, 10);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_isd_write_delay(struct b_tree_cow_cb *ds, uint64_t iter) {
+ struct thread_info info[10] = {{0}};
+ ds->init(&(ds->inst));
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 2; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 10;
+ info[i].write_delay = 1;
+ }
+ for (; i < 3; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 10;
+ info[i].write_delay = 1;
+ }
+ for (; i < 10; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 10;
+ info[i].write_delay = 1;
+ }
+
+ batch_test_run(ds, iter, "bench_isd_write_delay", info, 10);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_isd_read_delay(struct b_tree_cow_cb *ds, uint64_t iter) {
+ struct thread_info info[10] = {{0}};
+ ds->init(&(ds->inst));
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 2; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 10;
+ info[i].read_delay = 1;
+ }
+ for (; i < 3; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 10;
+ info[i].read_delay = 1;
+ }
+ for (; i < 10; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 10;
+ info[i].read_delay = 1;
+ }
+
+ batch_test_run(ds, iter, "bench_isd_read_delay", info, 10);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_high_thread_small_batch_read(struct b_tree_cow_cb *ds, uint64_t iter) {
+ /* DS can scale to many threads, so simulate high contention. */
+ struct thread_info info[80] = {{0}};
+ ds->init(&(ds->inst));
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 10; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 5;
+ }
+ for (; i < 20; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 5;
+ }
+ for (; i < 80; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 5;
+ }
+
+ batch_test_run(ds, iter, "bench_high_thread_small_batch_read", info, 80);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_high_thread_large_batch_read(struct b_tree_cow_cb *ds, uint64_t iter) {
+ /* DS can scale to many threads, so simulate high contention. */
+ struct thread_info info[80] = {{0}};
+ ds->init(&(ds->inst));
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 10; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 64;
+ }
+ for (; i < 20; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 64;
+ }
+ for (; i < 80; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 64;
+ }
+
+ batch_test_run(ds, iter, "bench_high_thread_large_batch_read", info, 80);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_high_thread_small_batch_write(struct b_tree_cow_cb *ds, uint64_t iter) {
+ /* DS can scale to many threads, so simulate high contention. */
+ struct thread_info info[80] = {{0}};
+ ds->init(&(ds->inst));
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 30; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 5;
+ }
+ for (; i < 50; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 5;
+ }
+ for (; i < 80; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 5;
+ }
+
+ batch_test_run(ds, iter, "bench_high_thread_small_batch_write", info, 80);
+
+ ds->destroy(&(ds->inst));
+}
+
+void
+bench_high_thread_large_batch_write(struct b_tree_cow_cb *ds, uint64_t iter) {
+ /* DS can scale to many threads, so simulate high contention. */
+ struct thread_info info[80] = {{0}};
+ ds->init(&(ds->inst));
+ populate_ds(ds, iter);
+ size_t i = 0;
+ for (; i < 30; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_insert;
+ info[i].batchsize = 64;
+ }
+ for (; i < 50; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_delete;
+ info[i].batchsize = 64;
+ }
+ for (; i < 80; i++) {
+ info[i].iter = iter;
+ info[i].tid = i;
+ info[i].ds = ds;
+ info[i].op = batch_search;
+ info[i].batchsize = 64;
+ }
+
+ batch_test_run(ds, iter, "bench_high_thread_large_batch_write", info, 80);
+
+ ds->destroy(&(ds->inst));
+}
+
+/* End tests */
+
+int
+main (int argc __attribute__((unused)), char **argv __attribute__((unused))) {
+
+ uint64_t test_arrays[] = {5000, 10000, 100000, 500000, 1000000, 2500000, 5000000,
10000000};
+
+ for (size_t i = 0; i < 3; i++) {
+ /*
+ bench_insert_search_delete_batch(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_insert_search_delete_batch(&hash_med_cb_test, test_arrays[i]);
+ bench_insert_search_delete_batch(&hash_large_cb_test, test_arrays[i]);
+ bench_insert_search_delete_batch(&bptree_test, test_arrays[i]);
+ bench_insert_search_delete_batch(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_isd_write_delay(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_isd_write_delay(&hash_med_cb_test, test_arrays[i]);
+ bench_isd_write_delay(&hash_large_cb_test, test_arrays[i]);
+ bench_isd_write_delay(&bptree_test, test_arrays[i]);
+ bench_isd_write_delay(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_isd_read_delay(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_isd_read_delay(&hash_med_cb_test, test_arrays[i]);
+ bench_isd_read_delay(&hash_large_cb_test, test_arrays[i]);
+ bench_isd_read_delay(&bptree_test, test_arrays[i]);
+ bench_isd_read_delay(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_high_thread_small_batch_read(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_high_thread_small_batch_read(&hash_med_cb_test, test_arrays[i]);
+ bench_high_thread_small_batch_read(&hash_large_cb_test, test_arrays[i]);
+ bench_high_thread_small_batch_read(&bptree_test, test_arrays[i]);
+ bench_high_thread_small_batch_read(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_high_thread_large_batch_read(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_high_thread_large_batch_read(&hash_med_cb_test, test_arrays[i]);
+ bench_high_thread_large_batch_read(&hash_large_cb_test, test_arrays[i]);
+ bench_high_thread_large_batch_read(&bptree_test, test_arrays[i]);
+ bench_high_thread_large_batch_read(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_high_thread_small_batch_write(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_high_thread_small_batch_write(&hash_med_cb_test, test_arrays[i]);
+ bench_high_thread_small_batch_write(&hash_large_cb_test, test_arrays[i]);
+ bench_high_thread_small_batch_write(&bptree_test, test_arrays[i]);
+ bench_high_thread_small_batch_write(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_high_thread_large_batch_write(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_high_thread_large_batch_write(&hash_med_cb_test, test_arrays[i]);
+ bench_high_thread_large_batch_write(&hash_large_cb_test, test_arrays[i]);
+ bench_high_thread_large_batch_write(&bptree_test, test_arrays[i]);
+ bench_high_thread_large_batch_write(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ /*
+ bench_insert_search_delete_random(&hash_small_cb_test, test_arrays[i]);
+ */
+ bench_insert_search_delete_random(&hash_med_cb_test, test_arrays[i]);
+ bench_insert_search_delete_random(&hash_large_cb_test, test_arrays[i]);
+ bench_insert_search_delete_random(&bptree_test, test_arrays[i]);
+ bench_insert_search_delete_random(&bptree_cow_test, test_arrays[i]);
+ printf("---\n");
+
+ }
+}
diff --git a/src/libsds/test/benchmark_par.h b/src/libsds/test/benchmark_par.h
new file mode 100644
index 0000000..418cfb3
--- /dev/null
+++ b/src/libsds/test/benchmark_par.h
@@ -0,0 +1,73 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include <time.h>
+#include <pthread.h>
+
+struct b_tree_cow_cb {
+ char *name;
+ void *inst;
+ int64_t (*init)(void **inst);
+ int64_t (*read_begin)(void **inst, void **read_txn);
+ int64_t (*read_complete)(void **inst, void *read_txn);
+ int64_t (*write_begin)(void **inst, void **write_txn);
+ int64_t (*write_commit)(void **inst, void *write_txn);
+ int64_t (*add)(void **inst, void *write_txn, void *key, void *value);
+ int64_t (*search)(void **inst, void *read_txn, void *key, void **value_out);
+ int64_t (*delete)(void **inst, void *write_txn, void *key);
+ int64_t (*destroy)(void **inst);
+};
+
+struct thread_info {
+ uint64_t iter;
+ size_t tid;
+ struct b_tree_cow_cb *ds;
+ size_t batchsize;
+ void (*op)(struct thread_info *info);
+ uint32_t write_delay;
+ uint32_t read_delay;
+ time_t sec;
+ long nsec;
+};
+
+/* Wrappers and definitions */
+
+
+int64_t generic_read_begin(void **inst, void **txn);
+int64_t generic_read_complete(void **inst, void *txn);
+int64_t generic_write_begin(void **inst, void **txn);
+int64_t generic_write_commit(void **inst, void *txn);
+
+int64_t bptree_init_wrapper(void **inst);
+int64_t bptree_read_begin(void **inst, void **txn);
+int64_t bptree_read_complete(void **inst, void *txn);
+int64_t bptree_write_begin(void **inst, void **txn);
+int64_t bptree_write_commit(void **inst, void *txn);
+int64_t bptree_add_wrapper(void **inst, void *txn, void *key, void *value);
+int64_t bptree_search_wrapper(void **inst, void *txn, void *key, void **value_out);
+int64_t bptree_delete_wrapper(void **inst, void *txn, void *key);
+int64_t bptree_destroy_wrapper(void **inst);
+
+int64_t bptree_cow_init_wrapper(void **inst);
+int64_t bptree_cow_read_begin(void **inst, void **read_txn);
+int64_t bptree_cow_read_complete(void **inst, void *read_txn);
+int64_t bptree_cow_write_begin(void **inst, void **write_txn);
+int64_t bptree_cow_write_commit(void **inst, void *write_txn);
+int64_t bptree_cow_add_wrapper(void **inst, void *write_txn, void *key, void *value);
+int64_t bptree_cow_search_wrapper(void **inst, void *read_txn, void *key, void
**value_out);
+int64_t bptree_cow_delete_wrapper(void **inst, void *write_txn, void *key);
+int64_t bptree_cow_destroy_wrapper(void **inst);
+
+/* Hash map structures */
+int64_t hash_small_init_wrapper(void **inst);
+int64_t hash_med_init_wrapper(void **inst);
+int64_t hash_large_init_wrapper(void **inst);
+int64_t hash_add_wrapper(void **inst, void *write_txn, void *key, void *value);
+int64_t hash_search_wrapper(void **inst, void *read_txn, void *key, void **value_out);
+int64_t hash_delete_wrapper(void **inst, void *write_txn, void *key);
+int64_t hash_destroy_wrapper(void **inst);
diff --git a/src/libsds/test/benchmark_parwrap.c b/src/libsds/test/benchmark_parwrap.c
new file mode 100644
index 0000000..36fe9db
--- /dev/null
+++ b/src/libsds/test/benchmark_parwrap.c
@@ -0,0 +1,311 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+#include "benchmark_par.h"
+#include <plhash.h>
+#include <stdio.h>
+#include <unistd.h>
+
+// #define WITH_RWLOCK 1
+
+#ifdef WITH_RWLOCK
+static pthread_rwlock_t the_lock;
+#else
+static pthread_mutex_t the_lock;
+#endif
+
+int64_t generic_read_begin(void **inst __attribute__((unused)), void **txn
__attribute__((unused))) {
+#ifdef WITH_RWLOCK
+ pthread_rwlock_rdlock(&the_lock);
+#else
+ pthread_mutex_lock(&the_lock);
+#endif
+ return 0;
+}
+
+int64_t generic_read_complete(void **inst __attribute__((unused)), void *txn
__attribute__((unused))) {
+#ifdef WITH_RWLOCK
+ pthread_rwlock_unlock(&the_lock);
+#else
+ pthread_mutex_unlock(&the_lock);
+#endif
+ return 0;
+}
+
+int64_t generic_write_begin(void **inst __attribute__((unused)), void **txn
__attribute__((unused))) {
+#ifdef WITH_RWLOCK
+ pthread_rwlock_wrlock(&the_lock);
+#else
+ pthread_mutex_lock(&the_lock);
+#endif
+ return 0;
+}
+
+int64_t generic_write_commit(void **inst __attribute__((unused)), void *txn
__attribute__((unused))) {
+#ifdef WITH_RWLOCK
+ pthread_rwlock_unlock(&the_lock);
+#else
+ pthread_mutex_unlock(&the_lock);
+#endif
+ return 0;
+}
+
+/* The sds b+ tree wrappers */
+
+int64_t bptree_init_wrapper(void **inst) {
+#ifdef WITH_RWLOCK
+ pthread_rwlock_init(&the_lock, NULL);
+#else
+ pthread_mutex_init(&the_lock, NULL);
+#endif
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_bptree_init(binst, 0, sds_uint64_t_compare, sds_free, sds_uint64_t_free,
sds_uint64_t_dup);
+ return 0;
+}
+
+int64_t bptree_add_wrapper(void **inst, void *txn __attribute__((unused)), void *key,
void *value) {
+ // THIS WILL BREAK SOMETIME!
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_bptree_insert(*binst, key, value);
+ return 0;
+}
+
+int64_t bptree_search_wrapper(void **inst, void *txn __attribute__((unused)), void *key,
void **value_out __attribute__((unused))) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_result result = sds_bptree_search(*binst, key);
+ if (result != SDS_KEY_PRESENT) {
+ // printf("search result is %d\n", result);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_delete_wrapper(void **inst, void *txn __attribute__((unused)), void *key)
{
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ sds_result result = sds_bptree_delete(*binst, key);
+
+ if (result != SDS_KEY_PRESENT) {
+ // printf("delete result is %d\n", result);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_destroy_wrapper(void **inst) {
+ sds_bptree_instance **binst = (sds_bptree_instance **)inst;
+ // sds_bptree_display(*binst);
+ sds_bptree_destroy(*binst);
+#ifdef WITH_RWLOCK
+ pthread_rwlock_destroy(&the_lock);
+#else
+ pthread_mutex_destroy(&the_lock);
+#endif
+ return 0;
+}
+
+/* sds bptree cow wrapper */
+
+int64_t bptree_cow_init_wrapper(void **inst) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_cow_init(binst, 0, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_dup, sds_uint64_t_free, sds_uint64_t_dup);
+ return 0;
+}
+
+int64_t bptree_cow_read_begin(void **inst, void **read_txn) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_transaction **txn = (sds_bptree_transaction **)read_txn;
+ sds_bptree_cow_rotxn_begin(*binst, txn);
+ return 0;
+}
+
+int64_t bptree_cow_read_complete(void **inst __attribute__((unused)), void *read_txn) {
+ // sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_transaction *txn = (sds_bptree_transaction *)read_txn;
+ sds_bptree_cow_rotxn_close(&txn);
+ return 0;
+}
+
+int64_t bptree_cow_write_begin(void **inst, void **write_txn) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_transaction **txn = (sds_bptree_transaction **)write_txn;
+#ifdef DEBUG
+ assert_int_equal(sds_bptree_cow_verify(*binst), SDS_SUCCESS);
+#endif
+ sds_bptree_cow_wrtxn_begin(*binst, txn);
+ return 0;
+}
+
+int64_t bptree_cow_write_commit(void **inst __attribute__((unused)), void *write_txn) {
+ // sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_transaction *txn = (sds_bptree_transaction *)write_txn;
+ sds_bptree_cow_wrtxn_commit(&txn);
+ return 0;
+}
+
+int64_t bptree_cow_add_wrapper(void **inst __attribute__((unused)), void *write_txn, void
*key, void *value) {
+ // THIS WILL BREAK SOMETIME!
+ sds_bptree_transaction *txn = (sds_bptree_transaction *)write_txn;
+ sds_bptree_cow_insert(txn, key, value);
+ return 0;
+}
+
+int64_t bptree_cow_search_wrapper(void **inst __attribute__((unused)), void *read_txn,
void *key, void **value_out __attribute__((unused))) {
+ sds_bptree_transaction *txn = (sds_bptree_transaction *)read_txn;
+ sds_result result = sds_bptree_cow_search(txn, key);
+ if (result != SDS_KEY_PRESENT) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_cow_delete_wrapper(void **inst __attribute__((unused)), void *write_txn,
void *key) {
+ // sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ sds_bptree_transaction *txn = (sds_bptree_transaction *)write_txn;
+ sds_result result = sds_bptree_cow_delete(txn, key);
+ if (result != SDS_KEY_PRESENT) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t bptree_cow_destroy_wrapper(void **inst) {
+ sds_bptree_cow_instance **binst = (sds_bptree_cow_instance **)inst;
+ // sds_bptree_display(*binst);
+ sds_bptree_cow_destroy(*binst);
+ return 0;
+}
+
+/* Hashmap structures */
+
+/* NSPR PLHash wrappers */
+
+/*
+ * This number is chosen because it's the amount used by the NDN cache in
+ * Directory Server. Most of the other caches are 2047 or lower. The exception
+ * is the entry cache, which is often upwards of 20483 buckets. Even with
+ * 20483 buckets, at 1,000,000, we still with perfect hashing devolve to
+ * linked lists of length 48.
+ * If you increase the DS cache to 120MB, you get 245761 slots, which is only
+ * 4 long.
+ * There is a quirk! DS cache only allows 1 item, so the moment the cache
+ * hash matches it's overriden! Uh oh!
+ * With this value of 2053, we are looking at lists of 487 at a maximum!
+ */
+
+#define HASH_BUCKETS_SMALL 2053
+#define HASH_BUCKETS_MED 20483
+#define HASH_BUCKETS_LARGE 245761
+
+PLHashNumber
+hash_func_large(const void *key) {
+ uint64_t ik = (uint64_t)key;
+ return ik % HASH_BUCKETS_LARGE;
+}
+
+PLHashNumber
+hash_func_med(const void *key) {
+ uint64_t ik = (uint64_t)key;
+ return ik % HASH_BUCKETS_MED;
+}
+
+PLHashNumber
+hash_func_small(const void *key) {
+ uint64_t ik = (uint64_t)key;
+ return ik % HASH_BUCKETS_SMALL;
+}
+
+PRIntn
+hash_key_compare (const void *a, const void *b) {
+ uint64_t ia = (uint64_t)a;
+ uint64_t ib = (uint64_t)b;
+ return ia == ib;
+}
+
+PRIntn
+hash_value_compare(const void *a __attribute__((unused)), const void *b
__attribute__((unused))) {
+ // This cheats and says they are always differnt, but I don't think I use this
....
+ return 1;
+}
+
+int64_t
+hash_small_init_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+#ifdef WITH_RWLOCK
+ pthread_rwlock_init(&the_lock, NULL);
+#else
+ pthread_mutex_init(&the_lock, NULL);
+#endif
+
+ *table = PL_NewHashTable(HASH_BUCKETS_SMALL, hash_func_small, hash_key_compare,
hash_value_compare, NULL, NULL);
+ return 0;
+}
+
+int64_t
+hash_med_init_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+#ifdef WITH_RWLOCK
+ pthread_rwlock_init(&the_lock, NULL);
+#else
+ pthread_mutex_init(&the_lock, NULL);
+#endif
+
+ *table = PL_NewHashTable(HASH_BUCKETS_MED, hash_func_med, hash_key_compare,
hash_value_compare, NULL, NULL);
+ return 0;
+}
+
+int64_t
+hash_large_init_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+#ifdef WITH_RWLOCK
+ pthread_rwlock_init(&the_lock, NULL);
+#else
+ pthread_mutex_init(&the_lock, NULL);
+#endif
+
+ *table = PL_NewHashTable(HASH_BUCKETS_LARGE, hash_func_large, hash_key_compare,
hash_value_compare, NULL, NULL);
+ return 0;
+}
+
+int64_t
+hash_add_wrapper(void **inst, void *write_txn __attribute__((unused)), void *key, void
*value __attribute__((unused))) {
+ PLHashTable **table = (PLHashTable **)inst;
+ // WARNING: We have to add key as value too else hashmap won't add it!!!
+ PL_HashTableAdd(*table, key, key);
+ return 0;
+}
+
+int64_t
+hash_search_wrapper(void **inst, void *read_txn __attribute__((unused)), void *key, void
**value_out) {
+ PLHashTable **table = (PLHashTable **)inst;
+ *value_out = PL_HashTableLookup(*table, key);
+ if (*value_out == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t
+hash_delete_wrapper(void **inst, void *write_txn __attribute__((unused)), void *key) {
+ PLHashTable **table = (PLHashTable **)inst;
+ PL_HashTableRemove(*table, key);
+ return 0;
+}
+
+int64_t
+hash_destroy_wrapper(void **inst) {
+ PLHashTable **table = (PLHashTable **)inst;
+#ifdef WITH_RWLOCK
+ pthread_rwlock_destroy(&the_lock);
+#else
+ pthread_mutex_destroy(&the_lock);
+#endif
+ PL_HashTableDestroy(*table);
+ return 0;
+}
+
diff --git a/src/libsds/test/test_fixtures.c b/src/libsds/test/test_fixtures.c
new file mode 100644
index 0000000..a530c02
--- /dev/null
+++ b/src/libsds/test/test_fixtures.c
@@ -0,0 +1,201 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+
+int
+bptree_test_setup(void **state)
+{
+ // Create a new tree, and then destroy it.
+ sds_bptree_instance *binst = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(&binst, 1, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_free, sds_uint64_t_dup);
+
+ assert_int_equal(result, SDS_SUCCESS);
+
+ *state = binst;
+
+ return 0;
+}
+
+int
+bptree_str_test_setup(void **state)
+{
+ // Create a new tree, and then destroy it.
+ sds_bptree_instance *binst = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(&binst, 1, sds_uint64_t_compare, sds_free,
sds_uint64_t_free, sds_uint64_t_dup);
+
+ assert_int_equal(result, SDS_SUCCESS);
+
+ *state = binst;
+
+ return 0;
+}
+
+int
+bptree_test_teardown(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_verify(binst);
+
+#ifdef DEBUG
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification failed
%d binst", result);
+ }
+#endif
+
+ assert_int_equal(result, SDS_SUCCESS);
+
+
+ result = sds_bptree_destroy(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ return 0;
+}
+
+int bptree_test_set_setup(void **state) {
+ sds_bptree_instance *binst_a = NULL;
+ sds_bptree_instance *binst_b = NULL;
+ sds_bptree_instance *binst_out = NULL;
+ void **binst = sds_malloc(sizeof(void *) * 3);
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(&binst_a, 1, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_free, sds_uint64_t_dup);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_init(&binst_b, 1, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_free, sds_uint64_t_dup);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ binst[0] = (void *)binst_a;
+ binst[1] = (void *)binst_b;
+ binst[2] = (void *)binst_out;
+
+ *state = binst;
+
+ return 0;
+
+}
+
+int bptree_test_set_teardown(void **state) {
+ void **binst = *state;
+ sds_bptree_instance *binst_a = (sds_bptree_instance *) binst[0];
+ sds_bptree_instance *binst_b = (sds_bptree_instance *) binst[1];
+ sds_bptree_instance *binst_out = (sds_bptree_instance *) binst[2];
+
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_destroy(binst_a);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_destroy(binst_b);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_destroy(binst_out);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ sds_free(binst);
+
+ return 0;
+}
+
+int
+bptree_test_cow_setup(void **state)
+{
+ // Create a new tree, and then destroy it.
+ sds_bptree_cow_instance *binst = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_cow_init(&binst, 1, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_dup, sds_uint64_t_free, sds_uint64_t_dup);
+
+ assert_int_equal(result, SDS_SUCCESS);
+
+ *state = binst;
+
+ return 0;
+}
+
+int
+bptree_test_cow_teardown(void **state)
+{
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_cow_verify(binst);
+
+#ifdef DEBUG
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree COW verification
failed %d binst", result);
+ }
+#endif
+
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_cow_destroy(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ return 0;
+}
+
+int
+queue_test_setup(void **state) {
+ sds_queue *q = NULL;
+ sds_result result = SDS_SUCCESS;
+ result = sds_queue_init(&q, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ *state = q;
+ return 0;
+}
+
+int
+queue_test_teardown(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ result = sds_queue_destroy(q);
+ assert_int_equal(result, SDS_SUCCESS);
+ return 0;
+}
+
+int
+tqueue_test_setup(void **state) {
+ sds_tqueue *q = NULL;
+ sds_result result = SDS_SUCCESS;
+ result = sds_tqueue_init(&q, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ *state = q;
+ return 0;
+}
+
+int
+tqueue_test_teardown(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ result = sds_tqueue_destroy(q);
+ assert_int_equal(result, SDS_SUCCESS);
+ return 0;
+}
+
+int
+lqueue_test_setup(void **state) {
+ sds_lqueue *q = NULL;
+ sds_result result = SDS_SUCCESS;
+ result = sds_lqueue_init(&q, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ *state = q;
+ return 0;
+}
+
+int
+lqueue_test_teardown(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ result = sds_lqueue_destroy(q);
+ assert_int_equal(result, SDS_SUCCESS);
+ return 0;
+}
diff --git a/src/libsds/test/test_sds.c b/src/libsds/test/test_sds.c
new file mode 100644
index 0000000..276d3d5
--- /dev/null
+++ b/src/libsds/test/test_sds.c
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+
+int
+main ( int argc __attribute__((unused)), char **argv __attribute__((unused))) {
+ int result = 0;
+ result += run_bpt_tests();
+ result += run_set_tests();
+ result += run_cow_tests();
+ result += run_queue_tests();
+ result += run_tqueue_tests();
+ result += run_lqueue_tests();
+ return result;
+}
+
diff --git a/src/libsds/test/test_sds.h b/src/libsds/test/test_sds.h
new file mode 100644
index 0000000..994611a
--- /dev/null
+++ b/src/libsds/test/test_sds.h
@@ -0,0 +1,262 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#pragma once
+
+#include <config.h>
+
+/* As this is a test, we can access internal functions */
+#include "../sds/sds_internal.h"
+/* sds pulls in NSPR threads. */
+#include <sds.h>
+
+/* For cmocka */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+static const uint64_t fill_pattern[] = {
+ 228, 733, 609, 1378, 557, 199, 1336, 1923, 1813, 746, 52,
+ 1627, 586, 2048, 1749, 965, 625, 725, 1798, 393, 1551,
+ 212, 976, 1410, 1386, 491, 98, 1338, 1807, 967, 1984,
+ 1930, 604, 1873, 22, 1454, 90, 892, 455, 1389, 1727,
+ 33, 149, 937, 1236, 1080, 1883, 1291, 59, 1025, 1275,
+ 1495, 2027, 920, 1459, 537, 1768, 15, 1478, 352, 1834,
+ 415, 1784, 1036, 1272, 650, 642, 1366, 363, 1147, 813,
+ 1299, 1753, 1550, 784, 999, 1695, 46, 244, 1067, 2021,
+ 167, 949, 2039, 957, 1820, 1778, 648, 756, 242, 1644,
+ 256, 1351, 1027, 1899, 27, 408, 1402, 1189, 504, 2036,
+ 539, 800, 1713, 1659, 227, 342, 264, 1411, 689, 721,
+ 1171, 587, 1658, 524, 1207, 480, 250, 40, 1430, 1728,
+ 824, 887, 467, 731, 309, 344, 380, 1723, 80, 517,
+ 1408, 993, 81, 203, 1060, 1563, 618, 29, 1651, 1413,
+ 669, 1019, 340, 371, 1840, 1214, 23, 1481, 434, 1170,
+ 1349, 939, 2007, 894, 1155, 737, 1610, 160, 381, 1141,
+ 720, 1120, 1989, 1064, 1119, 569, 711, 1245, 961, 173,
+ 329, 575, 406, 312, 461, 483, 1731, 132, 61, 131,
+ 179, 940, 749, 1348, 1472, 1305, 521, 900, 1023, 64,
+ 253, 25, 245, 134, 912, 1122, 1540, 2, 841, 876,
+ 1079, 602, 728, 570, 213, 450, 508, 1864, 295, 1669,
+ 968, 1851, 845, 424, 1316, 759, 1814, 1487, 1881, 1645,
+ 1029, 278, 335, 1174, 207, 493, 787, 1920, 34, 1740,
+ 496, 1670, 1002, 632, 185, 916, 563, 1854, 2000, 1446,
+ 68, 812, 275, 1641, 2046, 477, 1846, 910, 703, 782,
+ 159, 502, 91, 1453, 1146, 655, 2020, 1936, 272, 1957,
+ 1145, 44, 1637, 1463, 536, 1912, 1694, 909, 966, 722,
+ 718, 1300, 511, 1745, 444, 1942, 796, 786, 1357, 1867,
+ 10, 1896, 931, 922, 633, 290, 1123, 1907, 1847, 427,
+ 1092, 1737, 305, 530, 260, 1314, 1802, 1556, 30, 992,
+ 1032, 631, 304, 154, 1368, 1973, 1842, 284, 1783, 8,
+ 825, 1096, 811, 1250, 901, 3, 1566, 779, 1751, 468,
+ 77, 667, 2014, 1871, 1022, 666, 761, 1771, 1330, 783,
+ 608, 1581, 899, 1279, 127, 798, 872, 1473, 616, 1634,
+ 1138, 1307, 259, 1904, 686, 691, 1894, 1206, 1738, 141,
+ 190, 930, 1360, 762, 1801, 1557, 334, 1108, 1661, 988,
+ 274, 1457, 1815, 1782, 556, 488, 1827, 1282, 1543, 1926,
+ 479, 1996, 54, 736, 208, 573, 1082, 1417, 1441, 1034,
+ 1167, 528, 1701, 1442, 600, 515, 1301, 1986, 1010, 1296,
+ 1828, 647, 1427, 1475, 1278, 1447, 1256, 2031, 147, 2004,
+ 1042, 1900, 804, 869, 1676, 417, 1180, 551, 463, 1704,
+ 1139, 1028, 1335, 1571, 1260, 1735, 280, 247, 1437, 1104,
+ 808, 588, 1165, 1517, 140, 1483, 1588, 217, 365, 287,
+ 1614, 929, 485, 165, 2015, 1761, 1906, 323, 898, 1401,
+ 744, 170, 1602, 1126, 979, 447, 617, 1003, 1561, 1154,
+ 820, 959, 1486, 109, 1725, 238, 357, 534, 1748, 326,
+ 896, 391, 1529, 1844, 1511, 1241, 1083, 1097, 702, 341,
+ 246, 1237, 1322, 1574, 1075, 1849, 889, 236, 1233, 1056,
+ 1204, 1806, 1202, 821, 1488, 1549, 378, 85, 1916, 1391,
+ 1963, 232, 1302, 644, 270, 148, 1346, 809, 1952, 801,
+ 1375, 603, 704, 392, 1730, 1334, 1266, 1880, 1223, 1193,
+ 13, 390, 1555, 478, 1177, 1332, 1796, 578, 1211, 71,
+ 111, 1600, 1931, 535, 1705, 1319, 161, 300, 394, 1823,
+ 982, 369, 1825, 1527, 1608, 571, 765, 310, 1777, 1858,
+ 1054, 2042, 197, 429, 1303, 301, 1393, 302, 994, 1743,
+ 1325, 1121, 1091, 1979, 1344, 73, 271, 420, 874, 1045,
+ 220, 51, 695, 269, 303, 748, 1927, 60, 1809, 316,
+ 1166, 1255, 1978, 268, 281, 1598, 676, 581, 443, 932,
+ 1756, 481, 826, 1940, 810, 1093, 706, 1653, 1431, 1682,
+ 938, 1812, 1741, 1595, 672, 550, 2008, 86, 286, 1294,
+ 1132, 2032, 997, 291, 198, 1271, 31, 193, 645, 1364,
+ 1535, 2043, 265, 990, 1498, 971, 1714, 12, 599, 1901,
+ 115, 375, 1425, 509, 1337, 318, 1893, 120, 1020, 423,
+ 919, 414, 950, 687, 531, 331, 1863, 1175, 646, 584,
+ 1966, 1955, 1070, 56, 168, 1585, 1734, 4, 317, 1151,
+ 172, 1674, 1520, 1818, 441, 1173, 1994, 1345, 221, 1482,
+ 547, 1125, 911, 1217, 856, 405, 261, 693, 1762, 996,
+ 1788, 1573, 1012, 1760, 738, 2030, 1496, 713, 107, 730,
+ 482, 2037, 297, 1981, 991, 776, 320, 324, 1681, 129,
+ 745, 1639, 636, 473, 1073, 903, 1014, 1958, 285, 1219,
+ 2019, 816, 158, 254, 1186, 1850, 249, 873, 723, 498,
+ 205, 1168, 1909, 2041, 1076, 1631, 946, 337, 1052, 546,
+ 955, 1911, 1274, 1515, 14, 163, 214, 572, 1747, 1522,
+ 258, 1934, 1974, 1688, 610, 146, 960, 1763, 1578, 562,
+ 1239, 1772, 1668, 418, 958, 1800, 1293, 138, 918, 65,
+ 592, 799, 567, 189, 327, 519, 888, 1633, 640, 865,
+ 492, 1292, 1397, 1306, 308, 162, 364, 1359, 1856, 1356,
+ 1594, 1793, 2040, 935, 1228, 1387, 554, 266, 1804, 1616,
+ 1604, 1882, 1666, 123, 1304, 682, 332, 1650, 630, 1757,
+ 186, 235, 1130, 755, 1980, 945, 74, 1510, 928, 1703,
+ 1606, 1680, 815, 624, 1059, 1774, 741, 72, 1265, 1350,
+ 360, 1371, 2012, 897, 438, 696, 215, 514, 1833, 1379,
+ 2035, 986, 456, 607, 1114, 840, 428, 1891, 560, 1131,
+ 1548, 1824, 1617, 501, 917, 1532, 906, 516, 1908, 1309,
+ 1838, 1505, 1438, 777, 1494, 382, 1892, 495, 1328, 1176,
+ 1895, 83, 585, 1528, 1231, 211, 1465, 1181, 1716, 1164,
+ 942, 895, 276, 1044, 1497, 1832, 401, 1226, 1506, 1967,
+ 506, 1063, 1158, 1200, 1358, 1944, 359, 1609, 397, 1201,
+ 1455, 407, 35, 1816, 757, 987, 1765, 319, 2009, 716,
+ 1374, 1440, 1311, 781, 1865, 753, 1196, 606, 933, 176,
+ 1501, 591, 1577, 1085, 1331, 980, 1640, 353, 1137, 1112,
+ 683, 101, 837, 178, 1057, 223, 41, 1711, 827, 1143,
+ 70, 1157, 1046, 17, 828, 50, 1699, 1767, 1537, 1766,
+ 859, 500, 187, 886, 844, 1719, 760, 1461, 1503, 1603,
+ 1988, 1959, 377, 1519, 1822, 834, 1229, 1776, 383, 838,
+ 2024, 622, 767, 1995, 1932, 660, 1103, 1182, 37, 24,
+ 2017, 1596, 1960, 409, 734, 1968, 82, 273, 63, 2010,
+ 294, 677, 1474, 1471, 263, 2038, 878, 1534, 806, 1915,
+ 1235, 538, 105, 1615, 1087, 1150, 1491, 315, 1586, 936,
+ 1283, 1857, 1590, 1785, 1848, 1227, 764, 1780, 1448, 1853,
+ 1597, 210, 32, 333, 1937, 1726, 425, 1710, 321, 439,
+ 1775, 589, 962, 177, 754, 457, 1671, 1791, 802, 975,
+ 1184, 1140, 1524, 842, 251, 1246, 830, 2044, 543, 541,
+ 21, 11, 156, 778, 1855, 67, 373, 1149, 1953, 2016,
+ 1006, 1038, 1078, 151, 772, 1589, 78, 1352, 1933, 1289,
+ 143, 1429, 1746, 121, 128, 823, 1285, 1982, 1127, 116,
+ 1514, 1403, 981, 1116, 1493, 867, 346, 657, 513, 857,
+ 794, 1221, 153, 1706, 870, 1476, 671, 1192, 1764, 18,
+ 311, 413, 793, 386, 1339, 1913, 1872, 367, 113, 1545,
+ 1071, 137, 1015, 583, 117, 1972, 7, 1910, 1340, 1,
+ 440, 714, 1592, 240, 1373, 370, 133, 750, 1251, 442,
+ 1468, 350, 194, 614, 1888, 605, 419, 489, 1423, 2022,
+ 1317, 1412, 1587, 1144, 673, 1742, 849, 1656, 1134, 1017,
+ 1224, 885, 277, 1618, 152, 345, 927, 601, 196, 1612,
+ 1404, 701, 1055, 1678, 595, 855, 1329, 1372, 995, 969,
+ 307, 1754, 752, 510, 431, 1267, 1242, 1993, 1752, 708,
+ 1030, 2028, 1464, 1570, 792, 1861, 1831, 1194, 97, 1712,
+ 94, 1392, 330, 879, 453, 1432, 226, 130, 1810, 1792,
+ 314, 789, 768, 566, 349, 921, 598, 1405, 1361, 108,
+ 1286, 1579, 835, 1985, 1380, 831, 829, 36, 1443, 1069,
+ 619, 1948, 656, 2005, 1225, 1435, 1469, 1919, 1409, 641,
+ 1420, 1105, 222, 445, 780, 758, 282, 279, 1560, 1835,
+ 1922, 28, 807, 850, 529, 412, 685, 1542, 1086, 890,
+ 1377, 627, 882, 233, 239, 459, 395, 1638, 1708, 1956,
+ 1925, 1877, 1885, 1884, 1859, 1580, 104, 1252, 195, 1287,
+ 55, 1866, 1684, 1280, 296, 712, 663, 466, 175, 1347,
+ 432, 125, 1647, 1257, 293, 1794, 1068, 361, 1315, 1721,
+ 593, 1009, 1945, 372, 1691, 1965, 805, 1779, 1526, 1210,
+ 1733, 426, 1621, 926, 525, 19, 1018, 1890, 523, 1613,
+ 1685, 1547, 388, 39, 1383, 1162, 1142, 1717, 1421, 9,
+ 449, 545, 1553, 1698, 1795, 42, 1565, 1696, 1268, 313,
+ 180, 662, 751, 1318, 652, 1830, 1789, 1269, 854, 1281,
+ 1253, 1004, 26, 1109, 1041, 1605, 1664, 1270, 1797, 1961,
+ 775, 1450, 891, 1718, 2001, 774, 858, 559, 1939, 549,
+ 649, 710, 628, 880, 1593, 1692, 181, 680, 124, 1124,
+ 717, 952, 871, 1102, 819, 668, 629, 1954, 1662, 1720,
+ 1049, 1084, 836, 171, 1136, 20, 1039, 568, 1456, 913,
+ 1660, 1999, 1254, 174, 1876, 1969, 970, 1462, 1636, 1218,
+ 675, 1635, 1394, 1400, 1178, 1509, 574, 1657, 1277, 1048,
+ 1887, 1769, 1817, 1037, 183, 2034, 289, 1451, 953, 150,
+ 403, 1406, 715, 38, 100, 288, 1354, 505, 1655, 499,
+ 2003, 476, 1924, 306, 1205, 1786, 1554, 729, 788, 881,
+ 1536, 338, 743, 348, 435, 1739, 1943, 1024, 623, 1539,
+ 1467, 542, 1466, 520, 1169, 472, 206, 1997, 1642, 219,
+ 1929, 1874, 135, 358, 1117, 925, 1860, 1047, 1051, 1365,
+ 956, 1363, 487, 1416, 1544, 1234, 1852, 112, 79, 561,
+ 1342, 963, 1507, 1513, 1398, 87, 1376, 355, 1693, 1290,
+ 742, 368, 118, 1652, 615, 106, 579, 690, 564, 907,
+ 1709, 1917, 325, 1106, 853, 490, 1343, 1284, 351, 1643,
+ 1886, 157, 416, 1026, 69, 1583, 470, 732, 1248, 1750,
+ 421, 1977, 654, 433, 773, 1263, 1230, 847, 791, 766,
+ 1115, 822, 1148, 1439, 1458, 1575, 565, 1369, 1690, 664,
+ 1665, 540, 136, 402, 544, 612, 503, 851, 1646, 1499,
+ 1869, 92, 404, 972, 1222, 1203, 863, 954, 1732, 1552,
+ 1770, 1001, 674, 884, 653, 234, 1031, 1259, 923, 552,
+ 1836, 1619, 1110, 191, 611, 446, 267, 1686, 621, 984,
+ 57, 532, 555, 613, 1500, 142, 1837, 1232, 110, 202,
+ 88, 839, 861, 1562, 396, 848, 1135, 670, 1898, 707,
+ 1722, 1428, 243, 356, 1090, 184, 2023, 1632, 1970, 1485,
+ 255, 1213, 1799, 576, 1264, 47, 1099, 705, 43, 1902,
+ 458, 229, 398, 1702, 1990, 1628, 989, 724, 924, 494,
+ 678, 230, 1161, 460, 1208, 1490, 1790, 2026, 1601, 484,
+ 299, 908, 727, 1889, 1053, 771, 684, 95, 139, 292,
+ 634, 1313, 1492, 941, 1673, 1384, 1611, 643, 651, 1436,
+ 1370, 580, 1333, 1683, 354, 1470, 1629, 1950, 1841, 1353,
+ 1622, 1811, 679, 1724, 1700, 699, 1191, 218, 1964, 1321,
+ 1000, 843, 6, 1288, 1007, 166, 1875, 1821, 475, 507,
+ 934, 436, 1396, 96, 665, 53, 1947, 1489, 1133, 735,
+ 1667, 1172, 1247, 1385, 533, 883, 1035, 1582, 1541, 597,
+ 797, 1829, 328, 1061, 1878, 1759, 527, 1198, 366, 1599,
+ 558, 1159, 688, 1195, 769, 1212, 582, 1298, 1697, 114,
+ 1382, 943, 548, 1649, 1043, 2033, 1016, 739, 590, 1521,
+ 437, 339, 1216, 1843, 122, 893, 1308, 983, 661, 486,
+ 248, 1533, 1094, 1341, 697, 868, 1390, 2047, 1576, 1518,
+ 465, 692, 1074, 902, 1179, 977, 1238, 1679, 1187, 1558,
+ 1962, 164, 905, 596, 322, 914, 1689, 1367, 1323, 1987,
+ 1433, 1938, 1951, 817, 1773, 638, 512, 948, 16, 66,
+ 430, 833, 1197, 192, 1715, 1868, 283, 985, 577, 1081,
+ 1591, 1415, 237, 410, 169, 1569, 336, 862, 2045, 1531,
+ 1504, 1559, 451, 1484, 639, 1062, 1946, 1477, 1584, 2018,
+ 526, 224, 1381, 462, 1903, 1209, 1240, 864, 1879, 1199,
+ 1327, 1419, 1118, 1156, 103, 464, 1626, 1185, 1624, 387,
+ 469, 376, 1065, 522, 1564, 1625, 1452, 1516, 637, 1460,
+ 973, 852, 144, 1422, 1983, 1426, 200, 1630, 343, 2029,
+ 145, 1755, 1258, 1005, 620, 681, 964, 1100, 58, 1101,
+ 1273, 866, 49, 102, 1424, 1648, 846, 832, 814, 944,
+ 1066, 225, 700, 1434, 201, 594, 298, 785, 726, 1736,
+ 182, 1687, 1941, 763, 658, 1538, 1826, 635, 818, 1107,
+ 1153, 1914, 1928, 1805, 1152, 1758, 1310, 1444, 422, 1215,
+ 1183, 709, 1355, 262, 1033, 1992, 1480, 1249, 385, 1244,
+ 1449, 974, 1512, 1523, 1572, 209, 1414, 803, 204, 1479,
+ 1220, 1445, 1188, 1971, 399, 1295, 1088, 626, 1297, 694,
+ 1975, 1008, 1677, 1276, 93, 347, 1654, 947, 1672, 1502,
+ 1839, 1089, 252, 1530, 860, 379, 1976, 400, 231, 241,
+ 553, 48, 719, 1508, 1707, 76, 877, 740, 1781, 471,
+ 1568, 45, 1819, 2002, 978, 1905, 2011, 1395, 951, 155,
+ 1918, 1620, 1803, 452, 126, 698, 1129, 1095, 1261, 84,
+ 2013, 1163, 1312, 5, 1160, 1072, 1949, 374, 1808, 747,
+ 1663, 1011, 1998, 1190, 62, 216, 1546, 1058, 875, 1991,
+ 2025, 119, 1388, 389, 1845, 1623, 89, 1729, 790, 474,
+ 1324, 1525, 257, 1128, 1607, 904, 188, 2006, 1113, 497,
+ 1935, 1567, 1326, 448, 362, 1111, 1399, 1262, 1320, 1362,
+ 1870, 75, 1243, 454, 1407, 770, 1418, 518, 1897, 1921,
+ 795, 99, 915, 1787, 1013, 659, 1040, 411, 1862, 1675,
+ 1050, 1021, 1098, 1744, 384, 998, 1077,
+ };
+
+static const uint64_t load_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64};
+
+/* Test runners */
+int run_bpt_tests(void);
+int run_set_tests(void);
+int run_cow_tests(void);
+int run_queue_tests (void);
+int run_tqueue_tests (void);
+int run_lqueue_tests (void);
+
+/* fixtures */
+int bptree_test_setup(void **state);
+int bptree_str_test_setup(void **state);
+int bptree_test_teardown(void **state);
+
+int bptree_test_set_setup(void **state);
+int bptree_test_set_teardown(void **state);
+
+int bptree_test_cow_setup(void **state);
+int bptree_test_cow_teardown(void **state);
+
+int queue_test_setup(void **state);
+int queue_test_teardown(void **state);
+
+int tqueue_test_setup(void **state);
+int tqueue_test_teardown(void **state);
+
+int lqueue_test_setup(void **state);
+int lqueue_test_teardown(void **state);
+
+
diff --git a/src/libsds/test/test_sds_bpt.c b/src/libsds/test/test_sds_bpt.c
new file mode 100644
index 0000000..3243026
--- /dev/null
+++ b/src/libsds/test/test_sds_bpt.c
@@ -0,0 +1,983 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+
+static void
+test_asan_overflow(void)
+{
+ char *test = sds_malloc(4 * sizeof(char));
+ test[100] = 'a';
+ // At this point, ASAN should explode.
+ sds_log("test_asan", "FAIL: This should not be possible!");
+}
+
+static void
+test_asan_leak(void)
+{
+ // Just malloc this, it should fail in the end.
+ char *test = sds_malloc(4 * sizeof(char));
+}
+
+static void
+test_1_invalid_binst_ptr(void **state __attribute__((unused)))
+{
+ // This will test that the bptree init handles a null pointer correctly.
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(NULL, 0, sds_uint64_t_compare, sds_free, sds_uint64_t_free,
sds_uint64_t_dup);
+
+ assert_int_equal(result, SDS_NULL_POINTER);
+
+}
+
+// Template: sds_result test_fn(sds_bptree_instance *binst) {}
+
+static void
+test_3_single_insert(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t key = 1;
+ // We need to strdup this, so we can free it.
+ char *test = strdup("Some random value");
+ result = sds_bptree_insert(binst, (void *)&key, (void *)test);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_search(binst, (void *)&key);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+}
+
+static void
+test_4_single_null_insert_fn(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t key = 1;
+ result = sds_bptree_insert(binst, (void *)&key, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_search(binst, (void *)&key);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+}
+
+/*
+static void
+test_5_single_null_mismatch_size_insert_fn(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ result = sds_bptree_insert(binst, (void *)1, NULL);
+ assert_int_equal(result, SDS_INVALID_VALUE_SIZE);
+ result = sds_bptree_search(binst, (void *)1);
+ assert_int_equal(result, SDS_KEY_NOT_PRESENT);
+}
+*/
+
+static void
+test_6_insert_less_than_no_split(void **state) {
+
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ uint64_t key1 = 1;
+ uint64_t key2 = 2;
+
+ result = sds_bptree_insert(binst, (void *)&key2, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_insert(binst, (void *)&key1, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_search(binst, (void *)&key1);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_search(binst, (void *)&key2);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+}
+
+static void
+test_7_insert_greater_than_no_split(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ uint64_t key2 = 2;
+ uint64_t key3 = 3;
+
+ result = sds_bptree_insert(binst, (void *)&key2, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_insert(binst, (void *)&key3, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_search(binst, (void *)&key3);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_search(binst, (void *)&key2);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+}
+
+static void
+test_8_insert_duplicate(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t key2 = 2;
+
+ result = sds_bptree_insert(binst, (void *)&key2, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_insert(binst, (void *)&key2, NULL);
+ assert_int_equal(result, SDS_DUPLICATE_KEY);
+
+ result = sds_bptree_search(binst, (void *)&key2);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+}
+
+static void
+test_9_insert_fill_and_split(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ // Add two to guarantee we don't conflict
+ for (uint64_t i = 2; i <= SDS_BPTREE_DEFAULT_CAPACITY + 2; i++) {
+ result = sds_bptree_insert(binst, (void *)&i, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ for (uint64_t i = 2; i <= SDS_BPTREE_DEFAULT_CAPACITY + 2; i++) {
+ result = sds_bptree_search(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+
+}
+
+static void
+test_10_tamper_with_inst(void **state __attribute__((unused))) {
+ // The verifictation in the wrapper will fail for us.
+
+ // Create a new tree, and then destroy it.
+ sds_bptree_instance *binst = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(&binst, 1, sds_uint64_t_compare, sds_free,
sds_uint64_t_free, sds_uint64_t_dup);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ binst->print_iter += 1;
+
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_CHECKSUM_FAILURE);
+
+ result = sds_bptree_destroy(binst);
+ /* If this reports unknown, it means we may not have freed some nodes. */
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_11_tamper_with_node(void **state __attribute__((unused))) {
+ // The verifictation in the wrapper will fail for us.
+
+ // Create a new tree, and then destroy it.
+ sds_bptree_instance *binst = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(&binst, 1, sds_uint64_t_compare, sds_free,
sds_uint64_t_free, sds_uint64_t_dup);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ binst->root->keys[0] = (void *)1;
+
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_CHECKSUM_FAILURE);
+
+ result = sds_bptree_destroy(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_12_insert_fill_split_and_grow(void **state) {
+ // This should make the tree grow and split enough to need to add extra heigh beyond
the root.
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ for (uint64_t i = 0; i <= ((SDS_BPTREE_DEFAULT_CAPACITY + 1) << 3) ; i++) {
+ uint64_t ti = i + 2;
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&ti, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ for (uint64_t i = 0; i <= ((SDS_BPTREE_DEFAULT_CAPACITY + 1) << 3) ; i++) {
+ uint64_t ti = i + 2;
+ result = sds_bptree_search(binst, (void *)&ti);
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not find %d",
ti);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+
+}
+
+static void
+test_13_insert_fill_split_and_grow_inverse(void **state) {
+ // This should make the tree grow and split enough to need to add extra heigh beyond
the root.
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ for (uint64_t i = ((SDS_BPTREE_DEFAULT_CAPACITY + 1) << 3) + 1; i > 0 ;
i--) {
+ // Add two to guarantee we don't conflict
+ uint64_t ti = i + 2;
+ result = sds_bptree_insert(binst, (void *)&ti, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ for (uint64_t i = ((SDS_BPTREE_DEFAULT_CAPACITY + 1) << 3) + 1; i > 0 ;
i--) {
+ uint64_t ti = i + 2;
+ result = sds_bptree_search(binst, (void *)&ti);
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not find %d",
ti);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+}
+
+// test14 is search
+static void
+test_14_insert_random(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ for (uint64_t i = 0; i < 200 ; i++) {
+ result = sds_bptree_insert(binst, (void *)&(fill_pattern[i]) , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+
+ }
+ // search
+
+ for (uint64_t i = 0; i < 200 ; i++) {
+ result = sds_bptree_search(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not find %d",
fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+}
+
+
+// test15 is search not present
+
+static void
+test_15_search_none(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ // Add many nodes.
+
+ for (uint64_t i = 1; i <= 20 ; i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ // search
+ uint64_t ti = 25;
+ result = sds_bptree_search(binst, (void *)&ti);
+ // key should be there
+ assert_int_equal(result, SDS_KEY_NOT_PRESENT);
+
+}
+
+static void
+test_16_insert_and_retrieve(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ uint64_t key = 1;
+ char *magic = strdup("magic");
+
+ result = sds_bptree_insert(binst, (void *)&key, magic);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ char *dest = NULL;
+ result = sds_bptree_retrieve(binst, (void *)&key, (void **)&dest);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ assert_int_equal(strncmp(dest, magic, 6 ), 0);
+
+ // Freed as part of the tree release
+ // sds_free(dest);
+ // sds_free(magic);
+}
+
+// test 17 insert and tamper with the data underneath
+/*
+static void
+test_17_insert_and_tamper(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ char *magic = strdup("magic");
+
+ result = sds_bptree_insert(binst, (void *)1, magic);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ // Right, now we cheat. Because we know the internals of the library, we can
+ // start to mess with stuff.
+
+ sds_bptree_value *value = binst->root->values[0];
+
+ char *data = (char *)value->data;
+ strncpy(data, "hello", 6);
+
+ char *dest = NULL;
+
+ result = sds_bptree_retrieve(binst, (void *)1, (void **)&dest);
+ assert_int_equal(result, SDS_CHECKSUM_FAILURE);
+
+ // Put the original data back, else we fail the teardown validation ....
+ strncpy(data, "magic", 6);
+
+ // sds_free(dest);
+ // sds_free(magic);
+}
+*/
+
+static void
+test_18_delete_single_value(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t key = 1;
+ char *test = strdup("Some random value");
+ sds_bptree_insert(binst, (void *)&key, (void *)test);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_delete(binst, (void *)&key);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ // sds_free(dest);
+}
+
+static void
+test_19_delete_non_existant(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t key = 1;
+ char *test = strdup("Some random value");
+ // Need to add one to account for the null byte ... fucking C.
+ sds_bptree_insert(binst, (void *)&key, (void *)test);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ key = 2;
+ result = sds_bptree_delete(binst, (void *)&key);
+ assert_int_equal(result, SDS_KEY_NOT_PRESENT);
+
+ // Should be null anyway!
+ // sds_free(dest);
+}
+
+static void
+test_20_delete_non_branch_key(void **state)
+{
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ // Add many nodes.
+ uint64_t i = 0;
+
+ for (uint64_t i = 2; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ }
+
+ // Inspect the internal to the tree to get the key from the node.
+ i = *(uint64_t *)(binst->root)->keys[0];
+
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+
+}
+
+static void
+test_21_delete_redist_left_leaf(void **state)
+{
+ /*
+ * This will make two nodes, A, B. When we delete from B, we get below the
+ * half node threshhold, so when we delete the key, we borrow max from A
+ * into B as the new lowest node. This will *not* update the parent link
+ * that points to B.
+ */
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ uint64_t i = 2;
+ // Add many nodes.
+
+ for (i = 2; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3) ; i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ // I is increment 1 extra, so decrement it ....
+ i--;
+
+#ifdef DEBUG
+ printf("Deleting %" PRIu64 "\n", i);
+#endif
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ i--;
+#ifdef DEBUG
+ printf("Deleting %" PRIu64 "\n", i);
+#endif
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ // How can I test for and prove that the value was redistributed?
+ // Return node of the previous?
+
+}
+
+static void
+test_22_delete_redist_right_leaf(void **state)
+{
+ /*
+ * We make a node, A, B, where B > A. We delete two nodes under A, which will
+ * force B to give nodes to A. This also causes an update of the root ref
+ * to B.
+ */
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ // Add many nodes.
+
+ for (uint64_t i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ // Now delete these two keys
+ uint64_t keya = SDS_BPTREE_DEFAULT_CAPACITY * 2;
+ uint64_t keyb = keya + 1;
+ // printf("Deleting %d\n", keya);
+ // printf("Deleting %d\n", keyb);
+
+ result = sds_bptree_delete(binst, (void *)&keya);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification failed
%d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+
+ /* Next delete */
+ result = sds_bptree_delete(binst, (void *)&keyb);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification failed
%d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+
+}
+
+static void
+test_22_5_redist_left_borrow(void **state)
+{
+ /* We make a small tree, that only splits once. Then we start to delete
+ * from the right node, which should force us to borrow from the left
+ * The construction of the tree is critical to allow overloading the left
+ * node to be > BHALF else this case won't trigger.
+ *
+ * This is also the first test to test deleting the root on deletion!
+ */
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ // Add many nodes.
+
+ for (uint64_t i = 3; i <= SDS_BPTREE_DEFAULT_CAPACITY + 2; i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ uint64_t key = 1;
+ result = sds_bptree_insert(binst, (void *)&key , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ key = 2;
+ result = sds_bptree_insert(binst, (void *)&key , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ for (uint64_t i = SDS_BPTREE_DEFAULT_CAPACITY; i > 2 ; i--) {
+#ifdef DEBUG
+ printf("Deleting %" PRIu64 "\n ", i);
+#endif
+ key = i + 1;
+ result = sds_bptree_delete(binst, (void *)&key);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+}
+
+static void
+test_23_delete_right_merge(void **state)
+{
+ // Delete keys from the A, and B, node, But B second. This shuold force A- >
merge
+ // Because the node in Branch 4 is:
+ // [ 7 | 8 | 9 | - ] -> [ 10 | 11 | 12 | - ]
+ // So we need to delete One of the nodes from A To make it on B_HALF.
+ // Hint: B is the right node here
+
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ // Add many nodes.
+
+ for (uint64_t i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ // Is there a way we can make this test work with a BFACTOR of != 4?
+ // Then when we delete 8 / 9 from A
+ // [ 7 | 8 | - | - ] -> [ 10 | 11 | 12 | - ]
+ // Now we start to remove from B.
+ // [ 7 | 8 | - | - ] -> [ 10 | 11 | - | - ]
+ // [ 7 | 8 | - | - ] -> [ 10 | - | - | - ]
+ // At this point the merge should begin, and we get:
+ // [ 7 | 8 | 10 | - ] -> [ - | - | - | - ]
+ // [ 7 | 8 | 10 | - ]
+ // Too easy ... ?
+
+ for (uint64_t i = (SDS_BPTREE_DEFAULT_CAPACITY * 3); i >=
SDS_BPTREE_DEFAULT_CAPACITY * 2; i--) {
+ // Add two to guarantee we don't conflict
+ // printf("Deleting %d\n ", i);
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+
+ // How do we actually assert this?
+ // I think we can check binst node_count.
+
+ return;
+}
+
+
+static void
+test_24_delete_left_merge(void **state)
+{
+ // Delete keys from the A and B node, but A second. This should force B -> A
merge.
+ // Because the node in Branch 4 is:
+ // [ 7 | 8 | 9 | - ] -> [ 10 | 11 | 12 | - ]
+ // So we need to delete One of the nodes from B To make it on B_HALF.
+ // Hint: B is the right node here
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ uint64_t i = 2;
+ // Add many nodes.
+
+ for (i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ // Then when we delete 11/12 from B
+ // [ 7 | 8 | 9 | - ] -> [ 10 | 11 | - | - ]
+ // Now we start to remove from A.
+ // [ 7 | 8 | - | - ] -> [ 10 | 11 | - | - ]
+ // Now we start to remove from A.
+ // [ 7 | - | - | - ] -> [ 10 | 11 | - | - ]
+ // At this point the merge should begin.
+ // Final result.
+ // [ - | - | - | - ] -> [ 7 | 10 | 11 | - ]
+ // And A should be removed too.
+
+ for (i = SDS_BPTREE_DEFAULT_CAPACITY; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 2) ;
i++) {
+ // Add two to guarantee we don't conflict
+#ifdef DEBUG
+ printf("Deleting %" PRIu64 "\n ", i);
+#endif
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+}
+
+static void
+test_25_delete_all_compress_root(void **state)
+{
+ // Add keys to grow a new root, then *delete* all the keys, we should shrink the
tree.
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ uint64_t i = 0;
+ // Add many nodes.
+
+ for (i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ for (i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 3); i++) {
+ // Add two to guarantee we don't conflict
+#ifdef DEBUG
+ printf("Deleting %" PRIu64 "\n ", i);
+#endif
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ }
+
+ return;
+}
+
+static void
+test_26_delete_right_branch_merge(void **state)
+{
+ // Delete keys from the A, and B, node, But B second. This shuold force A- >
merge
+ // Because the node in Branch 4 is:
+ // [ 7 | 8 | 9 | - ] -> [ 10 | 11 | 12 | - ]
+ // So we need to delete One of the nodes from A To make it on B_HALF.
+ // Hint: B is the right node here
+
+ // Difference here, is that we want to act on the branch, not the leafs
+
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ uint64_t i = 2;
+ // Add many nodes.
+
+ for (i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 6); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ // Then when we delete 8 / 9 from A
+ // [ 7 | 8 | - | - ] -> [ 10 | 11 | 12 | - ]
+ // Now we start to remove from B.
+ // [ 7 | 8 | - | - ] -> [ 10 | 11 | - | - ]
+ // [ 7 | 8 | - | - ] -> [ 10 | - | - | - ]
+ // At this point the merge should begin, and we get:
+ // [ 7 | 8 | 10 | - ] -> [ - | - | - | - ]
+ // [ 7 | 8 | 10 | - ]
+ // Too easy ... ?
+
+ for (i = (SDS_BPTREE_DEFAULT_CAPACITY * 6); i >= SDS_BPTREE_DEFAULT_CAPACITY * 2;
i--) {
+ // Add two to guarantee we don't conflict
+ // printf("Deleting %d\n ", i);
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ }
+
+ // How do we actually assert this?
+ // I think we can check binst node_count.
+
+ return;
+}
+
+
+static void
+test_27_delete_left_branch_merge(void **state)
+{
+ // Delete keys from the A and B node, but A second. This should force B -> A
merge.
+ // Because the node in Branch 4 is:
+ // [ 7 | 8 | 9 | - ] -> [ 10 | 11 | 12 | - ]
+ // So we need to delete One of the nodes from B To make it on B_HALF.
+ // Hint: B is the right node here
+ // Difference here is we are acting on the branches not the leaves alone now.
+ sds_bptree_instance *binst = *state;
+ // First, make a tree with 3 * SDS_BPTREE_DEFAULT_CAPACITY, then delete the highest
+ sds_result result = SDS_SUCCESS;
+ uint64_t i = 2;
+ // Add many nodes.
+
+ for (i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 6); i++) {
+ // Add two to guarantee we don't conflict
+ result = sds_bptree_insert(binst, (void *)&i , NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ // Then when we delete 11/12 from B
+ // [ 7 | 8 | 9 | - ] -> [ 10 | 11 | - | - ]
+ // Now we start to remove from A.
+ // [ 7 | 8 | - | - ] -> [ 10 | 11 | - | - ]
+ // Now we start to remove from A.
+ // [ 7 | - | - | - ] -> [ 10 | 11 | - | - ]
+ // At this point the merge should begin.
+ // Final result.
+ // [ - | - | - | - ] -> [ 7 | 10 | 11 | - ]
+ // And A should be removed too.
+ for (i = SDS_BPTREE_DEFAULT_CAPACITY * 2; i <= (SDS_BPTREE_DEFAULT_CAPACITY * 6) ;
i++) {
+ // Add two to guarantee we don't conflict
+#ifdef DEBUG
+ printf("Deleting %" PRIu64 "\n ", i);
+#endif
+ result = sds_bptree_delete(binst, (void *)&i);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+}
+
+// Test for branch key borrowing. Will need to add things out of order.
+
+// stress test and random operations. Should pass
+static void
+test_28_insert_and_delete_random(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ for (uint64_t i = 0; i < 200 ; i++) {
+ result = sds_bptree_insert(binst, (void *)&(fill_pattern[i]), NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+
+ }
+ // search
+
+ for (uint64_t i = 0; i < 200 ; i++) {
+ result = sds_bptree_search(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not find %d",
fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_delete(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not delete
%d", fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_search(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_NOT_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can find %d",
fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_NOT_PRESENT);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+
+ }
+}
+
+// stress test and random operations. Should pass
+static void
+test_29_insert_and_delete_random_large(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ for (uint64_t i = 0; i < 2048 ; i++) {
+ result = sds_bptree_insert(binst, (void *)&(fill_pattern[i]), NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+
+ }
+ for (uint64_t i = 0; i < 2048 ; i++) {
+ result = sds_bptree_search(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not find %d",
fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_delete(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can not delete
%d", fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_PRESENT);
+
+ result = sds_bptree_search(binst, (void *)&(fill_pattern[i]));
+ if (result != SDS_KEY_NOT_PRESENT) {
+ sds_log("bptree_test_teardown", "FAIL: Can find %d",
fill_pattern[i]);
+ }
+ assert_int_equal(result, SDS_KEY_NOT_PRESENT);
+
+ result = sds_bptree_verify(binst);
+
+ if (result != SDS_SUCCESS) {
+ sds_log("bptree_test_teardown", "FAIL: B+Tree verification
failed %d binst", result);
+ }
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+}
+
+static void
+test_30_insert_and_delete_strings(void **state __attribute__((unused))) {
+ // Create a new tree, and then destroy it.
+ sds_bptree_instance *binst = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_bptree_init(&binst, 1, sds_strcmp, sds_free, sds_free, sds_strdup);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ for (uint64_t i = 0; i < 200 ; i++) {
+ /* Make a new string */
+ char *ptr = sds_malloc(sizeof(char) * 4);
+ /* */
+ sprintf(ptr, "%03"PRIu64, i);
+ /* Insert CLONES this .... */
+ result = sds_bptree_insert(binst, ptr, NULL);
+ /* Free the string now, the tree cloned it! */
+ sds_free(ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ for (uint64_t i = 20; i < 150; i+=3) {
+ char *ptr = sds_malloc(sizeof(char) * 4);
+ sprintf(ptr, "%03"PRIu64, i);
+ result = sds_bptree_delete(binst, ptr);
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ sds_free(ptr);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+
+ result = sds_bptree_destroy(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+
+}
+
+int
+run_bpt_tests (void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_1_invalid_binst_ptr),
+#ifdef DEBUG
+ cmocka_unit_test(test_10_tamper_with_inst),
+ cmocka_unit_test(test_11_tamper_with_node),
+ /*
+ cmocka_unit_test_setup_teardown(test_17_insert_and_tamper,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_5_single_null_mismatch_size_insert_fn,
+ bptree_test_setup,
+ bptree_test_teardown),
+ */
+#endif
+ cmocka_unit_test_setup_teardown(test_3_single_insert,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_4_single_null_insert_fn,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_6_insert_less_than_no_split,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_7_insert_greater_than_no_split,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_8_insert_duplicate,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_9_insert_fill_and_split,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_12_insert_fill_split_and_grow,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_13_insert_fill_split_and_grow_inverse,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_14_insert_random,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_15_search_none,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_16_insert_and_retrieve,
+ /* Setup a string capable tree instead */
+ bptree_str_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_18_delete_single_value,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_19_delete_non_existant,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_20_delete_non_branch_key,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_21_delete_redist_left_leaf,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_22_delete_redist_right_leaf,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_22_5_redist_left_borrow,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_23_delete_right_merge,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_24_delete_left_merge,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_25_delete_all_compress_root,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_26_delete_right_branch_merge,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_27_delete_left_branch_merge,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_28_insert_and_delete_random,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_29_insert_and_delete_random_large,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test(test_30_insert_and_delete_strings),
+ };
+
+ return cmocka_run_group_tests_name("bpt", tests, NULL, NULL);
+
+}
diff --git a/src/libsds/test/test_sds_cow.c b/src/libsds/test/test_sds_cow.c
new file mode 100644
index 0000000..4ad5375
--- /dev/null
+++ b/src/libsds/test/test_sds_cow.c
@@ -0,0 +1,613 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+
+/* test_bptree is the test driver for the B+ Tree implemented by bptree/bptree.c */
+
+/* Contains test data. */
+#include "test_sds.h"
+
+// Predefine for now ...
+#ifdef DEBUG
+sds_result sds_bptree_cow_display(sds_bptree_transaction *btxn);
+#endif
+
+static void
+test_1_cow_init(void **state __attribute__((unused))) {
+ sds_bptree_cow_instance *binst;
+ sds_result result = SDS_SUCCESS;
+ // Check a null init
+ result = sds_bptree_cow_init(NULL, 1, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_dup, sds_uint64_t_free, sds_uint64_t_dup);
+ assert_int_equal(result, SDS_NULL_POINTER);
+ // Check a real init
+ result = sds_bptree_cow_init(&binst, 1, sds_uint64_t_compare, sds_uint64_t_free,
sds_uint64_t_dup, sds_uint64_t_free, sds_uint64_t_dup);
+ assert_int_equal(result, SDS_SUCCESS);
+ // Destroy.
+ result = sds_bptree_cow_destroy(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+// Test transactions.
+
+static void
+test_2_begin_rotxn(void **state) {
+ /* Take a read only txn */
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *btxn = NULL;
+
+ result = sds_bptree_cow_rotxn_begin(binst, &btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Check the refcount is 2 */
+ assert_int_equal(binst->txn->reference_count, 2);
+ /* Close it */
+ result = sds_bptree_cow_rotxn_close(&btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Check the refcount is 1 */
+ assert_int_equal(binst->txn->reference_count, 1);
+}
+
+static void
+test_3_begin_wrtxn_no_read(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *btxn = NULL;
+ sds_bptree_transaction *btxn_ref = NULL;
+
+ /* Make a txn, and then abort. Make sure it doesn't update. */
+ result = sds_bptree_cow_wrtxn_begin(binst, &btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Make sure the txn id's differ */
+ assert_int_not_equal(btxn->txn_id, binst->txn->txn_id);
+
+ result = sds_bptree_cow_wrtxn_abort(&btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_not_equal(btxn, binst->txn);
+
+ /* Now make a txn and commit, assert it's added. */
+ result = sds_bptree_cow_wrtxn_begin(binst, &btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ /* Commit will null the txn pointer, so we take a ref. */
+ btxn_ref = btxn;
+ result = sds_bptree_cow_wrtxn_commit(&btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_equal(btxn_ref, binst->txn);
+}
+
+static void
+test_4_begin_wrtxn_w_read(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *ro1_btxn = NULL;
+ sds_bptree_transaction *ro2_btxn = NULL;
+ sds_bptree_transaction *ro3_btxn = NULL;
+ sds_bptree_transaction *wr1_btxn = NULL;
+
+ /* Take a read txn */
+ result = sds_bptree_cow_rotxn_begin(binst, &ro1_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Take and commit a write txn */
+ result = sds_bptree_cow_wrtxn_begin(binst, &wr1_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_cow_wrtxn_commit(&wr1_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Take a new read txn. */
+ result = sds_bptree_cow_rotxn_begin(binst, &ro2_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Assert they are different. */
+ assert_ptr_not_equal(ro1_btxn, ro2_btxn);
+ /* Take and commit a write txn */
+ result = sds_bptree_cow_wrtxn_begin(binst, &wr1_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_cow_wrtxn_commit(&wr1_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Take a read txn */
+ result = sds_bptree_cow_rotxn_begin(binst, &ro3_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Assert they are different. */
+ assert_ptr_not_equal(ro3_btxn, ro2_btxn);
+ assert_ptr_not_equal(ro3_btxn, ro1_btxn);
+ /* Close them all, order doesn't matter */
+ result = sds_bptree_cow_rotxn_close(&ro1_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_cow_rotxn_close(&ro3_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_cow_rotxn_close(&ro2_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+// Test destroy with an open write txn.
+// Test destroy with an open read txn.
+
+// Test basic insert and delete
+
+static void
+test_misuse_rotxn(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *ro_btxn = NULL;
+
+ result = sds_bptree_cow_rotxn_begin(binst, &ro_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_cow_insert(ro_btxn, NULL, NULL);
+ assert_int_equal(result, SDS_INVALID_TXN);
+ result = sds_bptree_cow_delete(ro_btxn, NULL);
+ assert_int_equal(result, SDS_INVALID_TXN);
+ result = sds_bptree_cow_rotxn_close(&ro_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+// Similar to the basic tests of bptree, we need to quickly assert that at least
+// within a write transaction, our tree operations *are* sane.
+static void
+test_basic_insert(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *wr_btxn = NULL;
+
+ uint64_t key1 = 1;
+ uint64_t key2 = 2;
+
+ result = sds_bptree_cow_wrtxn_begin(binst, &wr_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ // Now do an insert, and search.
+ result = sds_bptree_cow_insert(wr_btxn, (void *)&key1, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_search(wr_btxn, (void *)&key1),
SDS_KEY_PRESENT);
+ assert_int_equal(sds_bptree_cow_search(wr_btxn, (void *)&key2),
SDS_KEY_NOT_PRESENT);
+
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ // Dump the current ro txn too.
+ // THIS IS A HACK, YOU SHOULD BE TAKING AN RO TXN YOURSELF!
+ // now insert enough to cause some splits and search.
+ for (uint64_t i = 10; i < (10 + SDS_BPTREE_DEFAULT_CAPACITY); i ++) {
+ result = sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_search(wr_btxn, (void *)&i),
SDS_KEY_PRESENT);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ }
+
+ // assert_int_equal(sds_bptree_cow_display(binst->txn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ // Check that an ro_txn still has nothing
+ // commit
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ // now check it's all there.
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+
+ // destroy ro txns
+}
+
+// These tests are based on the bpt tests, but basically just do hard tests
+// early on.
+
+static void
+test_large_insert(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *wr_btxn = NULL;
+ sds_bptree_transaction *ro_btxn = NULL;
+
+ for (uint64_t i = 1; i <= ((SDS_BPTREE_DEFAULT_CAPACITY + 1) << 4) ; i++) {
+ result = sds_bptree_cow_wrtxn_begin(binst, &wr_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_search(wr_btxn, (void *)&i),
SDS_KEY_PRESENT);
+ if (i == 40) {
+ result = sds_bptree_cow_rotxn_begin(binst, &ro_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ // now check it's all there.
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ }
+
+ // assert_int_equal(sds_bptree_cow_display(ro_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+
+ result = sds_bptree_cow_rotxn_close(&ro_btxn);
+ result = sds_bptree_cow_rotxn_close(&wr_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+// Test random with commits between each insert.
+
+static void
+test_random_insert(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+ sds_bptree_transaction *wr_btxn = NULL;
+
+ for (uint64_t i = 1; i <= 200 ; i++) {
+ result = sds_bptree_cow_wrtxn_begin(binst, &wr_btxn);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ result = sds_bptree_cow_insert(wr_btxn, (void *)&(fill_pattern[i]), NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_search(wr_btxn, (void *)&(fill_pattern[i])),
SDS_KEY_PRESENT);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ // now check it's all there.
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ }
+}
+
+// Test rotxn, wrtxn + insert, rotxn then search on both and assert the difference.
+
+static void
+test_out_of_order_txn_close(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+
+ sds_bptree_transaction *wr_btxn = NULL;
+ sds_bptree_transaction *ro_btxn_a = NULL;
+ sds_bptree_transaction *ro_btxn_b = NULL;
+ sds_bptree_transaction *ro_btxn_c = NULL;
+ uint64_t i = 2;
+ uint64_t key1 = 1;
+ uint64_t key3 = 3;
+
+ // Prepare the tree.
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ for (; i <= ((SDS_BPTREE_DEFAULT_CAPACITY + 1) * 2); i+=2) {
+ // This needs to add enough nodes to trigger a split
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL),
SDS_SUCCESS);
+ }
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ // First, open a write commit, and add an *odd* value.
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&key1, NULL),
SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ // Now take an RO txn to it.
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_a), SDS_SUCCESS);
+
+ // Second, open another write commit, add some more.
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ // Now take an RO txn to it.
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_b), SDS_SUCCESS);
+
+ // Now take the third write txn, add some value.
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&key3, NULL),
SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_c), SDS_SUCCESS);
+
+ // Now free the second txn.
+ /* At this point, ro_btxn_b should "own" the nodes in btxn_a, because of
COW */
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_b), SDS_SUCCESS);
+
+ // Now seacch for the taken value in the FIRST txn. Node may be freed.
+ assert_int_equal(sds_bptree_cow_search(ro_btxn_a, (void *)&key1),
SDS_KEY_PRESENT);
+ assert_int_equal(sds_bptree_cow_search(ro_btxn_a, (void *)&key3),
SDS_KEY_NOT_PRESENT);
+ assert_int_equal(sds_bptree_cow_search(ro_btxn_a, (void *)&i),
SDS_KEY_NOT_PRESENT);
+
+ // Now close the first txn.
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_a), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_c), SDS_SUCCESS);
+
+}
+
+// Test that leaving an RO txn alive doesn't break shutdown.
+static void
+test_dangling_txn_close(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+
+ sds_bptree_transaction *ro_btxn_a = NULL;
+ uint64_t key1 = 1;
+ uint64_t key2 = 2;
+
+ // Add a value
+ assert_int_equal(sds_bptree_cow_insert_atomic(binst, (void *)&key1, NULL),
SDS_SUCCESS);
+ // Open a txn and leave it dangling.
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_a), SDS_SUCCESS);
+ // Now complete another write to be sure it's there in the tail.
+ assert_int_equal(sds_bptree_cow_insert_atomic(binst, (void *)&key2, NULL),
SDS_SUCCESS);
+
+}
+
+// Test aborting a txn doesn't cause leaks.
+
+static void
+test_txn_abort(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+
+ sds_bptree_transaction *wr_btxn = NULL;
+ uint64_t key1 = 1;
+ uint64_t key2 = 2;
+
+ // Add a value
+ assert_int_equal(sds_bptree_cow_insert_atomic(binst, (void *)&key1, NULL),
SDS_SUCCESS);
+
+ // Create a new txn.
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ // Do an insert to trigger the COW
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&key2, NULL),
SDS_SUCCESS);
+ // And abort it!
+ assert_int_equal(sds_bptree_cow_wrtxn_abort(&wr_btxn), SDS_SUCCESS);
+
+}
+
+// Should set and retrieve a value from the tree atomically.
+// Perhaps a string type?
+static void
+test_txn_atomic_retrieve(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ uint64_t key5 = 5;
+ assert_int_equal(sds_bptree_cow_insert_atomic(binst, (void *)&key5,
sds_uint64_t_dup((void *)&key5)), SDS_SUCCESS);
+ uint64_t *x = 0;
+ assert_int_equal(sds_bptree_cow_retrieve_atomic(binst, (void *)&key5, (void
**)&x), SDS_KEY_PRESENT);
+ assert_int_equal(*x, 5);
+ /* With the atomics WE OWN the memory as it was dupped */
+ /* else it's not very atomic!!! */
+ sds_uint64_t_free(x);
+}
+
+/* Test double free of a txn */
+static void
+test_txn_double_close(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+
+ sds_bptree_transaction *ro_btxn_a = NULL;
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_a), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_a), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_a), SDS_INVALID_TXN);
+}
+/* Test misuse of write txn */
+static void
+test_txn_post_commit_use(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+ uint64_t key1 = 1;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&key1, NULL),
SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_rotxn_close(&wr_btxn), SDS_INVALID_TXN);
+ assert_int_equal(sds_bptree_cow_wrtxn_abort(&wr_btxn), SDS_INVALID_TXN);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_INVALID_TXN);
+}
+
+/* Test that all txn search fns handle null */
+static void
+test_null_txn(void **state __attribute__((unused))) {
+ // sds_bptree_cow_instance *binst = *state;
+ uint64_t key1 = 1;
+ void *x = NULL;
+
+ assert_int_equal(sds_bptree_cow_insert(NULL, (void *)&key1, NULL),
SDS_INVALID_TXN);
+ assert_int_equal(sds_bptree_cow_delete(NULL, (void *)&key1), SDS_INVALID_TXN);
+ assert_int_equal(sds_bptree_cow_search(NULL, (void *)&key1), SDS_INVALID_TXN);
+ assert_int_equal(sds_bptree_cow_retrieve(NULL, (void *)&key1, &x),
SDS_INVALID_TXN);
+}
+
+static void
+test_txn_delete_simple(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+ uint64_t key1 = 1;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&key1, NULL),
SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_delete(wr_btxn, (void *)&key1),
SDS_KEY_PRESENT);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+}
+
+static void
+test_txn_delete_leaf_left(void **state) {
+ /* Add enough to create some extra nodes */
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ for (uint64_t i = 1; i <= SDS_BPTREE_DEFAULT_CAPACITY * 4; i++) {
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL),
SDS_SUCCESS);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ }
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ /* Now delete down to create borrow and merge left */
+
+ for (uint64_t i = SDS_BPTREE_DEFAULT_CAPACITY * 4; i > SDS_BPTREE_DEFAULT_CAPACITY
* 2; i--) {
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_delete(wr_btxn, (void *)&i),
SDS_KEY_PRESENT);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ }
+}
+
+static void
+test_txn_delete_leaf_right(void **state) {
+ /* Add enough to create some extra nodes */
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ for (uint64_t i = 1; i <= SDS_BPTREE_DEFAULT_CAPACITY * 4; i++) {
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL),
SDS_SUCCESS);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ }
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ /* Now delete up to create borrow and merge right */
+
+ for (uint64_t i = 1; i < SDS_BPTREE_DEFAULT_CAPACITY * 2; i++) {
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_delete(wr_btxn, (void *)&i),
SDS_KEY_PRESENT);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ }
+}
+
+static void
+test_txn_delete_branch_left(void **state) {
+ /* Add enough to create some extra nodes */
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ for (uint64_t i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY << 3); i++) {
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL),
SDS_SUCCESS);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ }
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ /* Now delete down to create borrow and merge left in branches */
+ for (uint64_t i = (SDS_BPTREE_DEFAULT_CAPACITY << 3); i > 0; i--) {
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_delete(wr_btxn, (void *)&i),
SDS_KEY_PRESENT);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ }
+}
+
+static void
+test_txn_delete_branch_right(void **state) {
+ /* Add enough to create some extra nodes */
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ for (uint64_t i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY << 3); i++) {
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&i, NULL),
SDS_SUCCESS);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ }
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ /* Now delete down to create borrow and merge left in branches */
+ for (uint64_t i = 1; i <= (SDS_BPTREE_DEFAULT_CAPACITY << 3); i++) {
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_delete(wr_btxn, (void *)&i),
SDS_KEY_PRESENT);
+ // assert_int_equal(sds_bptree_cow_display(wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_verify(binst), SDS_SUCCESS);
+ }
+}
+
+static void
+test_cow_update(void **state) {
+ sds_bptree_cow_instance *binst = *state;
+ sds_bptree_transaction *wr_btxn = NULL;
+ sds_bptree_transaction *ro_btxn_a = NULL;
+ sds_bptree_transaction *ro_btxn_b = NULL;
+
+ uint64_t key1 = 1;
+ uint64_t key2 = 2;
+ uint64_t key3 = 3;
+ uint64_t *result = NULL;
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ // We have to dup the keys on insert as values, the value life time is now bound to
the tree
+ assert_int_equal(sds_bptree_cow_insert(wr_btxn, (void *)&key1,
sds_uint64_t_dup((void *)&key1)), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_a), SDS_SUCCESS);
+ /* Don't close yet! */
+
+ assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_update(wr_btxn, (void *)&key1,
sds_uint64_t_dup((void *)&key2)), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_update(wr_btxn, (void *)&key2,
sds_uint64_t_dup((void *)&key3)), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS);
+
+ assert_int_equal(sds_bptree_cow_rotxn_begin(binst, &ro_btxn_b), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_retrieve(ro_btxn_b, (void *)&key1, (void
*)&result), SDS_KEY_PRESENT);
+ assert_int_equal(*result, 2);
+ assert_int_equal(sds_bptree_cow_retrieve(ro_btxn_b, (void *)&key2, (void
*)&result), SDS_KEY_PRESENT);
+ assert_int_equal(*result, 3);
+ /* Make sure we didn't affect the previous transaction. */
+ assert_int_equal(sds_bptree_cow_retrieve(ro_btxn_a, (void *)&key1, (void
*)&result), SDS_KEY_PRESENT);
+ assert_int_equal(*result, 1);
+
+ /* Close now */
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_a), SDS_SUCCESS);
+ assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_b), SDS_SUCCESS);
+}
+
+int
+run_cow_tests (void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_1_cow_init),
+ cmocka_unit_test_setup_teardown(test_2_begin_rotxn,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_3_begin_wrtxn_no_read,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_4_begin_wrtxn_w_read,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_misuse_rotxn,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_basic_insert,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_large_insert,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_random_insert,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_out_of_order_txn_close,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_dangling_txn_close,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_abort,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_atomic_retrieve,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_double_close,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_post_commit_use,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_null_txn,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_delete_simple,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_delete_leaf_left,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_delete_leaf_right,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_delete_branch_left,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_txn_delete_branch_right,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ cmocka_unit_test_setup_teardown(test_cow_update,
+ bptree_test_cow_setup,
+ bptree_test_cow_teardown),
+ };
+ return cmocka_run_group_tests_name("bpt_cow", tests, NULL, NULL);
+}
+
diff --git a/src/libsds/test/test_sds_lqueue.c b/src/libsds/test/test_sds_lqueue.c
new file mode 100644
index 0000000..db91a8d
--- /dev/null
+++ b/src/libsds/test/test_sds_lqueue.c
@@ -0,0 +1,210 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+
+static void
+test_1_lqueue_invalid_create(void **state __attribute__((unused))) {
+ sds_result result = SDS_SUCCESS;
+ result = sds_lqueue_init(NULL, NULL);
+ assert_int_equal(result, SDS_NULL_POINTER);
+}
+
+static void
+test_2_lqueue_enqueue(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ result = sds_lqueue_enqueue(q, (void *)1);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_3_lqueue_enqueue_multiple(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ result = sds_lqueue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ for (uint64_t i = 1; i < 100; i++) {
+ result = sds_lqueue_enqueue(q, (void *)i);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+}
+
+static void
+test_4_lqueue_invalid_dequeue(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ void *ptr = NULL;
+ /* Attempt a dequeue on a list with no elements. */
+ result = sds_lqueue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_LIST_EXHAUSTED);
+ /* Attempt a dequeue on a list with an element, but null ptr */
+ result = sds_lqueue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_lqueue_dequeue(q, NULL);
+ assert_int_equal(result, SDS_NULL_POINTER);
+}
+
+static void
+test_5_lqueue_dequeue(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ void *ptr = NULL;
+ result = sds_lqueue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Attempt a dequeue on a list with 1 element. */
+ result = sds_lqueue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_equal(ptr, NULL);
+ /* Attempt a dequeue on a list with no elements. */
+ result = sds_lqueue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_LIST_EXHAUSTED);
+}
+
+static void
+test_6_lqueue_dequeue_multiple(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ uint64_t *ptr = NULL;
+
+ for (uint64_t i = 0; i < 100; i++) {
+ ptr = sds_malloc(sizeof(uint64_t));
+ *ptr = i;
+ result = sds_lqueue_enqueue(q, (void *)ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ for (uint64_t i = 0; i < 99; i++) {
+ result = sds_lqueue_dequeue(q, (void **)&ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(*ptr, i);
+ sds_free(ptr);
+ }
+ /* Attempt a dequeue on a list with 1 element. */
+ result = sds_lqueue_dequeue(q, (void **)&ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(*ptr, 99);
+ sds_free(ptr);
+}
+
+static void
+test_7_lqueue_random(void **state) {
+ sds_lqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ uint64_t ptr = 0;
+
+ for (size_t i = 0; i < 500; i++) {
+ if (fill_pattern[i] % 2 != 0) {
+ result = sds_lqueue_dequeue(q, (void **)&ptr);
+ if (result != SDS_SUCCESS && result != SDS_LIST_EXHAUSTED) {
+ assert_null(1);
+ }
+ } else {
+ result = sds_lqueue_enqueue(q, (void *)fill_pattern[i]);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ }
+}
+
+static void
+test_8_lqueue_implicit_free(void **state __attribute__((unused))) {
+ sds_result result = SDS_SUCCESS;
+ sds_lqueue *q = NULL;
+ result = sds_lqueue_init(&q, sds_free);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ /* Get the queue ready on this thread. */
+ assert_int_equal(sds_lqueue_tprep(q), SDS_SUCCESS);
+
+ void *ptr = sds_malloc(8);
+ result = sds_lqueue_enqueue(q, ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* We destroy the queue after enqueue, and it will trigger the free */
+ result = sds_lqueue_destroy(q);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_9_lqueue_thread(void *arg) {
+ void **state = (void **)arg;
+ test_7_lqueue_random(state);
+}
+
+static void
+test_9_lqueue_parallel_stress(void **state) {
+ PRThread *t[8] = {0};
+
+ /* Just launch the threads */
+ for (size_t i = 0; i < 8; i++) {
+ t[i] = PR_CreateThread(PR_USER_THREAD, test_9_lqueue_thread, (void *)state,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
0);
+ assert_ptr_not_equal(t[i], NULL);
+ }
+
+ /* Now wait .... */
+ for (size_t i = 0; i < 8; i++) {
+ assert_int_equal(PR_JoinThread(t[i]), PR_SUCCESS);
+ }
+
+}
+
+int
+run_lqueue_tests (void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_1_lqueue_invalid_create),
+ cmocka_unit_test_setup_teardown(test_2_lqueue_enqueue,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_3_lqueue_enqueue_multiple,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_4_lqueue_invalid_dequeue,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_5_lqueue_dequeue,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_6_lqueue_dequeue_multiple,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_7_lqueue_random,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ cmocka_unit_test(test_8_lqueue_implicit_free),
+ cmocka_unit_test_setup_teardown(test_9_lqueue_parallel_stress,
+ lqueue_test_setup,
+ lqueue_test_teardown),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+
diff --git a/src/libsds/test/test_sds_queue.c b/src/libsds/test/test_sds_queue.c
new file mode 100644
index 0000000..45d14d9
--- /dev/null
+++ b/src/libsds/test/test_sds_queue.c
@@ -0,0 +1,171 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+
+static void
+test_1_queue_invalid_create(void **state __attribute__((unused))) {
+ sds_result result = SDS_SUCCESS;
+ result = sds_queue_init(NULL, NULL);
+ assert_int_equal(result, SDS_NULL_POINTER);
+}
+
+static void
+test_2_queue_enqueue(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ result = sds_queue_enqueue(q, (void *)1);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_not_equal(q->head, NULL);
+ assert_ptr_not_equal(q->tail, NULL);
+}
+
+static void
+test_3_queue_enqueue_multiple(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_queue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Take a ref to the head */
+ sds_queue_node *head = q->head;
+
+ for (uint64_t i = 1; i < 100; i++) {
+ result = sds_queue_enqueue(q, (void *)i);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_equal(head, q->head);
+ }
+}
+
+static void
+test_4_queue_invalid_dequeue(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ void *ptr = NULL;
+ /* Attempt a dequeue on a list with no elements. */
+ result = sds_queue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_LIST_EXHAUSTED);
+ /* Attempt a dequeue on a list with an element, but null ptr */
+ result = sds_queue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_queue_dequeue(q, NULL);
+ assert_int_equal(result, SDS_NULL_POINTER);
+}
+
+static void
+test_5_queue_dequeue(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ void *ptr = NULL;
+ result = sds_queue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Attempt a dequeue on a list with 1 element. */
+ result = sds_queue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_equal(ptr, NULL);
+ assert_ptr_equal(q->head, NULL);
+ assert_ptr_equal(q->tail, NULL);
+ /* Attempt a dequeue on a list with no elements. */
+ result = sds_queue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_LIST_EXHAUSTED);
+ assert_ptr_equal(q->head, NULL);
+ assert_ptr_equal(q->tail, NULL);
+}
+
+static void
+test_6_queue_dequeue_multiple(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t *ptr = NULL;
+
+ for (uint64_t i = 0; i < 100; i++) {
+ ptr = sds_malloc(sizeof(uint64_t));
+ *ptr = i;
+ result = sds_queue_enqueue(q, (void *)ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ /* Take a ref to the tail */
+ sds_queue_node *tail = q->tail;
+ for (uint64_t i = 0; i < 99; i++) {
+ result = sds_queue_dequeue(q, (void **)&ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(*ptr, i);
+ sds_free(ptr);
+ assert_ptr_equal(tail, q->tail);
+ }
+ /* Attempt a dequeue on a list with 1 element. */
+ result = sds_queue_dequeue(q, (void **)&ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(*ptr, 99);
+ sds_free(ptr);
+ assert_ptr_equal(q->head, NULL);
+ assert_ptr_equal(q->tail, NULL);
+
+}
+
+static void
+test_7_queue_random(void **state) {
+ sds_queue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t ptr = 0;
+
+ for (size_t i = 0; i < 500; i++) {
+ if (fill_pattern[i] % 2 != 0) {
+ result = sds_queue_dequeue(q, (void **)&ptr);
+ if (result != SDS_SUCCESS && result != SDS_LIST_EXHAUSTED) {
+ assert_null(1);
+ }
+ } else {
+ result = sds_queue_enqueue(q, (void *)fill_pattern[i]);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ }
+}
+
+static void
+test_8_queue_implicit_free(void **state __attribute__((unused))) {
+ sds_result result = SDS_SUCCESS;
+ sds_queue *q = NULL;
+ result = sds_queue_init(&q, sds_free);
+ assert_int_equal(result, SDS_SUCCESS);
+ void *ptr = sds_malloc(8);
+ result = sds_queue_enqueue(q, ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* We destroy the queue after enqueue, and it will trigger the free */
+ result = sds_queue_destroy(q);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+int
+run_queue_tests (void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_1_queue_invalid_create),
+ cmocka_unit_test_setup_teardown(test_2_queue_enqueue,
+ queue_test_setup,
+ queue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_3_queue_enqueue_multiple,
+ queue_test_setup,
+ queue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_4_queue_invalid_dequeue,
+ queue_test_setup,
+ queue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_5_queue_dequeue,
+ queue_test_setup,
+ queue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_6_queue_dequeue_multiple,
+ queue_test_setup,
+ queue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_7_queue_random,
+ queue_test_setup,
+ queue_test_teardown),
+ cmocka_unit_test(test_8_queue_implicit_free),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+
diff --git a/src/libsds/test/test_sds_set.c b/src/libsds/test/test_sds_set.c
new file mode 100644
index 0000000..3b27873
--- /dev/null
+++ b/src/libsds/test/test_sds_set.c
@@ -0,0 +1,372 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+
+/* test_bptree is the test driver for the B+ Tree implemented by bptree/bptree.c */
+
+/* Contains test data. */
+#include "test_sds.h"
+
+static int32_t cb_count = 0;
+
+static void
+test_31_map_cb(void *k, void *v) {
+#ifdef DEBUG
+ printf("mapping %" PRIu64 ":%s\n", (uint64_t)k, (char *)v);
+#endif
+ cb_count++;
+}
+
+static void
+test_31_map(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ cb_count = 0;
+
+ for (uint64_t i = 1; i < 200 ; i++) {
+ /* Make a new string */
+ char *ptr = sds_malloc(sizeof(char) * 4);
+ /* */
+ sprintf(ptr, "%03"PRIu64, i);
+ result = sds_bptree_insert(binst, (void *)&i, ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ sds_bptree_map(binst, test_31_map_cb);
+ assert_int_equal(cb_count, 199);
+}
+
+/* This helper function allows us to create an array of pointers
+ * from the stack arrays
+ */
+static void
+_build_ptr_array(uint64_t **dest, const uint64_t *src, size_t capacity) {
+ for (size_t i = 0; i < capacity; i++) {
+ dest[i] = sds_uint64_t_dup((void *)&(src[i]));
+ }
+}
+
+static void
+test_32_build_simple_tree(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ size_t capacity = 2;
+ uint64_t *load_ptr_array[capacity];
+ _build_ptr_array(load_ptr_array, load_array, capacity);
+
+ result = sds_bptree_load(binst, (void **)load_ptr_array, NULL, capacity);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_33_build_small_tree(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ size_t capacity = 12;
+ uint64_t *load_ptr_array[capacity];
+ _build_ptr_array(load_ptr_array, load_array, capacity);
+
+ result = sds_bptree_load(binst, (void **)load_ptr_array, NULL, capacity);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_34_build_large_tree(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_result result = SDS_SUCCESS;
+
+ size_t capacity = 64;
+ uint64_t *load_ptr_array[capacity];
+ _build_ptr_array(load_ptr_array, load_array, capacity);
+
+ /* Make a simple array */
+ result = sds_bptree_load(binst, (void **)load_ptr_array, NULL, capacity);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+ // sds_bptree_display(binst);
+}
+
+static void
+run_set_test(void **state,
+ uint64_t *set_a, size_t set_a_count,
+ uint64_t *set_b, size_t set_b_count,
+ uint64_t *expect, size_t expect_count,
+ uint64_t *exclude, size_t exclude_count,
+ sds_result (*fn)(sds_bptree_instance *binst_a, sds_bptree_instance *binst_b,
sds_bptree_instance **binst_difference))
+{
+ void **binst = *state;
+ sds_bptree_instance *binst_a = (sds_bptree_instance *) binst[0];
+ sds_bptree_instance *binst_b = (sds_bptree_instance *) binst[1];
+ sds_bptree_instance *binst_out = (sds_bptree_instance *) binst[2];
+ sds_result result = SDS_SUCCESS;
+
+
+ /* Bulk load them */
+ uint64_t *ptr_set_a[set_a_count];
+ _build_ptr_array(ptr_set_a, set_a, set_a_count);
+ result = sds_bptree_load(binst_a, (void **)ptr_set_a, NULL, set_a_count);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ uint64_t *ptr_set_b[set_b_count];
+ _build_ptr_array(ptr_set_b, set_b, set_b_count);
+ result = sds_bptree_load(binst_b, (void **)ptr_set_b, NULL, set_b_count);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ /* Now difference */
+ result = fn(binst_a, binst_b, &binst_out);
+ assert_int_equal(result, SDS_SUCCESS);
+
+ /* For the clean up, we have to give the binst_out pointer back to state. */
+ /* We have to assign these here, else GCC optimises them out */
+ binst[2] = binst_out;
+
+ /* Make sure it's correct */
+ for (size_t i = 0; i < expect_count; i++) {
+ assert_int_equal(SDS_KEY_PRESENT, sds_bptree_search(binst_out, (void
*)&(expect[i])));
+ }
+
+ for (size_t i = 0; i < exclude_count; i++) {
+ assert_int_equal(SDS_KEY_NOT_PRESENT, sds_bptree_search(binst_out, (void
*)&(exclude[i])));
+ }
+
+
+}
+
+static void
+test_35_set_difference_1(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 2, 3, 6, 7, 8, 9};
+ uint64_t set_b[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+ uint64_t expect[] = {4, 5, 7, 8, 9, 10, 11, 12 ,13};
+ uint64_t exclude[] = {1, 3, 6};
+
+ run_set_test(state, set_a, 7, set_b, 9, expect, 9, exclude, 3,
sds_bptree_difference);
+}
+
+static void
+test_35_set_difference_2(void **state) {
+ uint64_t set_a[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+ uint64_t set_b[] = {1, 2, 3, 6, 7, 8, 9};
+ uint64_t expect[] = {4, 5, 7, 8, 9, 10, 11, 12 ,13};
+ uint64_t exclude[] = {1, 3, 6};
+
+ run_set_test(state, set_a, 9, set_b, 7, expect, 9, exclude, 3,
sds_bptree_difference);
+}
+
+static void
+test_35_set_difference_3(void **state) {
+ uint64_t set_a[] = {1, 9};
+ uint64_t set_b[] = {2, 3, 6, 7, 8, 9};
+ uint64_t expect[] = {1,2,3,6,7,8};
+ uint64_t exclude[] = {9};
+
+ run_set_test(state, set_a, 2, set_b, 6, expect, 6, exclude, 1,
sds_bptree_difference);
+}
+
+
+static void
+test_36_set_union_1(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 2, 3, 6, 7, 8, 9, 13};
+ uint64_t set_b[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+
+ uint64_t _union[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+ uint64_t exclude[] = {};
+ run_set_test(state, set_a, 8, set_b, 9, _union, 13, exclude, 0, sds_bptree_union);
+}
+
+static void
+test_36_set_union_2(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+ uint64_t set_b[] = {1, 2, 3, 6, 7, 8, 9, 13};
+
+ uint64_t _union[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+ uint64_t exclude[] = {};
+ run_set_test(state, set_a, 9, set_b, 8, _union, 13, exclude, 0, sds_bptree_union);
+}
+
+static void
+test_36_set_union_3(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {};
+ uint64_t set_b[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+
+ uint64_t _union[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+ uint64_t exclude[] = {};
+ run_set_test(state, set_a, 0, set_b, 9, _union, 9, exclude, 0, sds_bptree_union);
+}
+
+static void
+test_37_set_intersect_1(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 2, 3, 6, 7, 8, 9, 13};
+ uint64_t set_b[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+
+ uint64_t _intersect[] = {1, 3, 6, 13};
+ uint64_t exclude[] = {2, 4, 5, 7, 8, 9, 10, 11, 12};
+
+ run_set_test(state, set_a, 8, set_b, 9, _intersect, 4, exclude, 9,
sds_bptree_intersect);
+}
+
+static void
+test_37_set_intersect_2(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 3, 4, 5, 6, 10, 11, 12, 13};
+ uint64_t set_b[] = {1, 2, 3, 6, 7, 8, 9, 13};
+
+ uint64_t _intersect[] = {1, 3, 6, 13};
+ uint64_t exclude[] = {2, 4, 5, 7, 8, 9, 10, 11, 12};
+
+ run_set_test(state, set_a, 9, set_b, 8, _intersect, 4, exclude, 9,
sds_bptree_intersect);
+}
+
+static void
+test_37_set_intersect_3(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ uint64_t set_b[] = {10, 11, 12, 13, 14, 15};
+
+ uint64_t _intersect[] = {};
+ uint64_t exclude[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+ run_set_test(state, set_a, 9, set_b, 6, _intersect, 0, exclude, 15,
sds_bptree_intersect);
+}
+
+static void
+test_38_set_compliment_1(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 2, 3, 4, 5, 6, 7};
+ uint64_t set_b[] = {1, 2, 3, 4, 5, 8, 9};
+
+ uint64_t _intersect[] = {6, 7};
+ uint64_t exclude[] = {1, 2, 3, 4, 5, 8, 9};
+
+ run_set_test(state, set_a, 7, set_b, 7, _intersect, 2, exclude, 7,
sds_bptree_compliment);
+}
+
+static void
+test_38_set_compliment_2(void **state) {
+ /* Populate the two sets with different values */
+ uint64_t set_a[] = {1, 2, 3, 4, 5, 8, 9};
+ uint64_t set_b[] = {1, 2, 3, 4, 5, 6, 7};
+
+ uint64_t _intersect[] = {8, 9};
+ uint64_t exclude[] = {1, 2, 3, 4, 5, 6, 7};
+
+ run_set_test(state, set_a, 7, set_b, 7, _intersect, 2, exclude, 7,
sds_bptree_compliment);
+}
+
+static int64_t
+test_39_filter_cb(void *k, void *v) {
+#ifdef DEBUG
+ printf("filtering %" PRIu64 ":%s\n", *(uint64_t *)k, (char *)v);
+#endif
+ if (*(uint64_t *)k % 2 == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static void
+test_39_set_filter(void **state) {
+ sds_bptree_instance *binst = *state;
+ sds_bptree_instance *binst_filtered = NULL;
+ sds_result result = SDS_SUCCESS;
+
+ for (uint64_t i = 1; i < 200 ; i++) {
+ /* Make a new string */
+ char *ptr = sds_malloc(sizeof(char) * 4);
+ /* */
+ sprintf(ptr, "%03"PRIu64, i);
+ result = sds_bptree_insert(binst, (void *)&i, ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_bptree_verify(binst);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ sds_bptree_filter(binst, test_39_filter_cb, &binst_filtered);
+
+ for (uint64_t i = 1; i < 200 ; i++) {
+ result = sds_bptree_search(binst_filtered, (void *)&i);
+ if (i % 2 == 0) {
+ assert_int_equal(result, SDS_KEY_PRESENT);
+ } else {
+ assert_int_equal(result, SDS_KEY_NOT_PRESENT);
+ }
+ }
+ result = sds_bptree_destroy(binst_filtered);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+int
+run_set_tests(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_31_map,
+ bptree_str_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_32_build_simple_tree,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_33_build_small_tree,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_34_build_large_tree,
+ bptree_test_setup,
+ bptree_test_teardown),
+ cmocka_unit_test_setup_teardown(test_35_set_difference_1,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_35_set_difference_2,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_35_set_difference_3,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_36_set_union_1,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_36_set_union_2,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_36_set_union_3,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_37_set_intersect_1,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_37_set_intersect_2,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_37_set_intersect_3,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_38_set_compliment_1,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_38_set_compliment_2,
+ bptree_test_set_setup,
+ bptree_test_set_teardown),
+ cmocka_unit_test_setup_teardown(test_39_set_filter,
+ bptree_str_test_setup,
+ bptree_test_teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+
+
diff --git a/src/libsds/test/test_sds_tqueue.c b/src/libsds/test/test_sds_tqueue.c
new file mode 100644
index 0000000..c150c57
--- /dev/null
+++ b/src/libsds/test/test_sds_tqueue.c
@@ -0,0 +1,197 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (c) 2016, William Brown <william at blackhats dot net dot au>
+ * All rights reserved.
+ *
+ * License: License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "test_sds.h"
+
+static void
+test_1_tqueue_invalid_create(void **state __attribute__((unused))) {
+ sds_result result = SDS_SUCCESS;
+ result = sds_tqueue_init(NULL, NULL);
+ assert_int_equal(result, SDS_NULL_POINTER);
+}
+
+static void
+test_2_tqueue_enqueue(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ result = sds_tqueue_enqueue(q, (void *)1);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_not_equal(q->uq->head, NULL);
+ assert_ptr_not_equal(q->uq->tail, NULL);
+}
+
+static void
+test_3_tqueue_enqueue_multiple(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+
+ result = sds_tqueue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Take a ref to the head */
+ sds_queue_node *head = q->uq->head;
+
+ for (uint64_t i = 1; i < 100; i++) {
+ result = sds_tqueue_enqueue(q, (void *)i);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_equal(head, q->uq->head);
+ }
+}
+
+static void
+test_4_tqueue_invalid_dequeue(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ void *ptr = NULL;
+ /* Attempt a dequeue on a list with no elements. */
+ result = sds_tqueue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_LIST_EXHAUSTED);
+ /* Attempt a dequeue on a list with an element, but null ptr */
+ result = sds_tqueue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ result = sds_tqueue_dequeue(q, NULL);
+ assert_int_equal(result, SDS_NULL_POINTER);
+}
+
+static void
+test_5_tqueue_dequeue(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ void *ptr = NULL;
+ result = sds_tqueue_enqueue(q, (void *)NULL);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* Attempt a dequeue on a list with 1 element. */
+ result = sds_tqueue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_ptr_equal(ptr, NULL);
+ assert_ptr_equal(q->uq->head, NULL);
+ assert_ptr_equal(q->uq->tail, NULL);
+ /* Attempt a dequeue on a list with no elements. */
+ result = sds_tqueue_dequeue(q, &ptr);
+ assert_int_equal(result, SDS_LIST_EXHAUSTED);
+ assert_ptr_equal(q->uq->head, NULL);
+ assert_ptr_equal(q->uq->tail, NULL);
+}
+
+static void
+test_6_tqueue_dequeue_multiple(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t *ptr = NULL;
+
+ for (uint64_t i = 0; i < 100; i++) {
+ ptr = sds_malloc(sizeof(uint64_t));
+ *ptr = i;
+ result = sds_tqueue_enqueue(q, (void *)ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ /* Take a ref to the tail */
+ sds_queue_node *tail = q->uq->tail;
+ for (uint64_t i = 0; i < 99; i++) {
+ result = sds_tqueue_dequeue(q, (void **)&ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(*ptr, i);
+ sds_free(ptr);
+ assert_ptr_equal(tail, q->uq->tail);
+ }
+ /* Attempt a dequeue on a list with 1 element. */
+ result = sds_tqueue_dequeue(q, (void **)&ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ assert_int_equal(*ptr, 99);
+ sds_free(ptr);
+ assert_ptr_equal(q->uq->head, NULL);
+ assert_ptr_equal(q->uq->tail, NULL);
+
+}
+
+static void
+test_7_tqueue_random(void **state) {
+ sds_tqueue *q = *state;
+ sds_result result = SDS_SUCCESS;
+ uint64_t ptr = 0;
+
+ for (size_t i = 0; i < 500; i++) {
+ if (fill_pattern[i] % 2 != 0) {
+ result = sds_tqueue_dequeue(q, (void **)&ptr);
+ if (result != SDS_SUCCESS && result != SDS_LIST_EXHAUSTED) {
+ assert_null(1);
+ }
+ } else {
+ result = sds_tqueue_enqueue(q, (void *)fill_pattern[i]);
+ assert_int_equal(result, SDS_SUCCESS);
+ }
+ }
+}
+
+static void
+test_8_tqueue_implicit_free(void **state __attribute__((unused))) {
+ sds_result result = SDS_SUCCESS;
+ sds_tqueue *q = NULL;
+ result = sds_tqueue_init(&q, sds_free);
+ assert_int_equal(result, SDS_SUCCESS);
+ void *ptr = sds_malloc(8);
+ result = sds_tqueue_enqueue(q, ptr);
+ assert_int_equal(result, SDS_SUCCESS);
+ /* We destroy the queue after enqueue, and it will trigger the free */
+ result = sds_tqueue_destroy(q);
+ assert_int_equal(result, SDS_SUCCESS);
+}
+
+static void
+test_9_tqueue_thread(void *arg) {
+ test_7_tqueue_random((void **)arg);
+}
+
+static void
+test_9_tqueue_parallel_stress(void **state) {
+ PRThread *t[8] = {0};
+
+ /* Just launch the threads */
+ for (size_t i = 0; i < 8; i++) {
+ t[i] = PR_CreateThread(PR_USER_THREAD, test_9_tqueue_thread, (void *)state,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
0);
+ assert_ptr_not_equal(t[i], NULL);
+ }
+
+ /* Now wait .... */
+ for (size_t i = 0; i < 8; i++) {
+ assert_int_equal(PR_JoinThread(t[i]), PR_SUCCESS);
+ }
+
+}
+
+int
+run_tqueue_tests (void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_1_tqueue_invalid_create),
+ cmocka_unit_test_setup_teardown(test_2_tqueue_enqueue,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_3_tqueue_enqueue_multiple,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_4_tqueue_invalid_dequeue,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_5_tqueue_dequeue,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_6_tqueue_dequeue_multiple,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ cmocka_unit_test_setup_teardown(test_7_tqueue_random,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ cmocka_unit_test(test_8_tqueue_implicit_free),
+ cmocka_unit_test_setup_teardown(test_9_tqueue_parallel_stress,
+ tqueue_test_setup,
+ tqueue_test_teardown),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+
diff --git a/src/nunc-stans/README.md b/src/nunc-stans/README.md
new file mode 100644
index 0000000..ba37d46
--- /dev/null
+++ b/src/nunc-stans/README.md
@@ -0,0 +1,11 @@
+# nunc-stans
+
+Nunc Stans is an event framework wrapper that provides a thread pool for event callback
execution. It provides thread safety to event frameworks by isolating and protecting the
thread safe parts from the non-thread safe parts, and allows multi-threaded applications
to use event frameworks that are not thread safe.
+
+It has been primarily developed using
http://libevent.org .
+
+# Documentation
+
+Nunc Stans uses doxygen to generate docs from the source. The generated docs are here:
https://firstyear.fedorapeople.org/nunc-stans/index.html .
+
+
diff --git a/src/nunc-stans/include/nunc-stans.h b/src/nunc-stans/include/nunc-stans.h
new file mode 100644
index 0000000..bda6350
--- /dev/null
+++ b/src/nunc-stans/include/nunc-stans.h
@@ -0,0 +1,883 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+/*! \file nunc-stans.h
+ \brief Nunc Stans public API
+
+ This is the public API for Nunc Stans
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef NS_THRPOOL_H
+/** \cond */
+#define NS_THRPOOL_H
+/** \endcond */
+
+#include "nspr.h"
+
+/**
+ * Forward declaration of the thread pool struct
+ *
+ * The actual struct is opaque to applications. The forward declaration is here
+ * for the typedef.
+ */
+struct ns_thrpool_t;
+/**
+ * This is the thread pool typedef
+ *
+ * The actual thread pool is opaque to applications.
+ * \sa ns_thrpool_new, ns_thrpool_wait, ns_thrpool_destroy, ns_job_get_tp
+ */
+typedef struct ns_thrpool_t ns_thrpool_t;
+
+/** \struct ns_job_t
+ * The nunc stans event and worker job object.
+ *
+ * When a new job is created, a pointer to a struct of this object type
+ * is returned. This is the object that will be the argument to the job
+ * callback specified. Since the struct is opaque, the functions ns_job_get_data(),
ns_job_get_tp(),
+ * ns_job_get_type(), ns_job_get_fd(), and ns_job_get_output_type() can be used to get
+ * information about the job. The function ns_job_done() must be used
+ * when the job should be freed.
+ * \sa ns_job_get_data, ns_job_get_fd, ns_job_get_tp, ns_job_get_type,
ns_job_get_output_type
+ */
+struct ns_job_t;
+
+/**
+ * The job callback function type
+ *
+ * Job callback functions must have a function signature of ns_job_func_t.
+ * \sa ns_add_io_job, ns_add_io_timeout_job, ns_add_timeout_job, ns_add_signal_job,
ns_add_job
+ */
+typedef void (*ns_job_func_t)(struct ns_job_t *);
+
+/**
+ * Flag for jobs that are not associated with an event.
+ *
+ * Use this flag when creating a job that you want to be run but
+ * not associated with an event. Usually used in conjunction with
+ * ns_add_job() and #NS_JOB_THREAD to execute a function using the
+ * thread pool.
+ * \sa ns_add_job, NS_JOB_THREAD
+ */
+#define NS_JOB_NONE 0x0
+/**
+ * Flag for accept() jobs - new connection listeners
+ *
+ * Use this flag when creating jobs that listen for and accept
+ * new connections. This is typically used in conjunction with
+ * the #NS_JOB_PERSIST flag so that the job does not have to be
+ * rearmed every time it is called.
+ *
+ * \code
+ * struct ns_job_t *listenerjob;
+ * PRFileDesc *listenfd = PR_OpenTCPSocket(...);
+ * PR_Bind(listenfd, ...);
+ * PR_Listen(listenfd, ...);
+ * listenerctx = new_listenerctx(...); // the application context object
+ * ns_add_io_job(threadpool, NS_JOB_ACCEPT|NS_JOB_PERSIST,
+ * accept_new_connection, listenerctx, &listenerjob);
+ * \endcode
+ * You will probably want to keep track of listenerjob and use it with
+ * ns_job_done() at application shutdown time to avoid leaking resources.
+ * \sa ns_add_io_job, ns_add_io_timeout_job, NS_JOB_IS_ACCEPT, ns_job_get_type,
ns_job_get_output_type
+ */
+#define NS_JOB_ACCEPT 0x1
+/**
+ * Flag for jobs that will use connect()
+ *
+ * When creating an I/O job, set this flag in the job_type to be notified
+ * when the file descriptor is available for outgoing connections. In the
+ * job callback, use ns_job_get_output_type() and #NS_JOB_IS_CONNECT to
+ * see if the callback was called due to connect available if the callback is
+ * used with more than one of the job flags.
+ * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type,
NS_JOB_IS_CONNECT
+ */
+#define NS_JOB_CONNECT 0x2
+/**
+ * Flag for I/O read jobs
+ *
+ * When creating an I/O job, set this flag in the job_type to be notified
+ * when the file descriptor is available for reading. In the
+ * job callback, use ns_job_get_output_type() and #NS_JOB_IS_READ to
+ * see if the callback was called due to read available if the callback is
+ * used with more than one of the job flags.
+ * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type,
NS_JOB_IS_READ
+ */
+#define NS_JOB_READ 0x4
+/**
+ * Flag for I/O write jobs
+ *
+ * When creating an I/O job, set this flag in the job_type to be notified
+ * when the file descriptor is available for writing. In the
+ * job callback, use ns_job_get_output_type() and #NS_JOB_IS_WRITE to
+ * see if the callback was called due to write available if the callback is
+ * used with more than one of the job flags.
+ * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type,
NS_JOB_IS_WRITE
+ */
+#define NS_JOB_WRITE 0x8
+/**
+ * Flag for timer jobs
+ *
+ * When creating a timeout or I/O timeout job, set this flag in the job_type
+ * to be notified when the time given by the timeval argument has elapsed.
+ * In the job callback, use ns_job_get_output_type() and #NS_JOB_IS_TIMER to
+ * see if the callback was called due to elapsed time if the callback is
+ * used with more than one of the job flags.
+ * \sa ns_add_timeout_job, ns_add_io_timeout_job, ns_job_get_type,
ns_job_get_output_type, NS_JOB_IS_TIMER
+ */
+#define NS_JOB_TIMER 0x10
+/**
+ * Flag for signal jobs
+ *
+ * When creating a signal job, set this flag in the job_type
+ * to be notified when the process receives the given signal.
+ * In the job callback, use ns_job_get_output_type() and #NS_JOB_IS_SIGNAL to
+ * see if the callback was called due to receiving the signal if the callback is
+ * used with more than one of the job flags.
+ * \sa ns_add_signal_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_SIGNAL
+ */
+#define NS_JOB_SIGNAL 0x20
+/**
+ * Flag to make jobs persistent
+ *
+ * By default, when an event (I/O, timer, signal) is triggered and
+ * the job callback is called, the event is removed from the event framework,
+ * and the application will no longer receive callbacks for events. The
+ * application is then responsible for calling ns_job_rearm to "re-arm"
+ * the job to respond to the event again. Adding the job with the
+ * #NS_JOB_PERSIST flag added to the job_type means the job will not have
+ * to be rearmed. This is usually used in conjunction with #NS_JOB_ACCEPT
+ * for accept jobs. Use ns_job_get_type() or ns_job_get_output_type() with
+ * #NS_JOB_IS_PERSIST to test if the job is persistent.
+ *
+ * \note Be very careful when using this flag in conjunction with #NS_JOB_THREAD.
+ * For example, for a #NS_JOB_ACCEPT job, once you call accept(), the socket
+ * may be immediately available for another accept, and your callback could be
+ * called again immediately in another thread. Same for the other types of I/O
+ * jobs. In that case, your job callback function must be thread safe - global
+ * resources must be protected with a mutex, the function must be reentrant, etc.
+ *
+ * \sa NS_JOB_ACCEPT, NS_JOB_THREAD, NS_JOB_IS_PERSIST, ns_job_get_type,
ns_job_get_output_type
+ */
+#define NS_JOB_PERSIST 0x40
+/**
+ * Flag to make jobs run in a thread pool thread
+ *
+ * This flag allows you to specify if you want a job to run threaded or not.
+ * If the job is threaded, the job callback is executed by a thread in
+ * the thread pool, and the job callback function must be thread safe and
+ * reentrant. If the job is not threaded, the job runs in the same thread
+ * as the event loop thread.
+ *
+ * \note When #NS_JOB_THREAD is \e not used, the job callback will be run in the
+ * event loop thread, and will block all events from being processed.
+ * Care must be taken to ensure that the job callback does not block.
+ *
+ * Use ns_job_get_type() or ns_job_get_output_type() with #NS_JOB_IS_THREAD to
+ * test if the job is threaded.
+ *
+ * \sa NS_JOB_IS_THREAD, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_THREAD 0x80
+/**
+ * Flag to tell ns_job_done() not to close the job fd
+ *
+ * I/O jobs will have a file descriptor (fd). If the job->fd lifecycle
+ * is managed by the application, this flag tells ns_job_done() not to close
+ * the fd.
+ * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type,
NS_JOB_IS_PRESERVE_FD
+ */
+#define NS_JOB_PRESERVE_FD 0x100
+/**
+ * Bitflag type for job types
+ *
+ * This is the job_type bitfield argument used when adding jobs, and the return
+ * value of the functions ns_job_get_type() and ns_job_get_output_type(). The
+ * value is one or more of the NS_JOB_* macros OR'd together.
+ * \code
+ * ns_job_type_t job_type = NS_JOB_READ|NS_JOB_SIGNAL;
+ * \endcode
+ * When used with ns_job_get_type() or ns_job_get_output_type() to see what type
+ * of job it is, use the return value with one of the NS_JOB_IS_* macros:
+ * \code
+ * if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+ * // handle timeout
+ * }
+ * \endcode
+ * \sa ns_add_io_job, ns_add_io_timeout_job, ns_add_job, ns_add_signal_job,
ns_add_timeout_job, ns_job_get_type, ns_job_get_output_type
+ */
+typedef unsigned short ns_job_type_t;
+
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_ACCEPT
+ * \sa NS_JOB_ACCEPT, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_ACCEPT(eee) ((eee)&NS_JOB_ACCEPT)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_READ
+ * \sa NS_JOB_READ, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_READ(eee) ((eee)&NS_JOB_READ)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_CONNECT
+ * \sa NS_JOB_CONNECT, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_CONNECT(eee) ((eee)&NS_JOB_CONNECT)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_WRITE
+ * \sa NS_JOB_WRITE, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_WRITE(eee) ((eee)&NS_JOB_WRITE)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_TIMER
+ * \sa NS_JOB_TIMER, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_TIMER(eee) ((eee)&NS_JOB_TIMER)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_SIGNAL
+ * \sa NS_JOB_SIGNAL, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_SIGNAL(eee) ((eee)&NS_JOB_SIGNAL)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_PERSIST
+ * \sa NS_JOB_PERSIST, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_PERSIST(eee) ((eee)&NS_JOB_PERSIST)
+/**
+ * Used to test an #ns_job_type_t value to see if it is any sort of I/O job
+ * \sa NS_JOB_IS_ACCEPT, NS_JOB_IS_READ, NS_JOB_IS_CONNECT, NS_JOB_IS_WRITE,
ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_IO(eee)
(NS_JOB_IS_ACCEPT(eee)||NS_JOB_IS_READ(eee)||NS_JOB_IS_CONNECT(eee)||NS_JOB_IS_WRITE(eee))
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_THREAD
+ * \sa NS_JOB_THREAD, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_THREAD(eee) ((eee)&NS_JOB_THREAD)
+/**
+ * Used to test an #ns_job_type_t value for #NS_JOB_PRESERVE_FD
+ * \sa NS_JOB_PRESERVE_FD, ns_job_get_type, ns_job_get_output_type
+ */
+#define NS_JOB_IS_PRESERVE_FD(eee) ((eee)&NS_JOB_PRESERVE_FD)
+
+/**
+ * Used to set an #ns_job_type_t value to have #NS_JOB_READ
+ */
+#define NS_JOB_SET_READ(eee) ((eee) |= NS_JOB_READ)
+/**
+ * Used to set an #ns_job_type_t value to have #NS_JOB_WRITE
+ */
+#define NS_JOB_SET_WRITE(eee) ((eee) |= NS_JOB_WRITE)
+/**
+ * Used to set an #ns_job_type_t value to have #NS_JOB_THREAD
+ */
+#define NS_JOB_SET_THREAD(eee) ((eee) |= NS_JOB_THREAD)
+/**
+ * Remove #NS_JOB_READ from an #ns_job_type_t value
+ */
+#define NS_JOB_UNSET_READ(eee) ((eee) &= ~NS_JOB_READ)
+/**
+ * Remove #NS_JOB_WRITE from an #ns_job_type_t value
+ */
+#define NS_JOB_UNSET_WRITE(eee) ((eee) &= ~NS_JOB_WRITE)
+/**
+ * Remove #NS_JOB_THREAD from an #ns_job_type_t value
+ */
+#define NS_JOB_UNSET_THREAD(eee) ((eee) &= ~NS_JOB_THREAD)
+
+/**
+ * Used to configure the thread pool
+ *
+ * This is the argument to ns_thrpool_new(). This is used to set all of the
+ * configuration parameters for the thread pool.
+ *
+ * This must be initialized using ns_thrpool_config_init(). This will
+ * initialize the fields to their default values. Use like this:
+ * \code
+ * struct ns_thrpool_config nsconfig;
+ * ns_thrpool_config_init(&nsconfig);
+ * nsconfig.max_threads = 16;
+ * nsconfig.malloc_fct = mymalloc;
+ * ...
+ * rc = ns_thrpool_new(&nsconfig);
+ * \endcode
+ * \sa ns_thrpool_config_init, ns_thrpool_new
+ */
+struct ns_thrpool_config {
+ /** \cond */
+ int32_t init_flag;
+ /** \endcond */
+ size_t max_threads; /**< Do not grow the thread pool greater than this size */
+ size_t stacksize; /**< Thread stack size */
+
+ /* pluggable logging functions */
+ void (*log_fct)(int, const char *, va_list); /**< Provide a function that works
like vsyslog */
+ void (*log_start_fct)( void ); /**< Function to call to initialize the logging
system */
+ void (*log_close_fct)( void ); /**< Function to call to shutdown the logging
system */
+
+ /* pluggable memory functions */
+ void* (*malloc_fct)(size_t); /**< malloc() replacement */
+ void* (*memalign_fct)(size_t size, size_t alignment); /**< posix_memalign()
replacement. Note the argument order! */
+ void* (*calloc_fct)(size_t, size_t); /**< calloc() replacement */
+ void* (*realloc_fct)(void *, size_t); /**< realloc() replacement */
+ void (*free_fct)(void *); /**< free() replacement */
+};
+
+/**
+ * Initialize a thrpool config struct
+ *
+ * The config struct must be allocated/freed by the caller. A stack
+ * variable is typically used.
+ * \code
+ * struct ns_thrpool_config nsconfig;
+ * ns_thrpool_config_init(&nsconfig);
+ * nsconfig.max_threads = 16;
+ * nsconfig.malloc_fct = mymalloc;
+ * ...
+ * rc = ns_thrpool_new(&nsconfig);
+ * \endcode
+ * \sa ns_thrpool_config, ns_thrpool_new
+ * \param tp_config - thread pool config struct
+ */
+void ns_thrpool_config_init(struct ns_thrpool_config *tp_config);
+
+/**
+ * The application is finished with this job
+ *
+ * The application uses this function to tell nunc-stans that it is finished
+ * using this job. Once the application calls this function, it may no
+ * longer refer to job - it should be considered as an allocated pointer
+ * that the free() function has been called with. An application will
+ * typically call ns_job_done() at the end of a job callback function for
+ * non-persistent jobs (not using #NS_JOB_PERSIST), or at application
+ * shutdown time for persistent jobs (using #NS_JOB_PERSIST). For an I/O job,
+ * ns_job_done will close() the file descriptor associated with the job unless
+ * the #NS_JOB_PRESERVE_FD is specified when the job is added.
+ *
+ * Note that a persistant job can only disarm/ns_job_done() itself, unless the
+ * threadpool is in shutdown, then external threads may request the job to be marked
+ * as done(). This is to protect from a set of known race conditions that may occur.
+ *
+ * All jobs *must* have ns_job_done() called upon them to clean them correctly. Failure
+ * to do so may cause resource leaks.
+ *
+ * \code
+ * void read_callback(struct ns_job_t *job)
+ * {
+ * appctx_t *appctx = (appctx_t *)ns_job_get_data(job);
+ * ...
+ * ns_job_done(job);
+ * // ok to use appctx here, but not job
+ * // app must free or ensure appctx is not leaked
+ * return;
+ * }
+ * \endcode
+ * \param job the job to clean up
+ * \retval PR_SUCCESS Job was successfully queued for removal.
+ * \retval PR_FAILURE Failed to mark job for removal. Likely the job is ARMED!
+ * We cannot remove jobs that are armed due to the race conditions it can cause.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_PERSIST, NS_JOB_PRESERVE_FD
+ */
+PRStatus ns_job_done(struct ns_job_t *job);
+
+/**
+ * Create a new job which is not yet armed.
+ *
+ * Specify the type of job using the job_type bitfield. You can specify
+ * more than one type of job.
+ *
+ * The callback will need to rearm the job or add another job if it wants to
+ * be notified of more events.
+ *
+ * This job is not armed at creation unlike other ns_add_*_job. This means that
+ * after the job is created, you can use ns_job_set_*, and when ready, arm the job.
+ *
+ * \code
+ * struct ns_job_t *job;
+ * ns_create_job(tp, NS_JOB_READ, my_callback, &job);
+ * ns_job_set_data(job, data);
+ * // You may not alter job once it is armed.
+ * ns_job_rearm(job);
+ * \endcode
+ *
+ * \param tp The thread pool you want to add an I/O job to.
+ * \param job_type A set of flags that indicates the job type.
+ * \param func The callback function to call when processing the job.
+ * \param[out] job The address of a job pointer that will be filled in once the job is
allocated.
+ * \retval PR_SUCCESS Job was successfully added.
+ * \retval PR_FAILURE Failed to add job.
+ * \warning The thread pool will not allow a job to be added when it has been signaled
+ * to shutdown. It will return PR_FAILURE in that case.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_READ, NS_JOB_WRITE, NS_JOB_ACCEPT,
NS_JOB_CONNECT, NS_JOB_IS_IO, ns_job_done
+ */
+PRStatus
+ns_create_job(struct ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, struct
ns_job_t **job);
+
+/**
+ * Adds an I/O job to the thread pool
+ *
+ * Specify the type of I/O job using the job_type bitfield. You can specify
+ * more than one type of I/O job. Use ns_job_get_output_type(job) to
+ * determine which event triggered the I/O.
+ * \code
+ * ns_add_io_job(tp, fd, NS_JOB_READ|NS_JOB_WRITE, my_io_callback, ...);
+ * ...
+ * void my_io_callback(struct ns_job_t *job)
+ * {
+ * if (NS_JOB_IS_READ(ns_job_get_output_type(job))) {
+ * // handle reading from fd
+ * } else {
+ * // handle writing to fd
+ * }
+ * }
+ * \endcode
+ * The callback will need to rearm the job or add another job if it wants to
+ * be notified of more events, or use #NS_JOB_PERSIST. If you want an I/O job
+ * that will timeout if I/O is not detected within a certain period of time,
+ * use ns_add_io_timeout_job().
+ * \param tp The thread pool you want to add an I/O job to.
+ * \param fd The file descriptor to use for I/O.
+ * \param job_type A set of flags that indicates the job type.
+ * \param func The callback function to call when processing the job.
+ * \param data Arbitrary data that will be available to the job callback function.
+ * \param[out] job The address of a job pointer that will be filled in once the job is
allocated.
+ * \c NULL can be passed if a pointer to the job is not needed.
+ * \retval PR_SUCCESS Job was successfully added.
+ * \retval PR_FAILURE Failed to add job.
+ * \warning The thread pool will not allow a job to be added when it has been signaled
+ * to shutdown. It will return PR_FAILURE in that case.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_READ, NS_JOB_WRITE, NS_JOB_ACCEPT,
NS_JOB_CONNECT, NS_JOB_IS_IO, ns_job_done
+ */
+PRStatus ns_add_io_job(struct ns_thrpool_t *tp,
+ PRFileDesc *fd,
+ ns_job_type_t job_type,
+ ns_job_func_t func,
+ void *data,
+ struct ns_job_t **job);
+
+/**
+ * Adds a timeout job to the thread pool
+ *
+ * The func function will be called when the timer expires.
+ *
+ * \param tp The thread pool you want to add a timeout job to.
+ * \param tv The timer that needs to expire before triggering the callback function.
+ * \param job_type A set of flags that indicates the job type - #NS_JOB_TIMER + other
flags
+ * \param func The callback function to call when processing the job.
+ * \param data Arbitrary data that will be available to the job callback function.
+ * \param[out] job The address of a job pointer that will be filled in once the job is
allocated.
+ * \c NULL can be passed if a pointer to the job is not needed.
+ * \retval PR_SUCCESS Job was successfully added.
+ * \retval PR_FAILURE Failed to add job.
+ * \warning The thread pool will not allow a job to be added when it has been signaled
+ * to shutdown. It will return PR_FAILURE in that case.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_TIMER, NS_JOB_IS_TIMER, ns_job_done
+ */
+PRStatus ns_add_timeout_job(struct ns_thrpool_t *tp,
+ struct timeval *tv,
+ ns_job_type_t job_type,
+ ns_job_func_t func,
+ void *data,
+ struct ns_job_t **job);
+
+/**
+ * Adds an I/O job to the thread pool's work queue with a timeout.
+ *
+ * The callback func function should test the type of event that triggered
+ * the callback using ns_job_get_output_type(job) to get the
+ * #ns_job_type_t, then use #NS_JOB_IS_TIMER(output_type) to see if
+ * this callback was triggered by a timer event. This is useful if
+ * you want to perform some sort of I/O, but you require that I/O
+ * must happen in a certain amount of time.
+ * \code
+ * ns_add_io_timeout_job(tp, fd, &tv, NS_JOB_READ|NS_JOB_TIMER, my_iot_callback,
...);
+ * ...
+ * void my_iot_callback(struct ns_job_t *job)
+ * {
+ * if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+ * // handle timeout condition
+ * } else {
+ * // handle read from fd
+ * }
+ * }
+ * \endcode
+ * \note This is like adding an I/O job, with an optional timeout.
+ * This is not like adding a timeout job with an additional I/O
+ * event component. This depends on the underlying event framework
+ * having the ability to have a timed I/O job. For example, libevent
+ * I/O events can have a timeout.
+ *
+ * \param tp The thread pool whose work queue you want to add an I/O job to.
+ * \param fd The file descriptor to use for I/O.
+ * \param tv The timer that needs to expire before triggering the callback function.
+ * \param job_type A set of flags that indicates the job type.
+ * \param func The callback function for a worker thread to call when processing the
job.
+ * \param data Arbitrary data that will be available to the job callback function.
+ * \param[out] job The address of a job pointer that will be filled in once the job is
allocated.
+ * \c NULL can be passed if a pointer to the job is not needed.
+ * \retval PR_SUCCESS Job was successfully added.
+ * \retval PR_FAILURE Failed to add job.
+ * \warning The thread pool will not allow a job to be added when it has been signaled
+ * to shutdown. It will return PR_FAILURE in that case.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_READ, NS_JOB_WRITE, NS_JOB_ACCEPT,
NS_JOB_CONNECT, NS_JOB_IS_IO, ns_job_done, NS_JOB_TIMER, NS_JOB_IS_TIMER
+ */
+PRStatus ns_add_io_timeout_job(struct ns_thrpool_t *tp,
+ PRFileDesc *fd,
+ struct timeval *tv,
+ ns_job_type_t job_type,
+ ns_job_func_t func,
+ void *data,
+ struct ns_job_t **job);
+
+/**
+ * Adds a signal job to the thread pool
+ *
+ * The \a func function will be called when the signal is received by the process.
+ *
+ * \param tp The thread pool you want to add a signal job to.
+ * \param signum The signal number that you want to trigger the callback function.
+ * \param job_type A set of flags that indicates the job type.
+ * \param func The callback function to call when processing the job.
+ * \param data Arbitrary data that will be available to the job callback function.
+ * \param[out] job The address of a job pointer that will be filled in once the job is
allocated.
+ * \c NULL can be passed if a pointer to the job is not needed.
+ * \retval PR_SUCCESS Job was successfully added.
+ * \retval PR_FAILURE Failed to add job.
+ * \warning The thread pool will not allow a job to be added when it has been signaled
+ * to shutdown. It will return PR_FAILURE in that case.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_SIGNAL, NS_JOB_IS_SIGNAL
+ */
+PRStatus ns_add_signal_job(ns_thrpool_t *tp,
+ PRInt32 signum,
+ ns_job_type_t job_type,
+ ns_job_func_t func,
+ void *data,
+ struct ns_job_t **job);
+
+/**
+ * Add a non-event related job to the thread pool
+ *
+ * A non-event related job is a job that is executed immediately that is not contingent
+ * on an event or signal. This is typically used when the application wants to do some
+ * processing in parallel using a thread from the thread pool.
+ * \code
+ * ns_add_job(tp, NS_JOB_NONE|NS_JOB_THREAD, my_callback, ...);
+ * ...
+ * void my_callback(struct ns_job_t *job)
+ * {
+ * // now in a separate thread
+ * }
+ * \endcode
+ *
+ * \param tp The thread pool you want to add the job to.
+ * \param job_type A set of flags that indicates the job type (usually just
NS_JOB_NONE|NS_JOB_THREAD)
+ * \param func The callback function to call when processing the job.
+ * \param data Arbitrary data that will be available to the job callback function.
+ * \param[out] job The address of a job pointer that will be filled in once the job is
allocated.
+ * \c NULL can be passed if a pointer to the job is not needed.
+ * \retval PR_SUCCESS Job was successfully added.
+ * \retval PR_FAILURE Failed to add job.
+ * \warning The thread pool will not allow a job to be added when it has been signaled
+ * to shutdown. It will return PR_FAILURE in that case.
+ * \sa ns_job_t, ns_job_get_data, NS_JOB_NONE, NS_JOB_THREAD
+ */
+PRStatus ns_add_job(ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, void
*data, struct ns_job_t **job);
+
+/**
+ * Allows the callback to access the file descriptor for an I/O job
+ *
+ * \code
+ * void my_io_job_callback(struct ns_job_t *job)
+ * {
+ * PRFileDesc *fd = ns_job_get_fd(job);
+ * rc = PR_Read(fd, ...);
+ * ...
+ * }
+ * \endcode
+ * If the job is not an I/O job, the function will return NULL.
+ *
+ * \param job The job to get the fd for.
+ * \retval fd The file descriptor associated with the I/O job.
+ * \retval NULL The job is not an I/O job
+ * \sa ns_job_t, ns_add_io_job, ns_add_io_timeout_job
+ */
+PRFileDesc *ns_job_get_fd(struct ns_job_t *job);
+
+/**
+ * Allows the callback to access the private data field in the job.
+ *
+ * This is the \c data field passed in when the job is added. This
+ * data is private to the application - nunc-stans does not touch it
+ * in any way. The application is responsible for managing the lifecycle
+ * of this data.
+ * \code
+ * void my_job_callback(struct ns_job_t *job)
+ * {
+ * myappctx_t *myappctx = (myappctx_t *)ns_job_get_data(job);
+ * ...
+ * }
+ * \endcode
+ *
+ * \param job The job to get the data for.
+ * \return The private data associated with the job.
+ * \sa ns_job_t
+ */
+void *ns_job_get_data(struct ns_job_t *job);
+
+/**
+ * Allows the caller to set private data into the job
+ * Care should be taken to make sure that the previous contents are
+ * freed, or that the data is freed after use. Leaks will be annoying to track
+ * down with this!
+ *
+ * This sets the \c data field. This data is private to the application - nunc-stans
+ * will not touch it in any way.
+ * \code
+ * void my_job_callback(struct ns_job_t *job)
+ * {
+ * myappctx_t *myappctx = malloc(sizeof(struct appctx));
+ * ...
+ * // You must check and *free* the data if required.
+ * // Else you may introduce a memory leak!
+ * void *data = ns_job_get_data(job);
+ * if (data != NULL) {
+ * myapp_use_data(data);
+ * ...
+ * }
+ * free(data);
+ * if (ns_job_set_data(job, (void *)myappctx) == PR_SUCCESS) {
+ * //handle the error, you probably have a bug ....
+ * }
+ * }
+ * \endcode
+ *
+ * \param job The job to set the data for
+ * \param data The void * pointer to the data to set
+ * \return if the set data succeeded
+ * \sa ns_job_t
+ */
+PRStatus ns_job_set_data(struct ns_job_t *job, void *data);
+
+/**
+ * Allows the callback to access the job type flags.
+ *
+ * Usually used in conjunction with one of the NS_JOB_IS_* macros.
+ *
+ * \code
+ * void my_job_callback(struct ns_job_t *job)
+ * {
+ * if (NS_JOB_IS_READ(ns_job_get_type(job)) {
+ * ...
+ * }
+ * }
+ * \endcode
+ * \param job The job to get the type for.
+ * \return The #ns_job_type_t flags for the job
+ * \sa ns_job_t, NS_JOB_IS_READ, NS_JOB_IS_WRITE, NS_JOB_IS_IO, NS_JOB_IS_TIMER
+ */
+ns_job_type_t ns_job_get_type(struct ns_job_t *job);
+
+/**
+ * Allows the callback to access the thread pool that the job is associated with.
+ *
+ * Useful for adding jobs from within job callbacks.
+ *
+ * \code
+ * void my_job_callback(struct ns_job_t *job)
+ * {
+ * // do some work
+ * // need to listen for some events
+ * ns_add_io_job(ns_job_get_tp(job), fd, ...);
+ * // finished with job
+ * ns_job_done(job);
+ * return;
+ * }
+ * \endcode
+ * \param job The job to get the thread pool for.
+ * \return The thread pool associated with the job.
+ * \sa ns_job_t, ns_add_io_job
+ */
+ns_thrpool_t *ns_job_get_tp(struct ns_job_t *job);
+
+/**
+ * Allows the callback to know which event triggered the callback. Can only be called
from within the callback itself.
+ *
+ * The callback func may need to know which event triggered the
+ * callback. This function will allow access to the type of event
+ * that triggered the callback. For example, when using
+ * ns_add_io_timeout_job(), the callback can be called either because of
+ * an I/O event or a timer event. Use #NS_JOB_IS_TIMER to tell if
+ * the event is a timer event, like this:
+ * \code
+ * if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+ * ... handle timeout ...
+ * } else {
+ * ... handle I/O ...
+ * }
+ * \endcode
+ * \param job The job to get the output type for.
+ * \return The #ns_job_type_t corresponding to the event that triggered the callback
+ * \sa ns_job_t, NS_JOB_IS_TIMER, NS_JOB_IS_READ, NS_JOB_IS_WRITE, NS_JOB_IS_IO,
ns_add_io_timeout_job
+ */
+ns_job_type_t ns_job_get_output_type(struct ns_job_t *job);
+
+/**
+ * Allows setting the job done callback.
+ *
+ * The job done callback will be triggered when ns_job_done is called on the
+ * job. This allows jobs to have private data fields cleaned and freed correctly
+ * \code
+ * ns_create_job(tp, NS_JOB_READ, my_callback, &job);
+ * if (ns_job_set_done_cb(job, my_done_callback) != PR_SUCCESS) {
+ * // you must handle this error!!!! the cb did not set!
+ * }
+ * ns_job_rearm(job);
+ * ...
+ * void my_done_callback(struct ns_job_t job) {
+ * free(ns_job_get_data(job));
+ * }
+ * \endcode
+ * \return status if the set succeeded. Always check this!
+ * \param job The job to set the callback for.
+ * \param func The callback function, to be called when ns_job_done is triggered.
+ * \sa ns_job_t, ns_job_done
+ */
+PRStatus ns_job_set_done_cb(struct ns_job_t *job, ns_job_func_t func);
+
+/**
+ * Creates a new thread pool
+ *
+ * Must be called with a struct ns_thrpool_config that has been
+ * initialized by ns_thrpool_config_init(). Typically, once the
+ * thread pool has been created, one or more listener jobs or
+ * other long lived jobs will be added, and the application will
+ * just call ns_thrpool_wait(). The application should add at least
+ * one job that will listen for shutdown events, signals, etc. which
+ * will call ns_thrpool_shutdown(). After ns_thrpool_wait() returns,
+ * the application should use ns_job_done() to finish any long-lived
+ * jobs, then call ns_thrpool_destroy().
+ * \param config A pointer to a struct ns_thrpool_config
+ * \return A pointer to the newly created thread pool.
+ * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_wait, ns_thrpool_shutdown,
ns_thrpool_destroy
+ */
+struct ns_thrpool_t *ns_thrpool_new(struct ns_thrpool_config *config);
+
+/**
+ * Frees a thread pool from memory
+ *
+ * This will free a thread pool and it's internal resources from memory. You should
+ * be sure that the thread pool has been shutdown before destroying it by calling
+ * ns_thrpool_wait(), then call ns_job_done() to finish any long-lived jobs. After
+ * calling ns_thrpool_destroy(), do not use tp.
+ *
+ * \param tp The thread pool to destroy.
+ * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_new, ns_thrpool_wait,
ns_thrpool_shutdown
+ */
+void ns_thrpool_destroy(struct ns_thrpool_t *tp);
+
+/**
+ * Tells a thread pool to shutdown its threads
+ *
+ * The application will usually call ns_thrpool_shutdown() from an event
+ * callback that is listening for shutdown events e.g. a signal job that
+ * is listening for SIGINT or SIGTERM events.
+ * \code
+ * tp = ns_thrpool_new(...);
+ * ns_add_signal_job(tp, SIGTERM, handle_shutdown_signal, NS_JOB_SIGNAL|NS_JOB_THREAD,
...);
+ *
+ * void handle_shutdown_signal(job)
+ * {
+ * ns_thrpool_shutdown(ns_job_get_tp(job));
+ * // set application shutdown flag
+ * return;
+ * }
+ * \endcode
+ * Use NS_JOB_SIGNAL|NS_JOB_THREAD so that the job will run in a worker thread, not
+ * in the event loop thread.
+ * \note The application must call ns_thrpool_shutdown() or ns_thrpool_wait() will
+ * never return.
+ *
+ * \param tp The thread pool to shutdown.
+ * \warning This must only be called from a job that runs in a worker thread. Calling
+ * this function from the event thread can cause a deadlock.
+ * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_new, ns_thrpool_wait,
ns_thrpool_destroy, ns_add_signal_job, NS_JOB_THREAD
+ */
+void ns_thrpool_shutdown(struct ns_thrpool_t *tp);
+
+/**
+ * Checks if a thread pool is shutting down
+ *
+ * This can be called by worker threads so they know when the thread pool has
+ * been requested to shut down.
+ *
+ * \retval 0 if the thread pool is not shutting down.
+ * \retval 1 if the thread pool is shutting down.
+ * \sa ns_thrpool_shutdown
+ */
+PRInt32 ns_thrpool_is_shutdown(struct ns_thrpool_t *tp);
+
+/**
+ * Waits for all threads in the pool to exit
+ *
+ * This call will block the caller until all threads in the thread pool have exited. A
+ * program will typically create the thread pool by calling ns_thrpool_new(), then it
+ * will call ns_thrpool_wait() to wait until the thread pool is shutdown (which is
likely
+ * initiated by a signal handler). Once this function successfully returns, the thread
+ * pool can be safely destroyed by calling ns_thrpool_destroy().
+ * \note The application must call ns_thrpool_shutdown() or ns_thrpool_wait() will
+ * never return.
+ *
+ * \param tp The thread pool to wait for.
+ * \retval PR_SUCCESS The thread pool threads completed successfully
+ * \retval PR_FAILURE Failure waiting for the thread pool threads to terminate
+ * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_new, ns_thrpool_destroy,
ns_thrpool_shutdown
+ */
+PRStatus ns_thrpool_wait(struct ns_thrpool_t *tp);
+
+
+/**
+ * Convenience function to re-arm the same job
+ *
+ * This is used for non-persistent (not using #NS_JOB_PERSIST) jobs.
+ * For example, if you have an I/O reading job, and the job needs to read more data,
+ * the job callback can just call ns_job_rearm(), and the job callback will be
+ * called again when read is ready on the job fd. Once this function is called,
+ * the job callback may be called immediately if the job uses #NS_JOB_THREAD. Do not
+ * refer to job after calling ns_job_rearm().
+ * \note Do not call ns_job_done() with a job if using ns_job_rearm() with the job
+ * \param job The job to re-arm
+ * \sa ns_job_t, ns_job_done, NS_JOB_PERSIST, NS_JOB_THREAD
+ */
+PRStatus ns_job_rearm(struct ns_job_t *job);
+
+#endif /* NS_THRPOOL_H */
diff --git a/src/nunc-stans/ns/ns_event_fw.h b/src/nunc-stans/ns/ns_event_fw.h
new file mode 100644
index 0000000..1c3cee5
--- /dev/null
+++ b/src/nunc-stans/ns/ns_event_fw.h
@@ -0,0 +1,177 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+#ifndef NS_EVENT_FW_H
+#define NS_EVENT_FW_H
+
+#include "ns_private.h"
+
+/* For locks and cond var */
+#include <pthread.h>
+
+struct ns_event_fw_ctx_t;
+struct ns_event_fw_fd_t;
+struct ns_event_fw_time_t;
+struct ns_event_fw_sig_t;
+
+#ifndef NO_EVENT_TYPEDEFS
+typedef struct ns_event_fw_ctx_t ns_event_fw_ctx_t;
+typedef struct ns_event_fw_fd_t ns_event_fw_fd_t;
+typedef struct ns_event_fw_time_t ns_event_fw_time_t;
+typedef struct ns_event_fw_sig_t ns_event_fw_sig_t;
+#endif
+
+/*
+ * The states we can have for a job.
+ * We start in WAITING. Valid transitions are:
+ * // With waiting, *any* thread can change this. Prevents races.
+ * WAITING -> NEEDS_DELETE
+ * WAITING -> NEEDS_ARM
+ * NEEDS_ARM -> ARMED
+ * NEEDS_ARM -> NEEDS_DELETE
+ * ARMED -> RUNNING
+ * When a job is ARMED, and tp->shutdown 1, it CAN move to NEEDS_DELETE
+ * ARMED -> NEEDS_DELETE // Only by shutdown of event loop
+ * // The trick with RUNNING is that only the current thread working thread can change
this!
+ * RUNNING -> NEEDS_DELETE
+ * RUNNING -> NEEDS_ARM
+ * RUNNING -> WAITING
+ * NEEDS_DELETE -> DELETED
+ */
+
+typedef enum _ns_job_state {
+ NS_JOB_NEEDS_DELETE = 2,
+ NS_JOB_DELETED = 3,
+ NS_JOB_NEEDS_ARM = 4,
+ NS_JOB_ARMED = 5,
+ NS_JOB_RUNNING = 6,
+ NS_JOB_WAITING = 7,
+} ns_job_state_t;
+
+/* this is our "kitchen sink" pblock/glue object that is the main
+ interface between the app/thread pool/event framework */
+typedef struct ns_job_t {
+ pthread_mutex_t *monitor;
+ struct ns_thrpool_t *tp;
+ ns_job_func_t func;
+ struct ns_job_data_t *data;
+ ns_job_type_t job_type; /* NS_JOB_ACCEPT etc. */
+ PRFileDesc *fd; /* for I/O events */
+ struct timeval tv; /* used for timed events */
+ int signal; /* if the event was triggered by a signal, this is the signal number */
+ ns_event_fw_fd_t *ns_event_fw_fd; /* event framework fd event object */
+ ns_event_fw_time_t *ns_event_fw_time; /* event framework timer event object */
+ ns_event_fw_sig_t *ns_event_fw_sig; /* event framework signal event object */
+ ns_job_type_t output_job_type; /* info about event that triggered the callback */
+ ns_job_state_t state; /* What state the job is currently in. */
+ ns_event_fw_ctx_t *ns_event_fw_ctx;
+ void *(*alloc_event_context)(size_t size, struct ns_job_t *job);
+ void (*free_event_context)(void *ev_ctx, struct ns_job_t *job);
+ /* this is called by the event framework when an event is triggered */
+ void (*event_cb)(struct ns_job_t *tpec);
+ /* Called during dispose of the job, to allow user defined cleanup */
+ ns_job_func_t done_cb;
+} ns_job_t;
+
+typedef void (*ns_event_fw_accept_cb_t)(PRFileDesc *fd, ns_job_t *job);
+typedef void (*ns_event_fw_read_cb_t)(PRFileDesc *fd, ns_job_t *job);
+typedef void (*ns_event_fw_write_cb_t)(PRFileDesc *fd, ns_job_t *job);
+typedef void (*ns_event_fw_connect_cb_t)(PRFileDesc *fd, ns_job_t *job);
+
+typedef struct ns_event_fw_t {
+ ns_event_fw_ctx_t *(*ns_event_fw_init)(void);
+ /*
+struct event_base *event_base_new(void);
+struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx);
+ */
+ void (*ns_event_fw_destroy)(ns_event_fw_ctx_t *ns_event_fw_ctx);
+ /*
+void event_base_free(struct event_base *);
+int talloc_free(void *ptr);
+ */
+ int (*ns_event_fw_loop)(ns_event_fw_ctx_t *ns_event_fw_ctx); /* run the event fw main
loop */
+ /*
+ the return value - 1 = no events to process; 0 = normal termination; -1 = error
+int event_base_loop(struct event_base *, int);
+int tevent_loop_wait(struct tevent_context *ev)
+ */
+ void (*ns_event_fw_add_io)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ /*
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+int event_add(struct event *ev, const struct timeval *timeout);
+
+#define tevent_fd * tevent_add_fd(ev, mem_ctx, fd, flags, handler, private_data) \
+ */
+ void (*ns_event_fw_mod_io)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ /* write, accept, etc. */
+ void (*ns_event_fw_add_timer)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ /*
+tevent_timer *tevent_add_timer(ev, mem_ctx, next_event, handler, private_data)
+
+evtimer_set(ev, cb, arg)
+evtimer_add(ev, tv)
+ */
+ void (*ns_event_fw_mod_timer)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ void (*ns_event_fw_add_signal)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ /*
+struct tevent_signal *tevent_add_signal(ev, mem_ctx, signum, sa_flags, handler,
private_data)
+
+signal_set(ev, x, cb, arg)
+signal_add(ev, tv)
+ */
+ void (*ns_event_fw_mod_signal)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ void (*ns_event_fw_io_event_done)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ void (*ns_event_fw_timer_event_done)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+ void (*ns_event_fw_signal_event_done)(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job);
+} ns_event_fw_t;
+
+ns_event_fw_t *get_event_framework_event( void );
+ns_event_fw_t *get_event_framework_tevent( void );
+
+#endif /* THRPOOL_NS_EVENT_FW_H */
diff --git a/src/nunc-stans/ns/ns_event_fw_event.c
b/src/nunc-stans/ns/ns_event_fw_event.c
new file mode 100644
index 0000000..58dac28
--- /dev/null
+++ b/src/nunc-stans/ns/ns_event_fw_event.c
@@ -0,0 +1,343 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/*
+ * This implements the thrpool event framework interface
+ * to the libevent event framework
+ */
+#include <private/pprio.h>
+#include <event.h>
+#include <event2/event.h>
+typedef struct event_base ns_event_fw_ctx_t;
+typedef struct event ns_event_fw_fd_t;
+typedef struct event ns_event_fw_time_t;
+typedef struct event ns_event_fw_sig_t;
+#define NO_EVENT_TYPEDEFS
+#include "ns_event_fw.h"
+#include <syslog.h>
+
+
+static void
+event_logger_cb(int severity, const char *msg)
+{
+ int priority = 0;
+ char *text = NULL;
+
+ switch (severity) {
+ case _EVENT_LOG_DEBUG: priority = LOG_DEBUG; break;
+ case _EVENT_LOG_MSG: priority = LOG_DEBUG; break;
+ case _EVENT_LOG_WARN: priority = LOG_DEBUG; break;
+ case _EVENT_LOG_ERR: priority = LOG_ERR; break;
+ default: priority = LOG_ERR; break; /* never reached */
+ }
+ /* need to append newline */
+ text = PR_smprintf("%s\n",msg);
+ ns_log(priority, text);
+ ns_free(text);
+}
+
+static ns_job_type_t
+event_flags_to_type(short events)
+{
+ ns_job_type_t job_type = 0;
+ if (events & EV_READ) {
+ job_type |= NS_JOB_READ;
+ }
+ if (events & EV_WRITE) {
+ job_type |= NS_JOB_WRITE;
+ }
+ if (events & EV_TIMEOUT) {
+ job_type |= NS_JOB_TIMER;
+ }
+ if (events & EV_SIGNAL) {
+ job_type |= NS_JOB_SIGNAL;
+ }
+ return job_type;
+}
+
+/* this is called by the event main loop when an event
+ is triggered - this "maps" the event library interface
+ to our nspr/thrpool interface */
+static void
+event_cb(int fd, short event, void *arg)
+{
+ ns_job_t *job = (ns_job_t *)arg;
+
+ PR_ASSERT(arg);
+ if (job->fd && fd > 0) {
+ PR_ASSERT(fd == PR_FileDesc2NativeHandle(job->fd));
+ }
+
+ job->output_job_type = event_flags_to_type(event);
+
+ job->event_cb(job);
+}
+
+/* convert from job job_type to event fw type */
+static short
+job_type_to_flags(ns_job_type_t job_type)
+{
+ short flags = 0;
+ if (NS_JOB_IS_ACCEPT(job_type) || NS_JOB_IS_READ(job_type)) {
+ flags |= EV_READ;
+ }
+ if (NS_JOB_IS_WRITE(job_type) || NS_JOB_IS_CONNECT(job_type)) {
+ flags |= EV_WRITE;
+ }
+ if (NS_JOB_IS_PERSIST(job_type)) {
+ flags |= EV_PERSIST;
+ }
+ if (NS_JOB_IS_TIMER(job_type)) {
+ flags |= EV_TIMEOUT;
+ }
+ if (NS_JOB_IS_SIGNAL(job_type)) {
+ flags |= EV_SIGNAL;
+ }
+ return flags;
+}
+
+static ns_event_fw_ctx_t *
+ns_event_fw_init(void)
+{
+ event_set_log_callback(event_logger_cb);
+#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+ event_set_mem_functions(ns_malloc, ns_realloc, ns_free);
+#endif
+ return event_base_new();
+}
+
+static void
+ns_event_fw_destroy(ns_event_fw_ctx_t *ns_event_fw_ctx_t)
+{
+ event_base_free(ns_event_fw_ctx_t);
+}
+
+/*
+ * remove just removes the event from active consideration
+ * by the event framework - the event object may be
+ * reactivated by calling mod - event done will actually
+ * remove and free the object
+ */
+static void
+ns_event_fw_io_event_remove(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ event_del(job->ns_event_fw_fd);
+}
+
+static void
+ns_event_fw_io_event_done(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ ns_event_fw_io_event_remove(ns_event_fw_ctx, job);
+ job->free_event_context(job->ns_event_fw_fd, job);
+ job->ns_event_fw_fd = NULL;
+}
+
+static void
+ns_event_fw_timer_event_remove(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ evtimer_del(job->ns_event_fw_time);
+}
+
+static void
+ns_event_fw_timer_event_done(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ ns_event_fw_timer_event_remove(ns_event_fw_ctx, job);
+ job->free_event_context(job->ns_event_fw_time, job);
+ job->ns_event_fw_time = NULL;
+}
+
+static void
+ns_event_fw_signal_event_remove(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ evsignal_del(job->ns_event_fw_sig);
+}
+
+static void
+ns_event_fw_signal_event_done(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ ns_event_fw_signal_event_remove(ns_event_fw_ctx, job);
+ job->free_event_context(job->ns_event_fw_sig, job);
+ job->ns_event_fw_sig = NULL;
+}
+
+static void
+ns_event_fw_add_io(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ struct timeval *tv = NULL;
+ /* allocate a new event structure - use the job for the memory context */
+ struct event *ev = job->alloc_event_context(sizeof(struct event), job);
+ /* set the fields in the event */
+ short flags = job_type_to_flags(job->job_type);
+
+ event_assign(ev, ns_event_fw_ctx, PR_FileDesc2NativeHandle(job->fd), flags,
event_cb, job);
+ if (job->tv.tv_sec || job->tv.tv_usec) {
+ tv = &job->tv;
+ }
+ event_add(ev, tv);
+ job->ns_event_fw_fd = ev;
+}
+
+static void
+ns_event_fw_mod_io(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ struct timeval *tv = NULL;
+ short events = job_type_to_flags(job->job_type);
+
+ if (job->tv.tv_sec || job->tv.tv_usec) {
+ tv = &job->tv;
+ }
+ if (events) {
+ job->ns_event_fw_fd->ev_events = events;
+
+#ifdef DEBUG_FSM
+ /* REALLY make sure that we aren't being re-added */
+ if (event_pending(job->ns_event_fw_fd, events, tv)) {
+ abort();
+ }
+#endif
+ event_add(job->ns_event_fw_fd, tv);
+ } else {
+ /* setting the job_type to remove IO events will remove it from the event system
*/
+ ns_event_fw_io_event_remove(ns_event_fw_ctx, job);
+ }
+}
+
+static void
+ns_event_fw_add_timer(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ /* allocate a new event structure - use the job for the memory context */
+ struct event *ev = job->alloc_event_context(sizeof(struct event), job);
+ evtimer_assign(ev, ns_event_fw_ctx, event_cb, job);
+ evtimer_add(ev, &job->tv);
+ job->ns_event_fw_time = ev;
+}
+
+static void
+ns_event_fw_mod_timer(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ evtimer_add(job->ns_event_fw_time, &job->tv);
+}
+
+static void
+ns_event_fw_add_signal(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ /* allocate a new event structure - use the job for the memory context */
+ struct event *ev = job->alloc_event_context(sizeof(struct event), job);
+ evsignal_assign(ev, ns_event_fw_ctx, job->signal, event_cb, job);
+ evsignal_add(ev, NULL);
+ job->ns_event_fw_sig = ev;
+}
+
+static void
+ns_event_fw_mod_signal(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ /* If the event in the ev argument already has a scheduled timeout, calling
event_add() replaces the old timeout with the new one, or clears the old timeout if the
timeout argument is NULL. */
+ evsignal_add(job->ns_event_fw_sig, NULL);
+}
+
+/* returns
+ 1 - no events to process
+ 0 - normal termination
+ -1 - error
+*/
+static int
+ns_event_fw_loop(ns_event_fw_ctx_t *ns_event_fw_ctx)
+{
+ int rc = event_base_loop(ns_event_fw_ctx, EVLOOP_ONCE);
+ if (rc == 0) {
+ rc = 1;
+ } else {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static ns_event_fw_t ns_event_fw_event = {
+ ns_event_fw_init,
+ ns_event_fw_destroy,
+ ns_event_fw_loop,
+ ns_event_fw_add_io,
+ ns_event_fw_mod_io,
+ ns_event_fw_add_timer,
+ ns_event_fw_mod_timer,
+ ns_event_fw_add_signal,
+ ns_event_fw_mod_signal,
+ ns_event_fw_io_event_done,
+ ns_event_fw_timer_event_done,
+ ns_event_fw_signal_event_done
+};
+
+ns_event_fw_t *
+get_event_framework_event()
+{
+ return &ns_event_fw_event;
+}
diff --git a/src/nunc-stans/ns/ns_event_fw_tevent.c
b/src/nunc-stans/ns/ns_event_fw_tevent.c
new file mode 100644
index 0000000..08a0781
--- /dev/null
+++ b/src/nunc-stans/ns/ns_event_fw_tevent.c
@@ -0,0 +1,332 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/*
+ * This implements the thrpool event framework interface
+ * to the libtevent event framework
+ */
+#include <private/pprio.h>
+#include <talloc.h>
+#include <tevent.h>
+typedef struct tevent_context ns_event_fw_ctx_t;
+typedef struct tevent_fd ns_event_fw_fd_t;
+typedef struct tevent_timer ns_event_fw_time_t;
+typedef struct tevent_signal ns_event_fw_sig_t;
+#define NO_EVENT_TYPEDEFS
+#include "ns_event_fw.h"
+#include <syslog.h>
+
+static void ns_event_fw_add_io(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job);
+static void ns_event_fw_mod_io(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job);
+static void ns_event_fw_add_timer(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job);
+static void ns_event_fw_mod_timer(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job);
+static void ns_event_fw_add_signal(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job);
+static void ns_event_fw_mod_signal(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job);
+static void ns_event_fw_io_event_remove(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t
*job);
+static void ns_event_fw_io_event_done(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t
*job);
+static void ns_event_fw_timer_event_done(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t
*job);
+static void ns_event_fw_signal_event_done(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t
*job);
+
+static void
+tevent_logger_cb(void *context __attribute__((unused)), enum tevent_debug_level level,
const char *fmt, va_list ap)
+{
+ int priority = 0;
+ char *msg = NULL;
+
+ switch (level) {
+ case TEVENT_DEBUG_FATAL: priority = LOG_ERR; break;
+ case TEVENT_DEBUG_ERROR: priority = LOG_ERR; break;
+ case TEVENT_DEBUG_WARNING: priority = LOG_DEBUG; break;
+ case TEVENT_DEBUG_TRACE: priority = LOG_DEBUG; break;
+ default: priority = LOG_DEBUG; break; /* never reached */
+ }
+
+ msg = PR_smprintf("%s\n",fmt);
+ ns_log_valist(priority, msg, ap);
+ ns_free(msg);
+}
+
+static ns_job_type_t
+event_flags_to_type(uint16_t flags)
+{
+ ns_job_type_t job_type = 0;
+ if (flags & TEVENT_FD_READ) {
+ job_type |= NS_JOB_READ;
+ }
+ if (flags & TEVENT_FD_WRITE) {
+ job_type |= NS_JOB_WRITE;
+ }
+ return job_type;
+}
+
+/* this is called by the event main loop when an fd event
+ is triggered - this "maps" the event library interface
+ to our nspr/thrpool interface */
+static void
+fd_event_cb(struct tevent_context *ev __attribute__((unused)), struct tevent_fd *fde
__attribute__((unused)),
+ uint16_t flags, void *arg)
+{
+ ns_job_t *job = (ns_job_t *)arg;
+
+ PR_ASSERT(arg);
+
+ /* I/O jobs can be timed - if we get an fd event, cancel the timer */
+ if (job->ns_event_fw_time) {
+ ns_event_fw_timer_event_done(job->ns_event_fw_ctx, job);
+ }
+ job->output_job_type = event_flags_to_type(flags);
+ job->event_cb(job);
+}
+
+/* this is called by the event main loop when a timer event
+ is triggered - this "maps" the event library interface
+ to our nspr/thrpool interface */
+static void
+timer_event_cb(struct tevent_context *ev __attribute__((unused)), struct tevent_timer *te
__attribute__((unused)),
+ struct timeval current_time __attribute__((unused)), void *arg)
+{
+ ns_job_t *job = (ns_job_t *)arg;
+
+ PR_ASSERT(arg);
+
+ /* I/O jobs can be timed - if we get an timer event, cancel the fd */
+ if (job->ns_event_fw_fd) {
+ ns_event_fw_io_event_done(job->ns_event_fw_ctx, job);
+ }
+ job->output_job_type = NS_JOB_TIMER;
+ job->event_cb(job);
+}
+
+/* this is called by the event main loop when a signal event
+ is triggered - this "maps" the event library interface
+ to our nspr/thrpool interface */
+static void
+signal_event_cb(struct tevent_context *ev __attribute__((unused)), struct tevent_signal
*se __attribute__((unused)),
+ int signum, int count __attribute__((unused)), void *siginfo
__attribute__((unused)), void *arg)
+{
+ ns_job_t *job = (ns_job_t *)arg;
+
+ PR_ASSERT(arg);
+
+ job->output_job_type = NS_JOB_SIGNAL;
+ job->signal = signum;
+ job->event_cb(job);
+}
+
+/* convert from job job_type to event fw type */
+static uint16_t
+job_type_to_flags(ns_job_type_t job_type)
+{
+ uint16_t flags = 0;
+ if (NS_JOB_IS_ACCEPT(job_type) || NS_JOB_IS_READ(job_type)) {
+ flags |= TEVENT_FD_READ;
+ }
+ if (NS_JOB_IS_WRITE(job_type) || NS_JOB_IS_CONNECT(job_type)) {
+ flags |= TEVENT_FD_WRITE;
+ }
+ return flags;
+}
+
+static ns_event_fw_ctx_t *
+ns_event_fw_init(void)
+{
+ ns_event_fw_ctx_t *ev = tevent_context_init(NULL);
+ tevent_set_debug(ev, tevent_logger_cb, NULL);
+ return ev;
+}
+
+static void
+ns_event_fw_destroy(ns_event_fw_ctx_t *ns_event_fw_ctx_t)
+{
+ talloc_free(ns_event_fw_ctx_t);
+}
+
+/*
+ * remove just removes the event from active consideration
+ * by the event framework - the event object may be
+ * reactivated by calling mod - event done will actually
+ * remove and free the object
+ */
+static void
+ns_event_fw_io_event_remove(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ tevent_fd_set_flags(job->ns_event_fw_fd, 0);
+}
+
+static void
+ns_event_fw_io_event_done(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ ns_event_fw_io_event_remove(ns_event_fw_ctx, job);
+ talloc_free(job->ns_event_fw_fd);
+ job->ns_event_fw_fd = NULL;
+}
+
+static void
+ns_event_fw_timer_event_done(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ talloc_free(job->ns_event_fw_time);
+ job->ns_event_fw_time = NULL;
+}
+
+static void
+ns_event_fw_signal_event_done(
+ ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)),
+ ns_job_t *job
+)
+{
+ talloc_free(job->ns_event_fw_sig);
+ job->ns_event_fw_sig = NULL;
+}
+
+static void
+ns_event_fw_add_io(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ /* set the fields in the event */
+ uint16_t flags = job_type_to_flags(job->job_type);
+
+ job->ns_event_fw_fd = tevent_add_fd(ns_event_fw_ctx, ns_event_fw_ctx,
+ PR_FileDesc2NativeHandle(job->fd),
+ flags, fd_event_cb, job);
+ /* add/schedule the timer event */
+ if (job->tv.tv_sec || job->tv.tv_usec) {
+ job->ns_event_fw_time = tevent_add_timer(ns_event_fw_ctx, ns_event_fw_ctx,
+ job->tv, timer_event_cb, job);
+ }
+}
+
+static void
+ns_event_fw_mod_io(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ uint16_t events = job_type_to_flags(job->job_type);
+
+ if (job->tv.tv_sec || job->tv.tv_usec) {
+ ns_event_fw_mod_timer(ns_event_fw_ctx, job);
+ }
+ tevent_fd_set_flags(job->ns_event_fw_fd, events);
+}
+
+static void
+ns_event_fw_add_timer(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ job->ns_event_fw_time = tevent_add_timer(ns_event_fw_ctx, ns_event_fw_ctx,
+ job->tv, timer_event_cb, job);
+}
+
+static void
+ns_event_fw_mod_timer(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ ns_event_fw_timer_event_done(ns_event_fw_ctx, job);
+ ns_event_fw_add_timer(ns_event_fw_ctx, job);
+}
+
+static void
+ns_event_fw_add_signal(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ job->ns_event_fw_sig = tevent_add_signal(ns_event_fw_ctx, ns_event_fw_ctx,
+ job->signal, 0, signal_event_cb, job);
+}
+
+static void
+ns_event_fw_mod_signal(
+ ns_event_fw_ctx_t *ns_event_fw_ctx,
+ ns_job_t *job
+)
+{
+ ns_event_fw_signal_event_done(ns_event_fw_ctx, job);
+ ns_event_fw_add_signal(ns_event_fw_ctx, job);
+}
+
+/* returns
+ 1 - no events to process
+ 0 - normal termination
+ -1 - error
+*/
+static int
+ns_event_fw_loop(ns_event_fw_ctx_t *ns_event_fw_ctx)
+{
+ int rc = tevent_loop_once(ns_event_fw_ctx);
+ if (rc == 0) {
+ rc = 1;
+ } else {
+ rc = -1;
+ }
+ /* tevent apparently has no normal termination return value */
+ return rc;
+}
+
+static ns_event_fw_t ns_event_fw_tevent = {
+ ns_event_fw_init,
+ ns_event_fw_destroy,
+ ns_event_fw_loop,
+ ns_event_fw_add_io,
+ ns_event_fw_mod_io,
+ ns_event_fw_add_timer,
+ ns_event_fw_mod_timer,
+ ns_event_fw_add_signal,
+ ns_event_fw_mod_signal,
+ ns_event_fw_io_event_done,
+ ns_event_fw_timer_event_done,
+ ns_event_fw_signal_event_done
+};
+
+ns_event_fw_t *
+get_event_framework_tevent()
+{
+ return &ns_event_fw_tevent;
+}
diff --git a/src/nunc-stans/ns/ns_private.h b/src/nunc-stans/ns/ns_private.h
new file mode 100644
index 0000000..88e2732
--- /dev/null
+++ b/src/nunc-stans/ns/ns_private.h
@@ -0,0 +1,106 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+/*! \file ns_private.h
+ \brief Nunc Stans private API
+
+ This is the private API for Nunc Stans
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "nspr.h"
+#include "prmon.h"
+#include <nunc-stans.h>
+
+#include <inttypes.h>
+
+/**
+ * Forward declaration of the thread struct - internal
+ *
+ * The actual struct is opaque to applications. The forward declaration is here
+ * for the typedef.
+ */
+struct ns_thread_t;
+/**
+ * This is the thread type - internal
+ *
+ * The actual thread type is opaque to applications.
+ */
+typedef struct ns_thread_t ns_thread_t;
+
+/**
+ * Log an error to logging system. - internal
+ * \param priority The log level
+ * \param fmt The log message string format
+ */
+void ns_log(int priority, const char *fmt, ...);
+
+/**
+ * Log an error to logging system. - internal
+ * \param priority The log level
+ * \param fmt The log message string format
+ * \param varg The va_list arg
+ */
+void ns_log_valist(int priority, const char *fmt, va_list varg);
+
+/**
+ * \param size - Memory allocation size - internal
+ * \return - allocated memory
+ */
+void *ns_malloc(size_t size);
+
+/**
+ * \param size - Memory allocation size - internal
+ * \param alignment - The allignment of the memory. Must be a power of two.
+ * \return - allocated memory
+ */
+void *ns_memalign(size_t size, size_t alignment);
+
+/**
+ * \param count - number of items - internal
+ * \param size - Memory allocation size
+ * \return - allocated memory
+ */
+void *ns_calloc(size_t count, size_t size);
+
+/**
+ * \param ptr - pointer to the memory block - internal
+ * \param size - size of allocation
+ * \return - allocated memory
+ */
+void *ns_realloc(void *ptr, size_t size);
+
+/**
+ * \param ptr - pointer to memory to be freed - internal
+ */
+void ns_free(void *ptr);
diff --git a/src/nunc-stans/ns/ns_thrpool.c b/src/nunc-stans/ns/ns_thrpool.c
new file mode 100644
index 0000000..744749b
--- /dev/null
+++ b/src/nunc-stans/ns/ns_thrpool.c
@@ -0,0 +1,1584 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <nspr.h>
+#include <private/pprio.h>
+#include "ns_event_fw.h"
+
+/* SDS contains the lock free queue wrapper */
+#include <sds.h>
+
+#include <assert.h>
+
+
+/*
+ * Threadpool
+ */
+struct ns_thrpool_t {
+ sds_lqueue *work_q;
+ sds_lqueue *event_q;
+ int32_t shutdown;
+ int32_t shutdown_event_loop;
+ pthread_cond_t work_q_cv;
+ pthread_mutex_t work_q_lock;
+ sds_queue *thread_stack;
+ pthread_t event_thread;
+ PRFileDesc *event_q_wakeup_pipe_read;
+ PRFileDesc *event_q_wakeup_pipe_write;
+ ns_job_t *event_q_wakeup_job;
+ ns_event_fw_t *ns_event_fw;
+ ns_event_fw_ctx_t *ns_event_fw_ctx;
+ size_t stacksize;
+};
+
+struct ns_thread_t {
+ pthread_t thr; /* the thread */
+ struct ns_thrpool_t *tp; /* pointer back to thread pool */
+};
+
+#define ERRNO_WOULD_BLOCK(iii) (iii == EWOULDBLOCK) || (iii == EAGAIN)
+
+/* Forward declarations. */
+static void internal_ns_job_done(ns_job_t *job);
+static void internal_ns_job_rearm(ns_job_t *job);
+static void work_job_execute(ns_job_t *job);
+static void event_q_notify(ns_job_t *job);
+static void work_q_notify(ns_job_t *job);
+
+/* logging function pointers */
+static void (*logger)(int, const char *, va_list) = NULL;
+static void (*log_start)( void ) = NULL;
+static void (*log_close)( void ) = NULL;
+
+/* memory function pointers */
+static void *(*malloc_fct)(size_t) = NULL;
+static void *(*memalign_fct)(size_t size, size_t alignment) = NULL;
+static void *(*calloc_fct)(size_t, size_t) = NULL;
+static void *(*realloc_fct)(void *, size_t) = NULL;
+static void (*free_fct)(void *) = NULL;
+
+/* syslog functions */
+static void
+ns_syslog(int priority, const char *fmt, va_list varg)
+{
+ vsyslog(priority, fmt, varg);
+}
+
+static void
+ns_syslog_start( void )
+{
+ openlog("nunc-stans", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
+}
+
+static void
+ns_syslog_close( void )
+{
+ closelog();
+}
+
+/* default memory functions */
+static void*
+os_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+static void*
+os_memalign_malloc(size_t size, size_t alignment)
+{
+ void *ptr;
+ if (posix_memalign(&ptr, alignment, size) != 0) {
+ return NULL;
+ }
+ return ptr;
+}
+
+static void*
+os_calloc(size_t count, size_t size)
+{
+ return calloc(count, size);
+}
+
+static void*
+os_realloc(void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+static void
+os_free(void *ptr)
+{
+ free(ptr);
+}
+
+int32_t
+ns_thrpool_is_shutdown(struct ns_thrpool_t *tp)
+{
+ /* We need to barrier this somehow? */
+ int32_t result = 0;
+ __atomic_load(&(tp->shutdown), &result, __ATOMIC_SEQ_CST);
+ return result;
+}
+
+int32_t
+ns_thrpool_is_event_shutdown(struct ns_thrpool_t *tp)
+{
+ /* We need to barrier this somehow? */
+ int32_t result = 0;
+ __atomic_load(&(tp->shutdown_event_loop), &result, __ATOMIC_SEQ_CST);
+ return result;
+}
+
+
+static void
+job_queue_cleanup(void *arg) {
+ struct ns_job_t *job = (ns_job_t *)arg;
+ if (job != NULL) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_queue_job_cleanup: destroying job %x\n", job);
+#endif
+ internal_ns_job_done(job);
+ }
+}
+
+static void
+internal_ns_job_done(ns_job_t *job)
+{
+ pthread_mutex_lock(job->monitor);
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "internal_ns_job_done %x state %d moving to
NS_JOB_DELETED\n", job, job->state);
+#endif
+ /* In theory, due to the monitor placement, this should never be able to be seen by
any other thread ... */
+ job->state = NS_JOB_DELETED;
+
+ if (job->ns_event_fw_fd) {
+
job->tp->ns_event_fw->ns_event_fw_io_event_done(job->tp->ns_event_fw_ctx,
job);
+ }
+ if (job->ns_event_fw_time) {
+
job->tp->ns_event_fw->ns_event_fw_timer_event_done(job->tp->ns_event_fw_ctx,
job);
+ }
+ if (job->ns_event_fw_sig) {
+
job->tp->ns_event_fw->ns_event_fw_signal_event_done(job->tp->ns_event_fw_ctx,
job);
+ }
+
+ if (job->fd && !NS_JOB_IS_PRESERVE_FD(job->job_type)) {
+ PR_Close(job->fd);
+ } /* else application is responsible for fd */
+
+ if (job->done_cb != NULL) {
+ job->done_cb(job);
+ }
+
+ pthread_mutex_unlock(job->monitor);
+ pthread_mutex_destroy(job->monitor);
+ ns_free(job->monitor);
+
+ ns_free(job);
+}
+
+/* Is there a way to get a status back from here? */
+static void
+internal_ns_job_rearm(ns_job_t *job)
+{
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state == NS_JOB_NEEDS_ARM);
+ /* Don't think I need to check persistence here, it could be the first arm ...
*/
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "internal_ns_rearm_job %x state %d moving to
NS_JOB_ARMED\n", job, job->state);
+#endif
+ job->state = NS_JOB_ARMED;
+
+ if (NS_JOB_IS_IO(job->job_type) || NS_JOB_IS_TIMER(job->job_type) ||
NS_JOB_IS_SIGNAL(job->job_type)) {
+ event_q_notify(job);
+ } else {
+ /* if this is a non event task, just queue it on the work q */
+ /* Prevents an un-necessary queue / dequeue to the event_q */
+ work_q_notify(job);
+ }
+ pthread_mutex_unlock(job->monitor);
+}
+
+static void
+work_job_execute(ns_job_t *job)
+{
+ /*
+ * when we pull out a job to work on it, we do it here rather than calling
+ * job function directly.
+ *
+ * DANGER: After this is called, you must NOT ACCESS job again. It MAY be
+ * DELETED! Crashes abound, you have been warned ...
+ */
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "work_job_execute %x state %d moving to
NS_JOB_RUNNING\n", job, job->state);
+#endif
+ job->state = NS_JOB_RUNNING;
+ /* Do the work. */
+ PR_ASSERT(job->func);
+ job->func(job);
+
+ /* Only if !threaded job, and persistent, we automatically tell us to rearm */
+ if (!NS_JOB_IS_THREAD(job->job_type) &&
NS_JOB_IS_PERSIST(job->job_type) && job->state == NS_JOB_RUNNING) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "work_job_execute PERSIST and RUNNING, remarking %x as
NS_JOB_NEEDS_ARM\n", job);
+#endif
+ /*
+ * So at this point, if this is an IO or a SIGNAL job then, we are
+ * still in the event framework's io event queue. So we actually
+ * are already rearmed!!!
+ *
+ * This is *exactly* why it's impossible to disarm a persist IO job
+ * once we start it from external threads! Too many dangers abound!
+ */
+ if (NS_JOB_IS_IO(job->job_type) || NS_JOB_IS_SIGNAL(job->job_type)) {
+ job->state = NS_JOB_ARMED;
+ pthread_mutex_unlock(job->monitor);
+ return;
+ } else {
+ job->state = NS_JOB_NEEDS_ARM;
+ }
+ }
+
+ if (job->state == NS_JOB_NEEDS_DELETE) {
+ /* Send it to the job mincer! */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "work_job_execute %x state %d job func complete, sending
to job_done...\n", job, job->state);
+#endif
+ pthread_mutex_unlock(job->monitor);
+ internal_ns_job_done(job);
+ /* MUST NOT ACCESS JOB AGAIN.*/
+ } else if (job->state == NS_JOB_NEEDS_ARM) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "work_job_execute %x state %d job func complete, sending
to rearm...\n", job, job->state);
+#endif
+ /* Rearm the job! */
+ pthread_mutex_unlock(job->monitor);
+ internal_ns_job_rearm(job);
+ } else {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "work_job_execute %x state %d job func complete move to
NS_JOB_WAITING\n", job, job->state);
+#endif
+ /* This should never occur? What's going on ... */
+ PR_ASSERT(!NS_JOB_IS_PERSIST(job->job_type));
+ /* We are now idle, set waiting. */
+ job->state = NS_JOB_WAITING;
+ pthread_mutex_unlock(job->monitor);
+ }
+ /* MUST NOT ACCESS JOB AGAIN */
+}
+
+static void
+work_q_wait(ns_thrpool_t *tp)
+{
+ pthread_mutex_lock(&(tp->work_q_lock));
+ pthread_cond_wait(&(tp->work_q_cv), &(tp->work_q_lock));
+ pthread_mutex_unlock(&(tp->work_q_lock));
+}
+
+static void
+work_q_notify(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "work_q_notify %x state %d\n", job, job->state);
+#endif
+ PR_ASSERT(job->state == NS_JOB_ARMED);
+ if (job->state != NS_JOB_ARMED) {
+ /* Maybe we should return some error here? */
+ ns_log(LOG_ERR, "work_q_notify %x state %d is not ARMED, cannot
queue!\n", job, job->state);
+ pthread_mutex_unlock(job->monitor);
+ return;
+ }
+ /* MUST NOT ACCESS job after enqueue. So we stash tp.*/
+ ns_thrpool_t *ltp = job->tp;
+ pthread_mutex_unlock(job->monitor);
+ sds_lqueue_enqueue(ltp->work_q, (void *)job);
+ pthread_mutex_lock(&(ltp->work_q_lock));
+ pthread_cond_signal(&(ltp->work_q_cv));
+ pthread_mutex_unlock(&(ltp->work_q_lock));
+ PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield to allow worker thread to pick up job */
+}
+
+/*
+ * worker thread function
+ */
+static void *
+worker_thread_func(void *arg)
+{
+ ns_thread_t *thr = (ns_thread_t *)arg;
+ ns_thrpool_t *tp = thr->tp;
+
+ /* Get ready to use lock free ds */
+ sds_lqueue_tprep(tp->work_q);
+
+ /*
+ * Execute jobs until shutdown is set and the queues are empty.
+ */
+ while (!ns_thrpool_is_shutdown(tp)) {
+ ns_job_t *job = NULL;
+ /* Don't need monitor here, job_dequeue barriers the memory for us. Job will
be valid */
+ while(sds_lqueue_dequeue(tp->work_q, (void **)&job) == SDS_LIST_EXHAUSTED
&& !ns_thrpool_is_shutdown(tp))
+ {
+ work_q_wait(tp);
+ }
+
+ if (job) {
+ work_job_execute(job);
+ /* MUST NOT ACCESS JOB FROM THIS POINT */
+ }
+ }
+
+ /* With sds, it cleans the thread on join automatically. */
+ return NULL;
+}
+
+/*
+ * Add a new event, or modify or delete an existing event
+ */
+static void
+update_event(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "update_event %x state %d\n", job, job->state);
+#endif
+ PR_ASSERT(job->state == NS_JOB_NEEDS_DELETE || job->state == NS_JOB_ARMED);
+ if (job->state == NS_JOB_NEEDS_DELETE) {
+ pthread_mutex_unlock(job->monitor);
+ internal_ns_job_done(job);
+ return;
+ } else if (NS_JOB_IS_IO(job->job_type) || job->ns_event_fw_fd) {
+ if (!job->ns_event_fw_fd) {
+
job->tp->ns_event_fw->ns_event_fw_add_io(job->tp->ns_event_fw_ctx, job);
+ } else {
+
job->tp->ns_event_fw->ns_event_fw_mod_io(job->tp->ns_event_fw_ctx, job);
+ }
+ pthread_mutex_unlock(job->monitor);
+ /* We need these returns to prevent a race on the next else if condition when we
release job->monitor */
+ return;
+ } else if (NS_JOB_IS_TIMER(job->job_type) || job->ns_event_fw_time) {
+ if (!job->ns_event_fw_time) {
+
job->tp->ns_event_fw->ns_event_fw_add_timer(job->tp->ns_event_fw_ctx,
job);
+ } else {
+
job->tp->ns_event_fw->ns_event_fw_mod_timer(job->tp->ns_event_fw_ctx,
job);
+ }
+ pthread_mutex_unlock(job->monitor);
+ return;
+ } else if (NS_JOB_IS_SIGNAL(job->job_type) || job->ns_event_fw_sig) {
+ if (!job->ns_event_fw_sig) {
+
job->tp->ns_event_fw->ns_event_fw_add_signal(job->tp->ns_event_fw_ctx,
job);
+ } else {
+
job->tp->ns_event_fw->ns_event_fw_mod_signal(job->tp->ns_event_fw_ctx,
job);
+ }
+ pthread_mutex_unlock(job->monitor);
+ return;
+ } else {
+ /* It's a "run now" job. */
+ if (NS_JOB_IS_THREAD(job->job_type)) {
+ pthread_mutex_unlock(job->monitor);
+ work_q_notify(job);
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ event_q_notify(job);
+ }
+ }
+
+ return;
+}
+
+static void
+event_q_wait(ns_thrpool_t *tp __attribute__((unused)))
+{
+ /* unused for now */
+ /* the main event loop will do our "waiting" - waiting for events
+ to happen, or we can trigger an event to "wakeup" the event
+ loop (see event_q_notify) */
+}
+
+static void
+event_q_wake(ns_thrpool_t *tp)
+{
+ int32_t len;
+
+ /* Rather than trying to make anew event, tell the event loop to exit with no
+ * events.
+ */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_q_wake attempting to wake event queue.\n");
+#endif
+
+ /* NSPR I/O doesn't allow non-blocking signal pipes, so use write instead of
PR_Write */
+ len = write(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_write),
+ "a", 1);
+ if (1 != len) {
+ if ((errno == 0) || ERRNO_WOULD_BLOCK(errno)) {
+ ns_log(LOG_DEBUG, "Write blocked for wakeup pipe - ignore %d\n",
+ errno);
+ } else {
+ ns_log(LOG_ERR, "Error: could not write wakeup pipe: %d:%s\n",
+ errno, PR_ErrorToString(errno, PR_LANGUAGE_I_DEFAULT));
+ }
+ }
+ PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield to allow event thread to pick up event */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_q_wake result. 0\n");
+#endif
+}
+
+static void
+event_q_notify(ns_job_t *job)
+{
+ ns_thrpool_t *tp = job->tp;
+ /* if we are being called from a thread other than the
+ event loop thread, we have to notify that thread to
+ perform the event work */
+ if (pthread_equal(tp->event_thread, pthread_self()) != 0) {
+ /* If we are being run from the same thread as the event
+ loop thread, we can just update the event here */
+ update_event(job);
+ } else {
+ /* The event loop may be waiting for events, and may wait a long
+ time by default if there are no events to process - since we
+ want to add an event, we have to "wake up" the event loop by
+ posting an event - this will cause the wakeup_cb to be called
+ which will empty the event_q and add all of the events
+ */
+ /* NOTE: once job is queued, it may be deleted immediately in
+ * another thread, if the event loop picks up the deletion
+ * job before we can notify it below - so make sure not to
+ * refer to job after the enqueue.
+ */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_q_notify enqueuing %x with state %d\n", job,
job->state);
+#endif
+
+ sds_lqueue_enqueue(tp->event_q, (void *)job);
+ event_q_wake(tp);
+ }
+}
+
+/* This is run inside the event loop thread, and only in the
+ event loop thread
+ This function pulls the io/timer/signal event requests off
+ the request queue, formats the events in the format required
+ by the event framework, and adds them
+*/
+static void
+get_new_event_requests(ns_thrpool_t *tp)
+{
+ ns_job_t *job = NULL;
+ while (sds_lqueue_dequeue(tp->event_q, (void **)&job) == SDS_SUCCESS)
+ {
+ if (job != NULL) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "get_new_event_requests Dequeuing %x with state
%d\n", job, job->state);
+#endif
+ update_event(job);
+#ifdef DEBUG
+ } else {
+ ns_log(LOG_DEBUG, "get_new_event_requests Dequeuing NULL job\n");
+#endif
+ }
+ }
+}
+
+static void *
+event_loop_thread_func(void *arg)
+{
+ struct ns_thrpool_t *tp = (struct ns_thrpool_t *)arg;
+ int rc;
+
+ sds_lqueue_tprep(tp->event_q);
+
+ while (!ns_thrpool_is_event_shutdown(tp)) {
+ /* get new event requests */
+ get_new_event_requests(tp);
+ /* process events */
+ /* return 1 - no events ; 0 - normal exit ; -1 - error */
+ rc = tp->ns_event_fw->ns_event_fw_loop(tp->ns_event_fw_ctx);
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_loop_thread_func woke event queue. rc=%d\n",
rc);
+#endif
+ if (rc == -1) { /* error */
+ } else if (rc == 0) { /* exiting */
+ } else { /* no events to process */
+ event_q_wait(tp);
+ }
+ }
+ return NULL;
+}
+
+/*
+ * The event framework calls this function when it receives an
+ * event - the event framework does all of the work to map
+ * the framework flags, etc. into the job
+ * This function is called only in the event loop thread.
+ * If the THREAD flag is set, this means the job is to be
+ * handed off to the work q - otherwise, execute it now
+ * in this thread (the event loop thread)
+ * the function must be careful not to block the event
+ * loop thread or starvation will occur
+ */
+static void
+event_cb(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ /*
+ * Sometimes if we queue then request to delete a job REALLY fast, this
+ * will trigger
+ * Fix is that if ARMED we don't allow the job to move to NS_JOB_NEEDS_DELETE
+ */
+
+ /* There is no guarantee this won't be called once we start to enter the
shutdown, especially with timers .... */
+ pthread_mutex_lock(job->monitor);
+
+ PR_ASSERT(job->state == NS_JOB_ARMED || job->state == NS_JOB_NEEDS_DELETE);
+ if (job->state == NS_JOB_ARMED && NS_JOB_IS_THREAD(job->job_type)) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_cb %x state %d threaded, send to work_q\n",
job, job->state);
+#endif
+ pthread_mutex_unlock(job->monitor);
+ work_q_notify(job);
+ } else if (job->state == NS_JOB_NEEDS_DELETE) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_cb %x state %d ignoring as NS_JOB_NEEDS_DELETE
set\n", job, job->state);
+#endif
+ /*
+ * If the job is in need of delete we IGNORE IT
+ * It's here because it's been QUEUED for deletion and *may* be coming
+ * from the thrpool destroy thread!
+ */
+ pthread_mutex_unlock(job->monitor);
+
+ } else {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "event_cb %x state %d non-threaded, execute right
meow\n", job, job->state);
+#endif
+ /* Not threaded, execute now! */
+ pthread_mutex_unlock(job->monitor);
+ work_job_execute(job);
+ /* MUST NOT ACCESS JOB FROM THIS POINT */
+ }
+}
+
+static void
+wakeup_cb(ns_job_t *job)
+{
+ int32_t len;
+ char buf[1];
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "wakeup_cb %x state %d wakeup_cb\n", job,
job->state);
+#endif
+
+ /* NSPR I/O doesn't allow non-blocking signal pipes, so use read instead of
PR_Read */
+ len = read(PR_FileDesc2NativeHandle(job->tp->event_q_wakeup_pipe_read),
+ buf, 1);
+ if (1 != len) {
+ if ((errno == 0) || ERRNO_WOULD_BLOCK(errno)) {
+ ns_log(LOG_DEBUG, "Read blocked for wakeup pipe - ignore %d\n",
+ errno);
+ } else {
+ ns_log(LOG_ERR, "Error: could not read wakeup pipe: %d:%s\n",
+ errno, PR_ErrorToString(errno, PR_LANGUAGE_I_DEFAULT));
+ }
+ }
+ /* wakeup_cb is usually called because a worker thread has posted a new
+ event we need to add - get all new event requests */
+ get_new_event_requests(job->tp);
+}
+/* convenience function for the event fw to use to allocate
+ space for its event fw event object
+ it is assumed that eventually job will have a memory region/arena
+ to use
+ these functions will be called in the event loop thread from
+ the event framework function that adds a new event
+*/
+static void *
+alloc_event_context(size_t size, ns_job_t *job __attribute__((unused)))
+{
+ return ns_malloc(size);
+}
+
+static void
+free_event_context(void *ev_ctx, ns_job_t *job __attribute__((unused)))
+{
+ ns_free(ev_ctx);
+}
+
+static ns_job_t *
+new_ns_job(ns_thrpool_t *tp, PRFileDesc *fd, ns_job_type_t job_type, ns_job_func_t func,
struct ns_job_data_t *data)
+{
+ ns_job_t *job = ns_calloc(1, sizeof(ns_job_t));
+ job->monitor = ns_calloc(1, sizeof(pthread_mutex_t));
+
+ pthread_mutexattr_t *monitor_attr = ns_calloc(1, sizeof(pthread_mutexattr_t));
+ pthread_mutexattr_init(monitor_attr);
+ pthread_mutexattr_settype(monitor_attr, PTHREAD_MUTEX_RECURSIVE);
+ assert(pthread_mutex_init(job->monitor, monitor_attr) == 0);
+ ns_free(monitor_attr);
+
+ job->tp = tp;
+ /* We have to have this due to our obsession of hiding struct contents ... */
+ /* It's only used in tevent anyway .... */
+ job->ns_event_fw_ctx = tp->ns_event_fw_ctx;
+ job->fd = fd;
+ job->func = func;
+ job->data = data;
+ job->alloc_event_context = alloc_event_context;
+ job->free_event_context = free_event_context;
+ job->event_cb = event_cb;
+ job->job_type = job_type;
+ job->done_cb = NULL;
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "new_ns_job %x initial NS_JOB_WAITING\n", job);
+#endif
+ job->state = NS_JOB_WAITING;
+ return job;
+}
+
+static ns_job_t *
+alloc_io_context(ns_thrpool_t *tp, PRFileDesc *fd, ns_job_type_t job_type,
+ ns_job_func_t func, struct ns_job_data_t *data)
+{
+ ns_job_t *job = new_ns_job(tp, fd, job_type, func, data);
+
+ return job;
+}
+
+static ns_job_t *
+alloc_timeout_context(ns_thrpool_t *tp, struct timeval *tv, ns_job_type_t job_type,
+ ns_job_func_t func, struct ns_job_data_t *data)
+{
+ ns_job_t *job = new_ns_job(tp, NULL, NS_JOB_TIMER | job_type, func, data);
+ job->tv = *tv;
+
+ return job;
+}
+
+static ns_job_t *
+alloc_signal_context(ns_thrpool_t *tp, PRInt32 signum, ns_job_type_t job_type,
+ ns_job_func_t func, struct ns_job_data_t *data)
+{
+ ns_job_t *job = new_ns_job(tp, NULL, NS_JOB_SIGNAL | job_type, func, data);
+ job->signal = signum;
+
+ return job;
+}
+
+PRStatus
+ns_job_done(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ if (job == NULL) {
+ return PR_FAILURE;
+ }
+
+ /* Get the shutdown state ONCE at the start, atomically */
+ int32_t shutdown_state = ns_thrpool_is_shutdown(job->tp);
+
+ pthread_mutex_lock(job->monitor);
+
+ if (job->state == NS_JOB_NEEDS_DELETE || job->state == NS_JOB_DELETED){
+ /* Just return if the job has been marked for deletion */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> %x state %d return
early\n", job, shutdown_state, job->state);
+#endif
+ pthread_mutex_unlock(job->monitor);
+ return PR_SUCCESS;
+ }
+
+ /* Do not allow an armed job to be removed UNLESS the server is shutting down */
+ if (job->state == NS_JOB_ARMED && !shutdown_state) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> false state %d failed to
mark as done\n", job, job->state);
+#endif
+ pthread_mutex_unlock(job->monitor);
+ return PR_FAILURE;
+ }
+
+ if (job->state == NS_JOB_RUNNING || job->state == NS_JOB_NEEDS_ARM) {
+ /* For this to be called, and NS_JOB_RUNNING, we *must* be the callback thread!
*/
+ /* Just mark it (ie do nothing), the work_job_execute function will trigger
internal_ns_job_done */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> false state %d setting
to async NS_JOB_NEEDS_DELETE\n", job, job->state);
+#endif
+ job->state = NS_JOB_NEEDS_DELETE;
+ pthread_mutex_unlock(job->monitor);
+ } else if (!shutdown_state) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> false state %d setting
NS_JOB_NEEDS_DELETE and queuing\n", job, job->state);
+#endif
+ job->state = NS_JOB_NEEDS_DELETE;
+ pthread_mutex_unlock(job->monitor);
+ event_q_notify(job);
+ } else {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> true state %d setting
NS_JOB_NEEDS_DELETE and delete immediate\n", job, job->state);
+#endif
+ job->state = NS_JOB_NEEDS_DELETE;
+ /* We are shutting down, just remove it! */
+ pthread_mutex_unlock(job->monitor);
+ internal_ns_job_done(job);
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus
+ns_create_job(struct ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, struct
ns_job_t **job)
+{
+ if (job == NULL) {
+ /* This won't queue the job, so to pass NULL makes no sense */
+ return PR_FAILURE;
+ }
+
+ if (ns_thrpool_is_shutdown(tp)) {
+ return PR_FAILURE;
+ }
+
+ *job = new_ns_job(tp, NULL, job_type, func, NULL);
+ if (*job == NULL) {
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+/* queue a file descriptor to listen for and accept new connections */
+PRStatus
+ns_add_io_job(ns_thrpool_t *tp, PRFileDesc *fd, ns_job_type_t job_type,
+ ns_job_func_t func, void *data, ns_job_t **job)
+{
+ ns_job_t *_job = NULL;
+
+ if (job) {
+ *job = NULL;
+ }
+
+ /* Don't allow a job to be added if the threadpool is being shut down. */
+ if (ns_thrpool_is_shutdown(tp)) {
+ return PR_FAILURE;
+ }
+
+ /* Don't allow an accept job to be run outside of the event thread.
+ * We do this so a listener job won't shut down while still processing
+ * current connections in other threads.
+ * TODO: Need to be able to have multiple threads accept() at the same time
+ * This is fine - just have to remove the listen job in the polling thread
+ * immediately after receiving notification - then call the job to do the
+ * accept(), which will add back the persistent listener job immediately after
+ * doing the accept()
+ * This will be a combination of a non-threaded job and a threaded job
+ *
+ */
+ if (NS_JOB_IS_ACCEPT(job_type) && NS_JOB_IS_THREAD(job_type)) {
+ return PR_FAILURE;
+ }
+
+ /* get an event context for an accept */
+ _job = alloc_io_context(tp, fd, job_type, func, data);
+ if (!_job) {
+ return PR_FAILURE;
+ }
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_add_io_job state %d moving to NS_JOB_ARMED\n",
(_job)->state);
+#endif
+ _job->state = NS_JOB_NEEDS_ARM;
+ internal_ns_job_rearm(_job);
+
+ /* fill in a pointer to the job for the caller if requested */
+ if (job) {
+ *job = _job;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+ns_add_timeout_job(ns_thrpool_t *tp, struct timeval *tv, ns_job_type_t job_type,
+ ns_job_func_t func, void *data, ns_job_t **job)
+{
+ ns_job_t *_job = NULL;
+
+ if (job) {
+ *job = NULL;
+ }
+
+ /* Don't allow a job to be added if the threadpool is being shut down. */
+ if (ns_thrpool_is_shutdown(tp)) {
+ return PR_FAILURE;
+ }
+
+ /* get an event context for a timer job */
+ _job = alloc_timeout_context(tp, tv, job_type, func, data);
+ if (!_job) {
+ return PR_FAILURE;
+ }
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_add_timeout_job state %d moving to NS_JOB_ARMED\n",
(_job)->state);
+#endif
+ _job->state = NS_JOB_NEEDS_ARM;
+ internal_ns_job_rearm(_job);
+
+ /* fill in a pointer to the job for the caller if requested */
+ if (job) {
+ *job = _job;
+ }
+
+ return PR_SUCCESS;
+}
+
+/* queue a file descriptor to listen for and accept new connections */
+PRStatus
+ns_add_io_timeout_job(ns_thrpool_t *tp, PRFileDesc *fd, struct timeval *tv,
+ ns_job_type_t job_type, ns_job_func_t func, void *data, ns_job_t **job)
+{
+ ns_job_t *_job = NULL;
+
+ if (job) {
+ *job = NULL;
+ }
+
+ /* Don't allow a job to be added if the threadpool is being shut down. */
+ if (ns_thrpool_is_shutdown(tp)) {
+ return PR_FAILURE;
+ }
+
+ /* Don't allow an accept job to be run outside of the event thread.
+ * We do this so a listener job won't shut down while still processing
+ * current connections in other threads.
+ * TODO: Need to be able to have multiple threads accept() at the same time
+ * This is fine - just have to remove the listen job in the polling thread
+ * immediately after receiving notification - then call the job to do the
+ * accept(), which will add back the persistent listener job immediately after
+ * doing the accept()
+ * This will be a combination of a non-threaded job and a threaded job
+ *
+ */
+ if (NS_JOB_IS_ACCEPT(job_type) && NS_JOB_IS_THREAD(job_type)) {
+ return PR_FAILURE;
+ }
+
+ /* get an event context for an accept */
+ _job = alloc_io_context(tp, fd, job_type|NS_JOB_TIMER, func, data);
+ if (!_job) {
+ return PR_FAILURE;
+ }
+ _job->tv = *tv;
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_add_io_timeout_job state %d moving to
NS_JOB_ARMED\n", (_job)->state);
+#endif
+ _job->state = NS_JOB_NEEDS_ARM;
+ internal_ns_job_rearm(_job);
+
+ /* fill in a pointer to the job for the caller if requested */
+ if (job) {
+ *job = _job;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+ns_add_signal_job(ns_thrpool_t *tp, PRInt32 signum, ns_job_type_t job_type,
+ ns_job_func_t func, void *data, ns_job_t **job)
+{
+ ns_job_t *_job = NULL;
+
+ if (job) {
+ *job = NULL;
+ }
+
+ /* Don't allow a job to be added if the threadpool is being shut down. */
+ if (ns_thrpool_is_shutdown(tp)) {
+ return PR_FAILURE;
+ }
+
+ /* get an event context for a signal job */
+ _job = alloc_signal_context(tp, signum, job_type, func, data);
+ if (!_job) {
+ return PR_FAILURE;
+ }
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_add_signal_job state %d moving to NS_JOB_ARMED\n",
(_job)->state);
+#endif
+ _job->state = NS_JOB_NEEDS_ARM;
+ internal_ns_job_rearm(_job);
+
+ /* fill in a pointer to the job for the caller if requested */
+ if (job) {
+ *job = _job;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+ns_add_job(ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, void *data,
ns_job_t **job)
+{
+ ns_job_t *_job = NULL;
+
+ if (job) {
+ *job = NULL;
+ }
+
+ /* Don't allow a job to be added if the threadpool is being shut down. */
+ if (ns_thrpool_is_shutdown(tp)) {
+ return PR_FAILURE;
+ }
+
+ _job = new_ns_job(tp, NULL, job_type, func, data);
+ if (!_job) {
+ return PR_FAILURE;
+ }
+ /* fill in a pointer to the job for the caller if requested */
+ if (job) {
+ *job = _job;
+ }
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_add_job %x state %d moving to NS_JOB_ARMED\n", _job,
(_job)->state);
+#endif
+ _job->state = NS_JOB_NEEDS_ARM;
+ internal_ns_job_rearm(_job);
+
+ return PR_SUCCESS;
+}
+
+/*
+ * Because of the design of work_job_execute, when we are in RUNNING
+ * we hold the monitor. As a result, we don't need to assert the current thread
+ * because we *already must* be the current thread, as no one else could take
+ * the monitor away.
+ *
+ * The same is true of DELETED, which represents that we are deleting things.
+ * To set NEEDS_DELETE -> DELETED, we must hold the monitor, and at that point
+ * the monitor is released and the job destroyed. As a result, we only need to
+ * assert that we are not "NEEDS_DELETE" in many cases.
+ */
+
+void *
+ns_job_get_data(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state != NS_JOB_DELETED);
+ if (job->state != NS_JOB_DELETED) {
+ pthread_mutex_unlock(job->monitor);
+ return job->data;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return NULL;
+ }
+}
+
+PRStatus
+ns_job_set_data(ns_job_t *job, void *data)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING);
+ if (job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING ) {
+ job->data = data;
+ pthread_mutex_unlock(job->monitor);
+ return PR_SUCCESS;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return PR_FAILURE;
+ }
+}
+
+ns_thrpool_t *
+ns_job_get_tp(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state != NS_JOB_DELETED);
+ if (job->state != NS_JOB_DELETED) {
+ pthread_mutex_unlock(job->monitor);
+ return job->tp;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return NULL;
+ }
+}
+
+ns_job_type_t
+ns_job_get_output_type(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state == NS_JOB_RUNNING);
+ if (job->state == NS_JOB_RUNNING) {
+ pthread_mutex_unlock(job->monitor);
+ return job->output_job_type;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return 0;
+ }
+}
+
+ns_job_type_t
+ns_job_get_type(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state != NS_JOB_DELETED);
+ if (job->state != NS_JOB_DELETED) {
+ pthread_mutex_unlock(job->monitor);
+ return job->job_type;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return 0;
+ }
+}
+
+PRFileDesc *
+ns_job_get_fd(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state != NS_JOB_DELETED);
+ if (job->state != NS_JOB_DELETED) {
+ pthread_mutex_unlock(job->monitor);
+ return job->fd;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return NULL;
+ }
+}
+
+PRStatus
+ns_job_set_done_cb(struct ns_job_t *job, ns_job_func_t func)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING );
+ if (job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING ) {
+ job->done_cb = func;
+ pthread_mutex_unlock(job->monitor);
+ return PR_SUCCESS;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return PR_FAILURE;
+ }
+}
+
+
+/*
+ * This is a convenience function - use if you need to re-arm the same event
+ * usually not needed for persistent jobs
+ */
+PRStatus
+ns_job_rearm(ns_job_t *job)
+{
+ PR_ASSERT(job);
+ pthread_mutex_lock(job->monitor);
+ PR_ASSERT(job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING);
+ if (job->state == NS_JOB_WAITING) {
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_rearm_job %x state %d moving to
NS_JOB_NEEDS_ARM\n", job, job->state);
+#endif
+ job->state = NS_JOB_NEEDS_ARM;
+ internal_ns_job_rearm(job);
+ pthread_mutex_unlock(job->monitor);
+ return PR_SUCCESS;
+ } else if ( !NS_JOB_IS_PERSIST(job->job_type) && job->state ==
NS_JOB_RUNNING) {
+ /* For this to be called, and NS_JOB_RUNNING, we *must* be the callback thread!
*/
+ /* Just mark it (ie do nothing), the work_job_execute function will trigger
internal_ns_job_rearm */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_rearm_job %x state %d setting
NS_JOB_NEEDS_ARM\n", job, job->state);
+#endif
+ job->state = NS_JOB_NEEDS_ARM;
+ pthread_mutex_unlock(job->monitor);
+ return PR_SUCCESS;
+ } else {
+ pthread_mutex_unlock(job->monitor);
+ return PR_FAILURE;
+ }
+ /* Unreachable code .... */
+ return PR_FAILURE;
+}
+
+static void
+ns_thrpool_delete(ns_thrpool_t *tp)
+{
+ ns_free(tp);
+}
+
+static ns_thrpool_t *
+ns_thrpool_alloc(void)
+{
+ ns_thrpool_t *tp;
+
+ tp = ns_calloc(1, sizeof(struct ns_thrpool_t));
+ if (NULL == tp) {
+ goto failed;
+ }
+
+ return tp;
+failed:
+ ns_thrpool_delete(tp);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+}
+
+/* libevent does not make public the file descriptors used to
+ wakeup epoll_wait - you have to send a signal - instead of
+ that just create our own wakeup pipe */
+static void
+setup_event_q_wakeup(ns_thrpool_t *tp)
+{
+ ns_job_t *job;
+ PR_CreatePipe(&tp->event_q_wakeup_pipe_read,
&tp->event_q_wakeup_pipe_write);
+ /* setting options is not supported on NSPR pipes - use fcntl
+ PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
+ PR_SetSocketOption(tp->event_q_wakeup_pipe_read, &prsod);
+ PR_SetSocketOption(tp->event_q_wakeup_pipe_write, &prsod);
+ */
+ if (fcntl(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_read), F_SETFD,
O_NONBLOCK) == -1) {
+ ns_log(LOG_ERR, "setup_event_q_wakeup(): could not make read pipe
non-blocking: %d\n",
+ PR_GetOSError());
+ }
+ if (fcntl(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_write), F_SETFD,
O_NONBLOCK) == -1) {
+ ns_log(LOG_ERR, "setup_event_q_wakeup(): could not make write pipe
non-blocking: %d\n",
+ PR_GetOSError());
+ }
+ /* wakeup events are processed inside the event loop thread */
+ job = alloc_io_context(tp, tp->event_q_wakeup_pipe_read,
+ NS_JOB_READ|NS_JOB_PERSIST|NS_JOB_PRESERVE_FD,
+ wakeup_cb, NULL);
+
+ pthread_mutex_lock(job->monitor);
+
+ /* The event_queue wakeup is ready, arm it. */
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "setup_event_q_wakeup %x state %d moving NS_JOB_ARMED\n",
job, job->state);
+#endif
+ job->state = NS_JOB_ARMED;
+
+ /* Now allow it to process events */
+ tp->ns_event_fw->ns_event_fw_add_io(tp->ns_event_fw_ctx, job);
+
+ /* Stash the wakeup job in tp so we can release it later. */
+ tp->event_q_wakeup_job = job;
+ pthread_mutex_unlock(job->monitor);
+}
+
+/* Initialize the thrpool config */
+#define NS_INIT_MAGIC 0xdefa014
+
+void
+ns_thrpool_config_init(struct ns_thrpool_config *tp_config)
+{
+ tp_config->init_flag = NS_INIT_MAGIC;
+ tp_config->max_threads = 1;
+ tp_config->stacksize = 0;
+ tp_config->log_fct = NULL;
+ tp_config->log_start_fct = NULL;
+ tp_config->log_close_fct = NULL;
+ tp_config->malloc_fct = NULL;
+ tp_config->memalign_fct = NULL;
+ tp_config->calloc_fct = NULL;
+ tp_config->realloc_fct = NULL;
+ tp_config->free_fct = NULL;
+}
+
+/*
+ * Process the config and set the pluggable function pointers
+ */
+static int
+ns_thrpool_process_config(struct ns_thrpool_config *tp_config)
+{
+ /* Check that the config has been properly initialized */
+ if (!tp_config || tp_config->init_flag != NS_INIT_MAGIC){
+ return -1;
+ }
+ /*
+ * Assign our logging function pointers
+ */
+ if (tp_config->log_fct){
+ /* Set a logger function */
+ logger = tp_config->log_fct;
+ if (tp_config->log_start_fct){
+ log_start = tp_config->log_start_fct;
+ }
+ if (tp_config->log_close_fct){
+ log_close = tp_config->log_close_fct;
+ }
+ } else {
+ /* Default to syslog */
+ logger = ns_syslog;
+ log_start = ns_syslog_start;
+ log_close = ns_syslog_close;
+ }
+ if(log_start){
+ /* Start logging */
+ (log_start)();
+ }
+
+ /*
+ * Set the memory function pointers
+ */
+ /* malloc */
+ if (tp_config->malloc_fct){
+ malloc_fct = tp_config->malloc_fct;
+ } else {
+ malloc_fct = os_malloc;
+ }
+
+ if (tp_config->memalign_fct){
+ memalign_fct = tp_config->memalign_fct;
+ } else {
+ memalign_fct = os_memalign_malloc;
+ }
+
+ /* calloc */
+ if (tp_config->calloc_fct){
+ calloc_fct = tp_config->calloc_fct;
+ } else {
+ calloc_fct = os_calloc;
+ }
+ /* realloc */
+ if (tp_config->realloc_fct){
+ realloc_fct = tp_config->realloc_fct;
+ } else {
+ realloc_fct = os_realloc;
+ }
+ /* free */
+ if (tp_config->free_fct){
+ free_fct = tp_config->free_fct;
+ } else {
+ free_fct = os_free;
+ }
+
+ return 0;
+}
+
+ns_thrpool_t *
+ns_thrpool_new(struct ns_thrpool_config *tp_config)
+{
+ pthread_attr_t attr;
+ ns_thrpool_t *tp = NULL;
+ ns_thread_t *thr;
+ size_t ii;
+
+ if(ns_thrpool_process_config(tp_config) == -1){
+ ns_log(LOG_ERR, "ns_thrpool_new(): config has not been properly
initialized\n");
+ goto failed;
+ }
+
+ tp = ns_thrpool_alloc();
+ if (NULL == tp) {
+ ns_log(LOG_ERR, "ns_thrpool_new(): failed to allocate thread pool\n");
+ goto failed;
+ }
+
+ ns_log(LOG_DEBUG, "ns_thrpool_new(): max threads, (%d)\n"
+ "stacksize (%d), event q size (unbounded), work q size
(unbounded)\n",
+ tp_config->max_threads, tp_config->stacksize);
+
+ tp->stacksize = tp_config->stacksize;
+
+ if (sds_queue_init(&(tp->thread_stack), NULL) != SDS_SUCCESS) {
+ goto failed;
+ }
+
+ if (pthread_mutex_init(&(tp->work_q_lock), NULL) != 0) {
+ goto failed;
+ }
+ if (pthread_cond_init(&(tp->work_q_cv), NULL) != 0) {
+ goto failed;
+ }
+
+ if (sds_lqueue_init(&(tp->work_q), job_queue_cleanup) != SDS_SUCCESS) {
+ goto failed;
+ }
+ if (sds_lqueue_init(&(tp->event_q), job_queue_cleanup) != SDS_SUCCESS) {
+ goto failed;
+ }
+
+ /* NGK TODO - add tevent vs. libevent switch */
+ /* tp->ns_event_fw = get_event_framework_tevent(); */
+ tp->ns_event_fw = get_event_framework_event();
+ tp->ns_event_fw_ctx = tp->ns_event_fw->ns_event_fw_init();
+
+ setup_event_q_wakeup(tp);
+
+ /* Create the thread attributes. */
+ if (pthread_attr_init(&attr) != 0) {
+ goto failed;
+ }
+ /* Setup the stack size. */
+ if (tp_config->stacksize > 0) {
+ if (pthread_attr_setstacksize(&attr, tp_config->stacksize) != 0) {
+ goto failed;
+ }
+ }
+
+ for (ii = 0; ii < tp_config->max_threads; ++ii) {
+ thr = ns_calloc(1, sizeof(ns_thread_t));
+ PR_ASSERT(thr);
+ thr->tp = tp;
+ assert(pthread_create(&(thr->thr), &attr, &worker_thread_func,
thr) == 0);
+ sds_queue_enqueue(tp->thread_stack, thr);
+ }
+
+ assert(pthread_create(&(tp->event_thread), &attr,
&event_loop_thread_func, tp) == 0);
+
+ /* We keep the event thread separate from the stack of worker threads. */
+ // tp->event_thread = event_thr;
+
+ return tp;
+failed:
+ ns_thrpool_destroy(tp);
+ return NULL;
+}
+
+void
+ns_thrpool_destroy(struct ns_thrpool_t *tp)
+{
+ void *retval = NULL;
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_thrpool_destroy\n");
+#endif
+ if (tp) {
+ /* Set the flag to shutdown the event loop. */
+ __atomic_add_fetch(&(tp->shutdown_event_loop), 1, __ATOMIC_SEQ_CST);
+
+ /* Finish the event queue wakeup job. This has the
+ * side effect of waking up the event loop thread, which
+ * will cause it to exit since we set the event loop
+ * shutdown flag. Fake the job to be a threaded job
+ * so that we can run it from outside the event loop,
+ * and use it to wake up the event loop.
+ */
+
+ pthread_mutex_lock(tp->event_q_wakeup_job->monitor);
+
+ // tp->event_q_wakeup_job->job_type |= NS_JOB_THREAD;
+ /* This triggers the job to "run", which will cause a shutdown cascade
*/
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_thrpool_destroy %x state %d moving to
NS_JOB_NEEDS_DELETE\n", tp->event_q_wakeup_job,
tp->event_q_wakeup_job->state);
+#endif
+ tp->event_q_wakeup_job->state = NS_JOB_NEEDS_DELETE;
+ pthread_mutex_unlock(tp->event_q_wakeup_job->monitor);
+ /* Has to be event_q_notify, not internal_job_done */
+ event_q_notify(tp->event_q_wakeup_job);
+
+ /* Wait for the event thread to finish before we free the
+ * internals of tp. */
+ int32_t rc = pthread_join(tp->event_thread, &retval);
+ if (rc != 0) {
+ ns_log(LOG_DEBUG, "Failed to join event thread %d\n", rc);
+ }
+
+ if (tp->work_q) {
+ sds_lqueue_destroy(tp->work_q);
+ }
+
+ if (tp->thread_stack) {
+ sds_queue_destroy(tp->thread_stack);
+ }
+
+ /* Free the work queue condition variable. */
+ pthread_cond_destroy(&(tp->work_q_cv));
+ /* Free the work queue lock. */
+ pthread_mutex_destroy(&(tp->work_q_lock));
+
+ if (tp->event_q) {
+ sds_lqueue_destroy(tp->event_q);
+ }
+
+ /* Free the event queue wakeup pipe/job. */
+ if (tp->event_q_wakeup_pipe_read) {
+ PR_Close(tp->event_q_wakeup_pipe_read);
+ tp->event_q_wakeup_pipe_read = NULL;
+ }
+
+ if (tp->event_q_wakeup_pipe_write) {
+ PR_Close(tp->event_q_wakeup_pipe_write);
+ tp->event_q_wakeup_pipe_write = NULL;
+ }
+ /* Already destroyed in the event queue shutdown */
+ tp->event_q_wakeup_job = NULL;
+
+ /* Free the event framework context. */
+ if (tp->ns_event_fw_ctx) {
+ tp->ns_event_fw->ns_event_fw_destroy(tp->ns_event_fw_ctx);
+ tp->ns_event_fw_ctx = NULL;
+ }
+
+ /* Free the thread pool struct itself. */
+ ns_thrpool_delete(tp);
+ }
+ /* Stop logging */
+ if(log_close){
+ (log_close)();
+ }
+}
+
+/* Triggers the pool of worker threads to shutdown after finishing the remaining work.
+ * This must be run in a worker thread, not the event thread. Running it in the event
+ * thread could cause a deadlock. */
+void
+ns_thrpool_shutdown(struct ns_thrpool_t *tp)
+{
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_thrpool_shutdown initiated ...\n");
+#endif
+ if (ns_thrpool_is_shutdown(tp) != 0) {
+ /* Already done! */
+ return;
+ }
+ /* Set the shutdown flag. This will cause the worker
+ * threads to exit after they finish all remaining work. */
+ __atomic_add_fetch(&(tp->shutdown), 1, __ATOMIC_SEQ_CST);
+
+ /* Wake up the idle worker threads so they can exit. */
+ pthread_mutex_lock(&(tp->work_q_lock));
+ pthread_cond_broadcast(&(tp->work_q_cv));
+ pthread_mutex_unlock(&(tp->work_q_lock));
+}
+
+PRStatus
+ns_thrpool_wait(ns_thrpool_t *tp)
+{
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_thrpool_wait has begun\n");
+#endif
+ PRStatus retval = PR_SUCCESS;
+ ns_thread_t *thr;
+
+ while (sds_queue_dequeue(tp->thread_stack, (void **)&thr) == SDS_SUCCESS)
+ {
+ void *retval = NULL;
+ int32_t rc = pthread_join(thr->thr, &retval);
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_thrpool_wait joined thread, result %d\n", rc);
+#endif
+ if (rc != 0) {
+ /* NGK TODO - this is unused right now. */
+ ns_log(LOG_ERR, "ns_thrpool_wait, failed to join thread %d", rc);
+ }
+ ns_free(thr);
+ }
+
+
+#ifdef DEBUG
+ ns_log(LOG_DEBUG, "ns_thrpool_wait complete, retval %d\n", retval);
+#endif
+ return retval;
+}
+
+/*
+ * nunc stans logger
+ */
+void
+ns_log(int priority, const char *fmt, ...)
+{
+ va_list varg;
+
+ va_start(varg, fmt);
+ ns_log_valist(priority, fmt, varg);
+ va_end(varg);
+
+}
+
+void
+ns_log_valist(int priority, const char *fmt, va_list varg)
+{
+ (logger)(priority, fmt, varg);
+}
+
+/*
+ * Pluggable memory functions
+ */
+void *
+ns_malloc(size_t size)
+{
+ return (malloc_fct)(size);
+}
+
+void *
+ns_memalign(size_t size, size_t alignment)
+{
+ return (memalign_fct)(size, alignment);
+}
+
+void *
+ns_calloc(size_t count, size_t size)
+{
+ return (calloc_fct)(count, size);
+}
+
+void *
+ns_realloc(void *ptr, size_t size)
+{
+ return (realloc_fct)(ptr, size);
+}
+
+void
+ns_free(void *ptr)
+{
+ (free_fct)(ptr);
+}
+
diff --git a/src/nunc-stans/ns/ns_tls.c b/src/nunc-stans/ns/ns_tls.c
new file mode 100644
index 0000000..4a6356e
--- /dev/null
+++ b/src/nunc-stans/ns/ns_tls.c
@@ -0,0 +1,291 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+#include "nspr.h"
+#include "prio.h"
+#include "secitem.h"
+#include "nss.h"
+#include "ssl.h"
+#include "cert.h"
+#include "pk11pub.h"
+#include "keyhi.h"
+
+#include "ns_tls.h"
+#include <syslog.h>
+#include "ns_private.h"
+
+struct ns_sec_ctx_t {
+ NSSInitContext *initctx;
+ NSSInitParameters init_params;
+ PRFileDesc *model_fd;
+ PRBool is_client;
+};
+
+typedef struct ns_sec_ctx_t ns_sec_ctx_t;
+
+static void
+errExit(const char *msg)
+{
+ ns_log(LOG_ERR, msg);
+}
+
+static PRFileDesc *
+create_tls_model_sock(const char *certname, PRBool is_client)
+{
+ PRFileDesc *model_sock = NULL;
+ int rv;
+ SECStatus secStatus;
+ PRBool disableSSL2 = PR_TRUE;
+ PRBool disableSSL3 = PR_FALSE;
+ PRBool disableTLS = PR_FALSE;
+ PRBool disableRollBack = PR_FALSE;
+ PRBool NoReuse = PR_FALSE;
+ PRBool disableStepDown = PR_FALSE;
+ PRBool bypassPKCS11 = PR_FALSE;
+ PRBool disableLocking = PR_FALSE;
+ PRBool enableFDX = PR_FALSE;
+ PRBool enableSessionTickets = PR_FALSE;
+ PRBool enableCompression = PR_FALSE;
+ SSLKEAType certKEA;
+
+ model_sock = PR_NewTCPSocket();
+ if (model_sock == NULL) {
+ errExit("PR_NewTCPSocket on model socket");
+ }
+ model_sock = SSL_ImportFD(NULL, model_sock);
+ if (model_sock == NULL) {
+ errExit("SSL_ImportFD");
+ }
+
+ /* do SSL configuration. */
+ rv = SSL_OptionSet(model_sock, SSL_SECURITY,
+ !(disableSSL2 && disableSSL3 && disableTLS));
+ if (rv != SECSuccess) {
+ errExit("SSL_OptionSet SSL_SECURITY");
+ }
+
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL3, !disableSSL3);
+ if (rv != SECSuccess) {
+ errExit("error enabling SSLv3 ");
+ }
+
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_TLS, !disableTLS);
+ if (rv != SECSuccess) {
+ errExit("error enabling TLS ");
+ }
+
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, !disableSSL2);
+ if (rv != SECSuccess) {
+ errExit("error enabling SSLv2 ");
+ }
+
+ rv = SSL_OptionSet(model_sock, SSL_ROLLBACK_DETECTION, !disableRollBack);
+ if (rv != SECSuccess) {
+ errExit("error enabling RollBack detection ");
+ }
+
+ rv = SSL_OptionSet(model_sock, SSL_HANDSHAKE_AS_CLIENT, is_client);
+ if (rv != SECSuccess) {
+ errExit("error handshake as client ");
+ }
+
+ rv = SSL_OptionSet(model_sock, SSL_HANDSHAKE_AS_SERVER, !is_client);
+ if (rv != SECSuccess) {
+ errExit("error handshake as server ");
+ }
+
+ if (disableStepDown) {
+ rv = SSL_OptionSet(model_sock, SSL_NO_STEP_DOWN, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error disabling SSL StepDown ");
+ }
+ }
+ if (bypassPKCS11) {
+ rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error enabling PKCS11 bypass ");
+ }
+ }
+ if (disableLocking) {
+ rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error disabling SSL socket locking ");
+ }
+ }
+ if (enableSessionTickets) {
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error enabling Session Ticket extension ");
+ }
+ }
+
+ if (enableCompression) {
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error enabling compression ");
+ }
+ }
+
+ /* handle ciphers here - SSL_CipherPrefSetDefault etc. */
+
+ /*
+ rv = SSL_SNISocketConfigHook(model_sock, mySSLSNISocketConfig,
+ (void*)&virtServerNameArray);
+ if (rv != SECSuccess) {
+ errExit("error enabling SNI extension ");
+ }
+ */
+
+ if (!is_client) {
+ CERTCertificate *cert;
+ SECKEYPrivateKey *privKey;
+
+ cert = PK11_FindCertFromNickname(certname, NULL);
+ if (cert == NULL) {
+ ns_log(LOG_ERR, "selfserv: Can't find certificate %s\n", certname);
+ exit(10);
+ }
+ privKey = PK11_FindKeyByAnyCert(cert, NULL);
+ if (privKey == NULL) {
+ ns_log(LOG_ERR, "selfserv: Can't find Private Key for cert %s\n",
certname);
+ exit(11);
+ }
+ certKEA = NSS_FindCertKEAType(cert);
+ secStatus = SSL_ConfigSecureServer(model_sock, cert, privKey, certKEA);
+ if (secStatus != SECSuccess) {
+ errExit("SSL_ConfigSecureServer");
+ }
+ CERT_DestroyCertificate(cert);
+ SECKEY_DestroyPrivateKey(privKey);
+ SSL_ConfigServerSessionIDCache(0, 0, 0, NULL);
+ }
+
+ if (enableFDX) { /* doing FDX */
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1);
+ if (rv != SECSuccess) {
+ errExit("SSL_OptionSet SSL_ENABLE_FDX");
+ }
+ }
+
+ if (NoReuse) {
+ rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1);
+ if (rv != SECSuccess) {
+ errExit("SSL_OptionSet SSL_NO_CACHE");
+ }
+ }
+
+ /*
+ if (expectedHostNameVal) {
+ SSL_HandshakeCallback(model_sock, handshakeCallback,
+ (void*)expectedHostNameVal);
+ }
+ */
+
+ /*
+ SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate,
+ (void *)CERT_GetDefaultCertDB());
+ */
+
+ rv = SSL_OptionSet(model_sock, SSL_REQUEST_CERTIFICATE, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("first SSL_OptionSet SSL_REQUEST_CERTIFICATE");
+ }
+ rv = SSL_OptionSet(model_sock, SSL_REQUIRE_CERTIFICATE, PR_FALSE);
+ if (rv != SECSuccess) {
+ errExit("first SSL_OptionSet SSL_REQUIRE_CERTIFICATE");
+ }
+
+ /* end of ssl configuration. */
+ return model_sock;
+}
+
+void
+ns_tls_done(ns_sec_ctx_t *ctx)
+{
+ if (ctx->model_fd) {
+ PR_Close(ctx->model_fd);
+ }
+ if (ctx->initctx) {
+ NSS_ShutdownContext(ctx->initctx);
+ }
+ ns_free(ctx);
+}
+
+ns_sec_ctx_t *
+ns_tls_init(const char *dir, const char *prefix, const char *certname, PRBool is_client)
+{
+ SECStatus rc = SECSuccess;
+ ns_sec_ctx_t *ctx = ns_calloc(1, sizeof(ns_sec_ctx_t));
+
+ ctx->init_params.length = sizeof(ctx->init_params);
+ ctx->initctx = NSS_InitContext(dir, prefix, prefix, SECMOD_DB,
+ &ctx->init_params, NSS_INIT_READONLY);
+ if (ctx->initctx == NULL) {
+ rc = SECFailure;
+ goto fail;
+ }
+
+ NSS_SetDomesticPolicy();
+
+ ctx->model_fd = create_tls_model_sock(certname, is_client);
+ if (ctx->model_fd == NULL) {
+ rc = SECFailure;
+ goto fail;
+ }
+
+fail:
+ if (rc != SECSuccess) {
+ ns_tls_done(ctx);
+ ctx = NULL;
+ }
+
+ return ctx;
+}
+
+PRErrorCode
+ns_add_sec_layer(PRFileDesc *fd, ns_sec_ctx_t *ctx)
+{
+ PRErrorCode err = PR_SUCCESS;
+
+ fd = SSL_ImportFD(ctx->model_fd, fd);
+ if (fd == NULL) {
+ err = PR_GetError();
+ goto fail;
+ }
+
+ if (SECSuccess != SSL_ResetHandshake(fd, !ctx->is_client)) {
+ err = PR_GetError();
+ goto fail;
+ }
+
+fail:
+ return err;
+}
diff --git a/src/nunc-stans/ns/ns_tls.h b/src/nunc-stans/ns/ns_tls.h
new file mode 100644
index 0000000..62edb19
--- /dev/null
+++ b/src/nunc-stans/ns/ns_tls.h
@@ -0,0 +1,49 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2015 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+#ifndef NS_TLS_H
+#define NS_TLS_H
+
+#include "nspr.h"
+#include "prerror.h"
+#include "prio.h"
+
+/* should be in sec_ctx.h */
+struct ns_sec_ctx_t;
+/* adds the sec layer _in place_ to fd - if error returned, the sec
+ layer is unstable and the fd should be closed and discarded */
+PRErrorCode ns_add_sec_layer(PRFileDesc *fd, struct ns_sec_ctx_t *ctx);
+
+/* tls specific functions */
+void ns_tls_done(struct ns_sec_ctx_t *ctx);
+struct ns_sec_ctx_t *ns_tls_init(const char *dir, const char *prefix, const char
*certname, PRBool isClient);
+
+#endif /* NS_TLS_H */
diff --git a/src/nunc-stans/test/test_nuncstans.c b/src/nunc-stans/test/test_nuncstans.c
new file mode 100644
index 0000000..8eef9e6
--- /dev/null
+++ b/src/nunc-stans/test/test_nuncstans.c
@@ -0,0 +1,418 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2016 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* For cmocka */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+/* For string and time manipulation in tests */
+#include <unistd.h>
+#include <string.h>
+
+/* For signaling tests */
+#include <signal.h>
+
+/* For NS itself */
+#include <nunc-stans.h>
+/* We need the internal headers for state checks */
+#include "../ns/ns_event_fw.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+
+static int cb_check = 0;
+static PRLock *cb_lock = NULL;
+static PRCondVar *cb_cond = NULL;
+
+void
+ns_test_logger(int priority __attribute__((unused)), const char *fmt, va_list varg)
+{
+ // Should we do anything with priority?
+ vprintf(fmt, varg);
+}
+
+/* All our other tests will use this in some form. */
+static int
+ns_test_setup(void **state)
+{
+ /* Ensure that we can create a new nunc-stans instance. */
+ struct ns_thrpool_t *tp = NULL;
+ struct ns_thrpool_config ns_config;
+ /* Reset the callback check */
+ cb_check = 0;
+ /* Create the cond var the CB check will use. */
+ cb_lock = PR_NewLock();
+ cb_cond = PR_NewCondVar(cb_lock);
+
+ ns_thrpool_config_init(&ns_config);
+
+ ns_config.log_fct = ns_test_logger;
+ ns_config.max_threads = 4;
+ tp = ns_thrpool_new(&ns_config);
+ assert_non_null(tp);
+
+ *state = tp;
+
+ return 0;
+}
+
+static int
+ns_test_teardown(void **state)
+{
+ struct ns_thrpool_t *tp = *state;
+ ns_thrpool_shutdown(tp);
+ assert_int_equal(ns_thrpool_wait(tp), 0);
+
+ ns_thrpool_destroy(tp);
+
+ PR_DestroyCondVar(cb_cond);
+ PR_DestroyLock(cb_lock);
+
+ return 0;
+}
+
+static void
+ns_init_test_job_cb(struct ns_job_t *job __attribute__((unused)))
+{
+ cb_check += 1;
+ PR_Lock(cb_lock);
+ PR_NotifyCondVar(cb_cond);
+ PR_Unlock(cb_lock);
+}
+
+static void
+ns_init_disarm_job_cb(struct ns_job_t *job)
+{
+ if (ns_job_done(job) == PR_SUCCESS) {
+ cb_check = 1;
+ } else {
+ assert_int_equal(1,0);
+ }
+ PR_Lock(cb_lock);
+ PR_NotifyCondVar(cb_cond);
+ /* Disarm ourselves */
+ PR_Unlock(cb_lock);
+}
+
+static void
+ns_init_do_nothing_cb(struct ns_job_t *job __attribute__((unused)))
+{
+ /* I mean it, do nothing! */
+ return;
+}
+
+static void
+ns_init_test(void **state)
+{
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ PR_Lock(cb_lock);
+ assert_int_equal(
+ ns_add_job(tp, NS_JOB_NONE|NS_JOB_THREAD, ns_init_test_job_cb, NULL, &job),
+ 0);
+
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+
+ assert_int_equal(cb_check, 1);
+
+ /* Once the job is done, it's not in the event queue, and it's complete */
+ /* We have to stall momentarily to let the work_job_execute release the job to us */
+ PR_Sleep(PR_SecondsToInterval(1));
+ assert_int_equal(ns_job_done(job), 0);
+}
+
+static void
+ns_set_data_test(void **state)
+{
+ /* Add a job with data */
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ char *data = malloc(6);
+
+ strcpy(data, "first");
+
+ PR_Lock(cb_lock);
+ assert_int_equal(
+ ns_add_job(tp, NS_JOB_NONE|NS_JOB_THREAD, ns_init_test_job_cb, data, &job),
+ 0);
+
+ /* Let the job run */
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+
+ /* Check that the data is correct */
+ char *retrieved = (char *)ns_job_get_data(job);
+ assert_int_equal(strcmp("first", retrieved), 0);
+
+ free(retrieved);
+
+ /* set new data */
+ data = malloc(7);
+ strcpy(data, "second");
+
+ while (job->state != NS_JOB_WAITING) {
+ PR_Sleep(PR_MillisecondsToInterval(50));
+ }
+ ns_job_set_data(job, data);
+
+ /* Rearm, and let it run again. */
+ PR_Lock(cb_lock);
+ ns_job_rearm(job);
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+
+ /* Make sure it's now what we expect */
+ retrieved = (char *)ns_job_get_data(job);
+ assert_int_equal(strcmp("second", retrieved), 0);
+
+ free(retrieved);
+
+ /* Because the job is not queued, we must free it */
+ /*
+ * It's possible here, that the worker thread is still processing state
+ * as a result, we aren't the owning thread, and ns_job_done fails.
+ * So we actually have to loop on freeing this until it's released to
+ * waiting. we might need a load barrier here ...
+ */
+
+ while (job->state != NS_JOB_WAITING) {
+ PR_Sleep(PR_MillisecondsToInterval(50));
+ }
+
+ assert_int_equal(ns_job_done(job), 0);
+}
+
+static void
+ns_job_done_cb_test(void **state)
+{
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ PR_Lock(cb_lock);
+ assert_int_equal(
+ ns_create_job(tp, NS_JOB_NONE|NS_JOB_THREAD, ns_init_do_nothing_cb, &job),
+ 0);
+
+ ns_job_set_done_cb(job, ns_init_test_job_cb);
+ /* Remove it */
+ assert_int_equal(ns_job_done(job), 0);
+
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+
+ assert_int_equal(cb_check, 1);
+
+}
+
+static void
+ns_init_rearm_job_cb(struct ns_job_t *job)
+{
+ if (ns_job_rearm(job) == PR_FAILURE) {
+ cb_check = 1;
+ /* we failed to re-arm as expected, let's go away ... */
+ assert_int_equal(ns_job_done(job), 0);
+ } else {
+ assert_int_equal(1, 0);
+ }
+ PR_Lock(cb_lock);
+ PR_NotifyCondVar(cb_cond);
+ /* Disarm ourselves */
+ PR_Unlock(cb_lock);
+}
+
+static void
+ns_job_persist_rearm_ignore_test(void **state)
+{
+ /* Test that rearm ignores the persistent job. */
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ PR_Lock(cb_lock);
+ assert_int_equal(
+ ns_create_job(tp, NS_JOB_NONE|NS_JOB_THREAD|NS_JOB_PERSIST, ns_init_rearm_job_cb,
&job),
+ 0);
+
+ /* This *will* arm the job, and will trigger the cb. */
+ assert_int_equal(ns_job_rearm(job), 0);
+ /*
+ * Now when the CB fires, it will *try* to rearm, but will fail, so we
+ * should see only 1 in the cb_check.
+ */
+
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+
+ /* If we fail to rearm, this is set to 1 Which is what we want. */
+ assert_int_equal(cb_check, 1);
+
+}
+
+static void
+ns_job_persist_disarm_test(void **state)
+{
+ /* Make a persistent job */
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ assert_int_equal(
+ ns_create_job(tp, NS_JOB_NONE|NS_JOB_PERSIST, ns_init_disarm_job_cb, &job),
+ 0);
+
+ assert_int_equal(ns_job_rearm(job), 0);
+
+ /* In the callback it should disarm */
+ PR_Lock(cb_lock);
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+ /* Make sure it did */
+ assert_int_equal(cb_check, 1);
+}
+
+/*
+ * This tests a very specific issue in the directory server code. It's possible
+ * that a job will try to disarm itself from within the worker thread. This can
+ * race, as the event thread will get the work, free the job. The worker then
+ * returns from the fn, and will heap-use-after-free because the job was
+ * yanked from under it. To test this, you *will* need ASAN enabled to detect
+ * the failure condition (it will crash and burn)
+ *
+ * The bug happens in ns_add_job, when it calls the cb.
+ * The moment we add this, it will then trigger the job to run in thrpool event_cb
+ * this of course will call job->func(job), in this case ns_init_race_done_job_cb.
+ * Because the race_done job sends to ns_job_done, this will free job in the event
+ * thread, but then the work thread will return after the PR_Sleep to event_cb.
+ * At this point, the wt attempts to access job->type and job->state, to determine
+ * if the job needs rearm. Because the et freed job, this is now a use after
+ * free.
+ */
+static void
+ns_init_race_done_job_cb(struct ns_job_t *job)
+{
+ cb_check += 1;
+ ns_job_done(job);
+ /* We need to sleep to let the job race happen */
+ PR_Sleep(PR_SecondsToInterval(2));
+ PR_Lock(cb_lock);
+ PR_NotifyCondVar(cb_cond);
+ /* Disarm ourselves */
+ PR_Unlock(cb_lock);
+}
+
+static void
+ns_job_race_done_test(void **state)
+{
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ PR_Lock(cb_lock);
+ assert_int_equal(
+ ns_add_job(tp, NS_JOB_NONE|NS_JOB_THREAD, ns_init_race_done_job_cb, NULL,
&job),
+ 0);
+
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(5));
+ PR_Unlock(cb_lock);
+
+ assert_int_equal(cb_check, 1);
+
+}
+
+/*
+ * This tests that when we raise a signal, we catch it and handle it correctly.
+ */
+
+static void
+ns_job_signal_cb_test(void **state)
+{
+ struct ns_thrpool_t *tp = *state;
+ struct ns_job_t *job = NULL;
+
+ PR_Lock(cb_lock);
+ assert_int_equal(
+ ns_add_signal_job(tp, SIGUSR1, NS_JOB_SIGNAL, ns_init_test_job_cb, NULL,
&job),
+ 0);
+
+ /* The addition of the signal job to the event fw is async */
+ PR_Sleep(PR_SecondsToInterval(2));
+ /* Send the signal ... */
+ raise(SIGUSR1);
+
+ PR_WaitCondVar(cb_cond, PR_SecondsToInterval(1));
+ PR_Unlock(cb_lock);
+
+ assert_int_equal(cb_check, 1);
+
+ /* Remove the signal job now */
+ assert_int_equal(ns_job_done(job), 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(ns_init_test,
+ ns_test_setup,
+ ns_test_teardown),
+ cmocka_unit_test_setup_teardown(ns_set_data_test,
+ ns_test_setup,
+ ns_test_teardown),
+ cmocka_unit_test_setup_teardown(ns_job_done_cb_test,
+ ns_test_setup,
+ ns_test_teardown),
+ cmocka_unit_test_setup_teardown(ns_job_persist_rearm_ignore_test,
+ ns_test_setup,
+ ns_test_teardown),
+ cmocka_unit_test_setup_teardown(ns_job_persist_disarm_test,
+ ns_test_setup,
+ ns_test_teardown),
+ cmocka_unit_test_setup_teardown(ns_job_race_done_test,
+ ns_test_setup,
+ ns_test_teardown),
+ cmocka_unit_test_setup_teardown(ns_job_signal_cb_test,
+ ns_test_setup,
+ ns_test_teardown),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+
+
diff --git a/src/nunc-stans/test/test_nuncstans_stress.c
b/src/nunc-stans/test/test_nuncstans_stress.c
new file mode 100644
index 0000000..29585d5
--- /dev/null
+++ b/src/nunc-stans/test/test_nuncstans_stress.c
@@ -0,0 +1,558 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * Copyright (C) 2016 Red Hat
+ * see files 'COPYING' and 'COPYING.openssl' for use and warranty
+ * information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <
http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL, or a modified version of OpenSSL licensed
+ * under the OpenSSL license
+ * (
https://www.openssl.org/source/license.html), the licensors of this
+ * Program grant you additional permission to convey the resulting
+ * work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts that are
+ * licensed under the OpenSSL license as well as that of the covered
+ * work.
+ * --- END COPYRIGHT BLOCK ---
+ */
+
+/*
+ * A self hosting echo server, that stress tests job addition,
+ * removal, timers, and more.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* For cmocka */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+/*
+#include <nspr.h>
+#include <plstr.h>
+#include <prlog.h>
+*/
+
+#include <nunc-stans.h>
+
+#include <stdio.h>
+#include <signal.h>
+
+#include <syslog.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+#include <assert.h>
+
+struct conn_ctx {
+ size_t offset; /* current offset into buffer for reading or writing */
+ size_t len; /* size of buffer */
+ size_t needed; /* content-length + start of body */
+ size_t body; /* when reading, offset from buffer of beginning of http body */
+ size_t cl; /* http content-length when reading */
+#define CONN_BUFFER_SIZE BUFSIZ /* default buffer size */
+ char *buffer;
+};
+
+static FILE *logfp;
+
+void do_logging(int, const char*, ...);
+
+int32_t client_success_count = 0;
+int32_t server_success_count = 0;
+int32_t client_fail_count = 0;
+int32_t client_timeout_count = 0;
+int32_t server_fail_count = 0;
+int32_t job_count = 0;
+int32_t client_thread_count = 80;
+int32_t server_thread_count = 20;
+int32_t jobs = 200;
+int32_t test_timeout = 70;
+
+#define PR_WOULD_BLOCK(iii) (iii == PR_PENDING_INTERRUPT_ERROR) || (iii ==
PR_WOULD_BLOCK_ERROR)
+
+static void
+setup_logging( void )
+{
+ logfp = stdout;
+}
+
+static void
+do_vlogging(int level, const char *format, va_list varg)
+{
+#ifdef DEBUG
+ if (level <= LOG_DEBUG) {
+#else
+ if (level <= LOG_ERR) {
+#endif
+ fprintf(logfp, "%d ", level);
+ vfprintf(logfp, format, varg);
+ }
+}
+
+void
+do_logging(int level, const char *format, ...)
+{
+ va_list varg;
+ va_start(varg, format);
+ do_vlogging(level, format, varg);
+ va_end(varg);
+}
+
+/* Server specifics */
+
+static struct conn_ctx *
+conn_ctx_new( void )
+{
+ struct conn_ctx *connctx = calloc(1, sizeof(struct conn_ctx));
+ return connctx;
+}
+
+static void
+conn_ctx_free(struct conn_ctx *connctx)
+{
+ /* Why don't we use PR_DELETE here? */
+ if (connctx->buffer != NULL) {
+ free(connctx->buffer);
+ }
+ free(connctx);
+
+}
+
+static void
+server_conn_write(struct ns_job_t *job)
+{
+ struct conn_ctx *connctx;
+ int32_t len;
+
+ do_logging(LOG_DEBUG, "job about to write ...\n");
+ assert(job != NULL);
+ connctx = (struct conn_ctx *)ns_job_get_data(job);
+ assert(connctx != NULL);
+ if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+ do_logging(LOG_ERR, "conn_write: job [%p] timeout\n", job);
+ __atomic_add_fetch(&server_fail_count, 1, __ATOMIC_SEQ_CST);
+ conn_ctx_free(connctx);
+ assert_int_equal(ns_job_done(job), 0);
+ return;
+ }
+
+ /* Get the data out of our connctx */
+ char *data = calloc(1, sizeof(char) * (connctx->offset + 1));
+ memcpy(data, connctx->buffer, connctx->offset);
+ data[connctx->offset] = '\0';
+
+ /* Should I write a new line also */
+ len = PR_Write(ns_job_get_fd(job), data, connctx->offset);
+
+ /* Set the buffer window back to the start */
+ connctx->offset = 0;
+
+ if (len < 0) {
+ /* Error */
+ printf("ERROR: occured in conn_write\n");
+ }
+ /* After we finish writing, do we stop being a thread? */
+ do_logging(LOG_DEBUG, "Wrote \"%s\"\n", data);
+ free(data);
+ /* The job is still a *read* IO event job, so this should be okay */
+ assert_int_equal(ns_job_rearm(job), 0);
+ return;
+}
+
+static void
+server_conn_read(struct ns_job_t *job)
+{
+ do_logging(LOG_DEBUG, "Reading from connection\n");
+
+ struct conn_ctx *connctx;
+ int32_t len;
+ int32_t nbytes;
+
+ assert(job != NULL);
+ connctx = (struct conn_ctx *)ns_job_get_data(job);
+ assert(connctx != NULL);
+
+ if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+ /* The event that triggered this call back is because we timed out waiting for IO
*/
+ do_logging(LOG_ERR, "conn_read: job [%p] timed out\n", job);
+ __atomic_add_fetch(&server_fail_count, 1, __ATOMIC_SEQ_CST);
+ conn_ctx_free(connctx);
+ assert_int_equal(ns_job_done(job), 0);
+ return;
+ }
+
+ if (connctx->needed != 0) {
+ nbytes = connctx->needed - connctx->offset;
+ } else {
+ nbytes = CONN_BUFFER_SIZE;
+ }
+
+ /* If our buffer is incorrectly sized, realloc it to match what we are about to read
*/
+ if ((nbytes + connctx->offset) > connctx->len) {
+ connctx->len = nbytes + connctx->offset;
+ connctx->buffer = (char *)PR_Realloc(connctx->buffer, connctx->len *
sizeof(char));
+ }
+
+ /* Read and append to our buffer */
+ len = PR_Read(ns_job_get_fd(job), connctx->buffer + connctx->offset, nbytes);
+ if (len < 0) {
+ PRErrorCode prerr = PR_GetError();
+ if (PR_WOULD_BLOCK(prerr)) {
+ /* We don't have poll, so we rearm the job for more data */
+ if (NS_JOB_IS_PERSIST(ns_job_get_type(job)) != 0) {
+ assert_int_equal(ns_job_rearm(job), 0);
+ }
+ do_logging(LOG_ERR, "conn_read: block error for job [%p] %d: %s\n",
job, PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
+ return;
+ } else {
+ do_logging(LOG_ERR, "conn_read: read error for job [%p] %d: %s\n",
job, PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
+ __atomic_add_fetch(&server_fail_count, 1, __ATOMIC_SEQ_CST);
+ conn_ctx_free(connctx);
+ assert_int_equal(ns_job_done(job), 0);
+ return;
+ }
+ /* Error */
+ } else if (len == 0) {
+ /* Didn't read anything */
+ do_logging(LOG_DEBUG, "conn_read: job [%p] closed\n", job);
+ /* Increment the success */
+ __atomic_add_fetch(&server_success_count, 1, __ATOMIC_SEQ_CST);
+ conn_ctx_free(connctx);
+ assert_int_equal(ns_job_done(job), 0);
+ return;
+ } else {
+ /* Wild data appears! */
+ connctx->offset += len;
+ char *data = PR_Malloc(sizeof(char) * (connctx->offset + 1));
+ memcpy(data, connctx->buffer, connctx->offset);
+ data[connctx->offset] = '\0';
+ do_logging(LOG_DEBUG, "[%p] data received \n", job);
+ do_logging(LOG_DEBUG, "data: \"%s\" \n", data);
+ free(data);
+ server_conn_write(job);
+ do_logging(LOG_DEBUG, "job rearmed for write ...\n");
+ return;
+ }
+}
+
+static void
+server_conn_handler(struct ns_job_t *job)
+{
+ do_logging(LOG_DEBUG, "Handling a connection\n");
+
+ assert(job != NULL);
+
+ if (NS_JOB_IS_READ(ns_job_get_type(job)) != 0) {
+ server_conn_read(job);
+ } else {
+ /* We should not be able to get here! */
+ assert(0);
+ }
+
+ return;
+}
+
+static void
+server_listen_accept(struct ns_job_t *job)
+{
+ PRFileDesc *connfd = NULL;
+ struct conn_ctx *connctx;
+ PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
+
+ PR_ASSERT(job);
+
+ PRFileDesc *listenfd = ns_job_get_fd(job);
+ PR_ASSERT(listenfd);
+ connfd = PR_Accept(listenfd, NULL, PR_INTERVAL_NO_WAIT);
+
+ if (connfd != NULL) {
+ PR_SetSocketOption(connfd, &prsod);
+ connctx = conn_ctx_new();
+
+ assert_int_equal(ns_add_io_job(ns_job_get_tp(job), connfd,
NS_JOB_READ|NS_JOB_THREAD, server_conn_handler, connctx, NULL), 0);
+
+ do_logging(LOG_DEBUG, "server_listen_accept: accepting connection to job
[%p]\n", job);
+
+ } else {
+ PRErrorCode prerr = PR_GetError();
+ if (PR_WOULD_BLOCK(prerr)) {
+ /* Let it go .... let it gooooo! */
+ /* Can't hold up connection dispatch anymore! */
+ } else {
+ do_logging(LOG_ERR, "server_listen_accept: accept error for job [%p] %d
%s\n", job, prerr, PR_ErrorToString(prerr, PR_LANGUAGE_I_DEFAULT));
+ }
+ }
+}
+
+static struct ns_job_t *
+server_listener_init(ns_thrpool_t *tp, PRFileDesc *listenfd)
+{
+ struct ns_job_t *listen_job = NULL;
+ ns_add_io_job(tp, listenfd, NS_JOB_ACCEPT|NS_JOB_PERSIST, server_listen_accept, NULL,
&listen_job);
+ return listen_job;
+}
+
+/* Client specifics */
+
+static void
+test_client_shutdown(struct ns_job_t *job)
+{
+ do_logging(LOG_DEBUG, "Received shutdown signal\n");
+ do_logging(LOG_DEBUG, "status .... job_count: %d fail_count: %d success_count:
%d\n", job_count, client_fail_count, client_success_count);
+ ns_thrpool_shutdown(ns_job_get_tp(job));
+ /* This also needs to start the thrpool shutdown for the server. */
+ ns_thrpool_shutdown(ns_job_get_data(job));
+
+}
+
+static void
+client_response_cb(struct ns_job_t *job)
+{
+
+ char *buffer = calloc(1, 20);
+ int32_t buflen = 20;
+ int32_t len = 0;
+
+ len = PR_Read(ns_job_get_fd(job), buffer, buflen);
+ if (len < 0) {
+ /* PRErrorCode prerr = PR_GetError(); */
+ do_logging(LOG_ERR, "FAIL: connection error, no data \n");
+ __atomic_add_fetch(&client_fail_count, 1, __ATOMIC_SEQ_CST);
+ goto done;
+ } else if (len == 0) {
+ do_logging(LOG_ERR, "FAIL: connection closed, no data \n");
+ __atomic_add_fetch(&client_fail_count, 1, __ATOMIC_SEQ_CST);
+ goto done;
+ } else {
+ /* Be paranoid, force last byte null */
+ buffer[buflen - 1] = '\0';
+ if (strncmp("this is a test!\n", buffer, strlen("this is a
test!\n")) != 0)
+ {
+ do_logging(LOG_ERR, "FAIL: connection incorrect response, no data
\n");
+ __atomic_add_fetch(&client_fail_count, 1, __ATOMIC_SEQ_CST);
+ goto done;
+ }
+ }
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ __atomic_add_fetch(&client_success_count, 1, __ATOMIC_SEQ_CST);
+ do_logging(LOG_ERR, "PASS: %ld.%ld %d\n", ts.tv_sec, ts.tv_nsec,
client_success_count);
+
+done:
+ free(buffer);
+ assert_int_equal(ns_job_done(job), 0);
+}
+
+static void
+client_initiate_connection_cb(struct ns_job_t *job)
+{
+ /* Create a socket */
+ PRFileDesc *sock = NULL;
+ PRNetAddr netaddr = {{0}};
+ char *data = "this is a test!\n";
+
+ sock = PR_OpenTCPSocket(PR_AF_INET6);
+ if (sock == NULL) {
+ do_logging(LOG_ERR, "Socket failed\n");
+ PR_AtomicAdd(&client_fail_count, 1);
+ goto done;
+ }
+
+ PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, 12345, &netaddr);
+
+ /* Connect */
+ /* */
+ if (PR_Connect(sock, &netaddr, PR_SecondsToInterval(5)) != 0) {
+ /* char *err = malloc(PR_GetErrorTextLength()); */
+ char *err = NULL;
+ PR_GetErrorText(err);
+ do_logging(LOG_ERR, "FAIL: cannot connect, timeout %d -> %s \n",
PR_GetError(), err);
+ /* Atomic increment fail */
+ __atomic_add_fetch(&client_timeout_count, 1, __ATOMIC_SEQ_CST);
+
+ if (sock != NULL) {
+ PR_Close(sock);
+ }
+ goto done;
+ }
+ /* Now write data. */
+ PR_Write(sock, data, strlen(data) + 1);
+ /* create the read job to respond to events on the socket. */
+ assert_int_equal(ns_add_io_job(ns_job_get_tp(job), sock, NS_JOB_READ|NS_JOB_THREAD,
client_response_cb, NULL, NULL), 0);
+
+done:
+ assert_int_equal(ns_job_done(job), 0);
+}
+
+static void
+client_create_work(struct ns_job_t *job)
+{
+ struct timespec ts;
+ PR_Sleep(PR_SecondsToInterval(1));
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ printf("BEGIN: %ld.%ld\n", ts.tv_sec, ts.tv_nsec);
+ for (int32_t i = 0; i < jobs; i++) {
+ assert_int_equal(ns_add_job(ns_job_get_tp(job), NS_JOB_NONE|NS_JOB_THREAD,
client_initiate_connection_cb, NULL, NULL), 0);
+ }
+ assert_int_equal(ns_job_done(job), 0);
+
+ printf("Create work thread complete!\n");
+}
+
+static void
+ns_stress_test(void **state __attribute__((unused)))
+{
+
+ /* Setup both thread pools. */
+
+ /* Client first */
+
+ int32_t job_count = jobs * client_thread_count;
+ struct ns_thrpool_t *ctp;
+ struct ns_thrpool_config client_ns_config;
+ struct ns_job_t *sigterm_job = NULL;
+ struct ns_job_t *sighup_job = NULL;
+ struct ns_job_t *sigint_job = NULL;
+ struct ns_job_t *sigtstp_job = NULL;
+ struct ns_job_t *sigusr1_job = NULL;
+ struct ns_job_t *sigusr2_job = NULL;
+ struct ns_job_t *final_job = NULL;
+
+ struct timeval timeout = { test_timeout, 0 };
+
+ setup_logging();
+
+ ns_thrpool_config_init(&client_ns_config);
+ client_ns_config.max_threads = client_thread_count;
+ client_ns_config.log_fct = do_vlogging;
+ ctp = ns_thrpool_new(&client_ns_config);
+
+ /* Now the server */
+
+ struct ns_thrpool_t *stp;
+ struct ns_thrpool_config server_ns_config;
+ struct ns_job_t *listen_job = NULL;
+
+ ns_thrpool_config_init(&server_ns_config);
+ server_ns_config.max_threads = server_thread_count;
+ server_ns_config.log_fct = do_vlogging;
+ stp = ns_thrpool_new(&server_ns_config);
+
+ /* Now, add the signal handlers. */
+
+ assert_int_equal(ns_add_signal_job(ctp, SIGTERM, NS_JOB_PERSIST,
test_client_shutdown, stp, &sigterm_job), 0);
+ assert_int_equal(ns_add_signal_job(ctp, SIGHUP, NS_JOB_PERSIST,
test_client_shutdown, stp, &sighup_job), 0);
+ assert_int_equal(ns_add_signal_job(ctp, SIGINT, NS_JOB_PERSIST,
test_client_shutdown, stp, &sigint_job), 0);
+ assert_int_equal(ns_add_signal_job(ctp, SIGTSTP, NS_JOB_PERSIST,
test_client_shutdown, stp, &sigtstp_job), 0);
+ assert_int_equal(ns_add_signal_job(ctp, SIGUSR1, NS_JOB_PERSIST,
test_client_shutdown, stp, &sigusr1_job), 0);
+ assert_int_equal(ns_add_signal_job(ctp, SIGUSR2, NS_JOB_PERSIST,
test_client_shutdown, stp, &sigusr2_job), 0);
+
+ /* Create the socket for the server, and set it to listen. */
+
+ PRFileDesc *listenfd = NULL;
+ PRNetAddr netaddr;
+ PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
+
+ PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 12345, &netaddr);
+
+ listenfd = PR_OpenTCPSocket(PR_NetAddrFamily(&netaddr));
+ PR_SetSocketOption(listenfd, &prsod);
+ prsod.option = PR_SockOpt_Reuseaddr;
+ PR_SetSocketOption(listenfd, &prsod);
+ PR_Bind(listenfd, &netaddr);
+ PR_Listen(listenfd, 32);
+
+ listen_job = server_listener_init(stp, listenfd);
+
+ /* Add the timeout. */
+
+ assert_int_equal(ns_add_timeout_job(ctp, &timeout, NS_JOB_NONE|NS_JOB_THREAD,
test_client_shutdown, stp, &final_job), 0);
+
+ /* While true, add connect / write jobs */
+ for (PRInt32 i = 0; i < client_thread_count; i++) {
+ assert_int_equal(ns_add_job(ctp, NS_JOB_NONE|NS_JOB_THREAD, client_create_work,
NULL, NULL), 0);
+ }
+
+ /* Wait for all the clients to be done dispatching jobs to the server */
+
+ if (ns_thrpool_wait(ctp) != 0) {
+ printf("Error in ctp?\n");
+ }
+
+ if (ns_thrpool_wait(stp) != 0) {
+ printf("Error in stp?\n");
+ }
+
+ /* Can mark as done becaus shutdown has begun. */
+ assert_int_equal(ns_job_done(listen_job), 0);
+
+
+ assert_int_equal(ns_job_done(sigterm_job), 0);
+ assert_int_equal(ns_job_done(sighup_job), 0);
+ assert_int_equal(ns_job_done(sigint_job), 0);
+ assert_int_equal(ns_job_done(sigtstp_job), 0);
+ assert_int_equal(ns_job_done(sigusr1_job), 0);
+ assert_int_equal(ns_job_done(sigusr2_job), 0);
+ assert_int_equal(ns_job_done(final_job), 0);
+
+ ns_thrpool_destroy(stp);
+ ns_thrpool_destroy(ctp);
+
+ /* Destroy the server thread pool. */
+
+ if (client_success_count != job_count) {
+ do_logging(LOG_ERR, "FAIL, not all client jobs succeeded!\n");
+ }
+ if (server_success_count != job_count) {
+ do_logging(LOG_ERR, "FAIL, not all server jobs succeeded!\n");
+ }
+ do_logging(LOG_ERR, "job_count: %d client_fail_count: %d client_timeout_count:
%d server_fail_count: %d client_success_count: %d server_success_count: %d\n",
job_count, client_fail_count, client_timeout_count, server_fail_count,
client_success_count, server_success_count);
+
+ assert_int_equal(client_fail_count, 0);
+ /* We don't check the client timeout count, because it's often non 0, and
it's generally because we *really* overwhelm the framework, we can handle the loss */
+ /* assert_int_equal(client_timeout_count, 0); */
+ assert_int_equal(server_fail_count, 0);
+ /* Can't assert this due to timeout: instead guarantee if we connect to the
server, we WORKED 100% of the time. */
+ /*
+ assert_int_equal(server_success_count, job_count);
+ assert_int_equal(client_success_count, job_count);
+ */
+ assert_int_equal(server_success_count, client_success_count);
+
+ PR_Cleanup();
+}
+
+
+int
+main (void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(ns_stress_test),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
diff --git a/src/pkgconfig/dirsrv.pc.in b/src/pkgconfig/dirsrv.pc.in
new file mode 100644
index 0000000..4140031
--- /dev/null
+++ b/src/pkgconfig/dirsrv.pc.in
@@ -0,0 +1,7 @@
+ldaplib=@ldaplib@
+
+Name: dirsrv
+Description: 389 Directory Server
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir}/@serverdir@ -lslapd
+Cflags: @ldaplib_defs@ -I${includedir}/@serverincdir@
diff --git a/src/pkgconfig/libsds.pc.in b/src/pkgconfig/libsds.pc.in
new file mode 100644
index 0000000..ae4243b
--- /dev/null
+++ b/src/pkgconfig/libsds.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/sds
+
+Name: Libsds
+Description: Libsds is the Slapi Data Structures Library.
+Version: @PACKAGE_VERSION@
+Requires: nspr
+Libs: -lsds -L${libdir}/@serverdir@
+Cflags: -I${includedir}/@serverincdir@
+
diff --git a/src/pkgconfig/nunc-stans.pc.in b/src/pkgconfig/nunc-stans.pc.in
new file mode 100644
index 0000000..127f330
--- /dev/null
+++ b/src/pkgconfig/nunc-stans.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Nunc Stans
+Description: Thread pool based event notification library
+Version: @PACKAGE_VERSION@
+Requires: nspr
+Libs: -L${libdir}/@serverdir@ -lnunc-stans
+Cflags: -I${includedir}/@serverincdir@
diff --git a/test/libslapd/operation/v3_compat.c b/test/libslapd/operation/v3_compat.c
index c69a670..4301a67 100644
--- a/test/libslapd/operation/v3_compat.c
+++ b/test/libslapd/operation/v3_compat.c
@@ -49,5 +49,7 @@ test_libslapd_operation_v3c_target_spec(void **state
__attribute__((unused))) {
/* free everything */
slapi_sdn_free(&test_a_sdn);
slapi_sdn_free(&a_sdn);
- slapi_sdn_free(&b_sdn);
+ /* target_spec in now the b_sdn, so operation free will free it */
+ // slapi_sdn_free(&b_sdn);
+ operation_free(&op, NULL);
}
diff --git a/test/libslapd/pblock/v3_compat.c b/test/libslapd/pblock/v3_compat.c
index 66c167c..bd22a8b 100644
--- a/test/libslapd/pblock/v3_compat.c
+++ b/test/libslapd/pblock/v3_compat.c
@@ -56,6 +56,9 @@ test_libslapd_pblock_v3c_target_dn(void **state __attribute__((unused)))
{
assert_int_equal(slapi_pblock_get(pb, SLAPI_TARGET_UNIQUEID, &dn), 0);
assert_null(dn);
+ /* pblock currently DOES NOT free the target_dn, so we must do this */
+ slapi_sdn_free(&sdn);
+
/* A property we cannot easily test is that setting a new DN frees the
* OLD sdn. But, we can test in SDN that setting via SDN does NOT free.
*
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.