From 04fcc5d48653b751552274155bcbcfd7de363e55 Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Fri, 6 Mar 2020 23:22:38 +0800 Subject: [PATCH] =?UTF-8?q?nif=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- c_src/.enq/enq_nif.c | 442 --- c_src/.enq/fifo.h | 71 - c_src/.enq/lifo.h | 63 - c_src/.enq/rebar.config | 12 - c_src/.mqtree/mqtree.c | 688 ---- c_src/.mqtree/rebar.config | 12 - c_src/.mqtree/uthash.h | 1074 ------ c_src/bitmap_filter/bitmap_filter.c | 80 - c_src/bitmap_filter/rebar.config | 30 - c_src/bsn/bsn_ext.c | 448 --- c_src/bsn/bsn_int.c | 331 -- c_src/bsn/c_src/bsn_ext.c | 448 --- c_src/bsn/c_src/bsn_int.c | 331 -- c_src/bsn/rebar.config | 29 - c_src/couchdb-khash2/hash.c | 843 ----- c_src/couchdb-khash2/hash.h | 240 -- c_src/couchdb-khash2/khash.c | 658 ---- c_src/couchdb-khash2/rebar.config | 12 - c_src/enlfq/Makefile | 80 - c_src/enlfq/concurrentqueue.h | 3637 -------------------- c_src/enlfq/enlfq.cc | 84 - c_src/enlfq/enlfq.h | 10 - c_src/enlfq/enlfq_nif.cc | 57 - c_src/enlfq/enlfq_nif.h | 19 - c_src/enlfq/nif_utils.cc | 27 - c_src/enlfq/nif_utils.h | 6 - c_src/enlfq/rebar.config | 7 - c_src/etsq/etsq.cpp | 172 - c_src/etsq/etsq.h | 130 - c_src/etsq/rebar.config | 7 - c_src/gb_lru/binary.h | 103 - c_src/gb_lru/btree.h | 2394 ------------- c_src/gb_lru/btree_container.h | 349 -- c_src/gb_lru/btree_map.h | 130 - c_src/gb_lru/btreelru_nif.cpp | 619 ---- c_src/gb_lru/erlterm.h | 71 - c_src/gb_lru/lru.h | 266 -- c_src/gb_lru/murmurhash2.h | 73 - c_src/gb_lru/rebar.config | 7 - c_src/native_array/native_array_nif.c | 90 - c_src/native_array/rebar.config | 7 - c_src/neural/NeuralTable.cpp | 905 ----- c_src/neural/NeuralTable.h | 121 - c_src/neural/neural.cpp | 134 - c_src/neural/neural_utils.cpp | 46 - c_src/neural/neural_utils.h | 9 - c_src/neural/rebar.config | 14 - c_src/nif_array/binary_tools.c | 111 - c_src/nif_array/rebar.config | 13 - priv/binary_tools.dll | Bin 114176 -> 0 bytes priv/binary_tools.exp | Bin 695 -> 0 bytes priv/binary_tools.lib | Bin 1840 -> 0 bytes priv/binary_tools.so | Bin 21104 -> 0 bytes priv/bitmap_filter.dll | Bin 91136 -> 0 bytes priv/bitmap_filter.exp | Bin 702 -> 0 bytes priv/bitmap_filter.lib | Bin 1852 -> 0 bytes priv/bitmap_filter.so | Bin 12528 -> 0 bytes priv/bsn_ext.dll | Bin 95744 -> 0 bytes priv/bsn_ext.exp | Bin 680 -> 0 bytes priv/bsn_ext.lib | Bin 1702 -> 0 bytes priv/bsn_ext.so | Bin 25952 -> 0 bytes priv/bsn_int.dll | Bin 93184 -> 0 bytes priv/bsn_int.exp | Bin 680 -> 0 bytes priv/bsn_int.lib | Bin 1702 -> 0 bytes priv/bsn_int.so | Bin 24576 -> 0 bytes priv/btreelru_nif.dll | Bin 164352 -> 0 bytes priv/btreelru_nif.exp | Bin 692 -> 0 bytes priv/btreelru_nif.lib | Bin 1840 -> 0 bytes priv/btreelru_nif.so | Bin 282608 -> 0 bytes priv/cbase64.dll | Bin 93696 -> 93696 bytes priv/enlfq.dll | Bin 152064 -> 0 bytes priv/enlfq.exp | Bin 678 -> 0 bytes priv/enlfq.lib | Bin 1678 -> 0 bytes priv/enlfq.so | Bin 278912 -> 0 bytes priv/epqueue_nif.dll | Bin 101376 -> 101376 bytes priv/etsq.dll | Bin 111616 -> 0 bytes priv/etsq.exp | Bin 674 -> 0 bytes priv/etsq.lib | Bin 1666 -> 0 bytes priv/etsq.so | Bin 197232 -> 0 bytes priv/hqueue.dll | Bin 96256 -> 96256 bytes priv/khash.dll | Bin 96768 -> 96768 bytes priv/khash2.dll | Bin 96768 -> 0 bytes priv/khash2.exp | Bin 688 -> 0 bytes priv/khash2.lib | Bin 1690 -> 0 bytes priv/khash2.so | Bin 72096 -> 0 bytes priv/mqtree.so | Bin 97352 -> 0 bytes priv/native_array_nif.dll | Bin 91648 -> 0 bytes priv/native_array_nif.exp | Bin 706 -> 0 bytes priv/native_array_nif.lib | Bin 1892 -> 0 bytes priv/native_array_nif.so | Bin 17856 -> 0 bytes priv/neural.dll | Bin 165376 -> 0 bytes priv/neural.exp | Bin 680 -> 0 bytes priv/neural.lib | Bin 1690 -> 0 bytes priv/neural.so | Bin 512088 -> 0 bytes priv/nifArray.dll | Bin 91648 -> 91648 bytes priv/nifHashb.dll | Bin 0 -> 96256 bytes priv/nifHashb.exp | Bin 0 -> 686 bytes priv/nifHashb.lib | Bin 0 -> 1714 bytes priv/nif_skiplist.dll | Bin 126976 -> 126976 bytes src/nifSrc/bitmap_filter/bitmap_filter.erl | 20 - src/nifSrc/bsn/bsn.erl | 77 - src/nifSrc/bsn/bsn_ext.erl | 56 - src/nifSrc/bsn/bsn_int.erl | 45 - src/nifSrc/bsn/bsn_measure.erl | 236 -- src/nifSrc/enlfq/enlfq.erl | 51 - src/nifSrc/enlfq/testing/benchmark.erl | 71 - src/nifSrc/enlfq/testing/multi_spawn.erl | 23 - src/nifSrc/enq/enq.erl | 159 - src/nifSrc/enq/enq_nif.erl | 63 - src/nifSrc/etsq/etsq.erl | 103 - src/nifSrc/etsq/etsq_tests.erl | 65 - src/nifSrc/gb_lru/btree_lru.erl | 102 - src/nifSrc/gb_lru/btree_lru_test.erl | 59 - src/nifSrc/gb_lru/gb_lru.app.src | 6 - src/nifSrc/native_array/native_array.erl | 19 - src/nifSrc/neural/neural.erl | 184 - src/nifSrc/neural/neural_app.erl | 16 - src/nifSrc/neural/neural_sup.erl | 31 - src/nifSrc/nif_array/binary_tools.erl | 60 - 119 files changed, 16956 deletions(-) delete mode 100644 c_src/.enq/enq_nif.c delete mode 100644 c_src/.enq/fifo.h delete mode 100644 c_src/.enq/lifo.h delete mode 100644 c_src/.enq/rebar.config delete mode 100644 c_src/.mqtree/mqtree.c delete mode 100644 c_src/.mqtree/rebar.config delete mode 100644 c_src/.mqtree/uthash.h delete mode 100644 c_src/bitmap_filter/bitmap_filter.c delete mode 100644 c_src/bitmap_filter/rebar.config delete mode 100644 c_src/bsn/bsn_ext.c delete mode 100644 c_src/bsn/bsn_int.c delete mode 100644 c_src/bsn/c_src/bsn_ext.c delete mode 100644 c_src/bsn/c_src/bsn_int.c delete mode 100644 c_src/bsn/rebar.config delete mode 100644 c_src/couchdb-khash2/hash.c delete mode 100644 c_src/couchdb-khash2/hash.h delete mode 100644 c_src/couchdb-khash2/khash.c delete mode 100644 c_src/couchdb-khash2/rebar.config delete mode 100644 c_src/enlfq/Makefile delete mode 100644 c_src/enlfq/concurrentqueue.h delete mode 100644 c_src/enlfq/enlfq.cc delete mode 100644 c_src/enlfq/enlfq.h delete mode 100644 c_src/enlfq/enlfq_nif.cc delete mode 100644 c_src/enlfq/enlfq_nif.h delete mode 100644 c_src/enlfq/nif_utils.cc delete mode 100644 c_src/enlfq/nif_utils.h delete mode 100644 c_src/enlfq/rebar.config delete mode 100644 c_src/etsq/etsq.cpp delete mode 100644 c_src/etsq/etsq.h delete mode 100644 c_src/etsq/rebar.config delete mode 100644 c_src/gb_lru/binary.h delete mode 100644 c_src/gb_lru/btree.h delete mode 100644 c_src/gb_lru/btree_container.h delete mode 100644 c_src/gb_lru/btree_map.h delete mode 100644 c_src/gb_lru/btreelru_nif.cpp delete mode 100644 c_src/gb_lru/erlterm.h delete mode 100644 c_src/gb_lru/lru.h delete mode 100644 c_src/gb_lru/murmurhash2.h delete mode 100644 c_src/gb_lru/rebar.config delete mode 100644 c_src/native_array/native_array_nif.c delete mode 100644 c_src/native_array/rebar.config delete mode 100644 c_src/neural/NeuralTable.cpp delete mode 100644 c_src/neural/NeuralTable.h delete mode 100644 c_src/neural/neural.cpp delete mode 100644 c_src/neural/neural_utils.cpp delete mode 100644 c_src/neural/neural_utils.h delete mode 100644 c_src/neural/rebar.config delete mode 100644 c_src/nif_array/binary_tools.c delete mode 100644 c_src/nif_array/rebar.config delete mode 100644 priv/binary_tools.dll delete mode 100644 priv/binary_tools.exp delete mode 100644 priv/binary_tools.lib delete mode 100644 priv/binary_tools.so delete mode 100644 priv/bitmap_filter.dll delete mode 100644 priv/bitmap_filter.exp delete mode 100644 priv/bitmap_filter.lib delete mode 100644 priv/bitmap_filter.so delete mode 100644 priv/bsn_ext.dll delete mode 100644 priv/bsn_ext.exp delete mode 100644 priv/bsn_ext.lib delete mode 100644 priv/bsn_ext.so delete mode 100644 priv/bsn_int.dll delete mode 100644 priv/bsn_int.exp delete mode 100644 priv/bsn_int.lib delete mode 100644 priv/bsn_int.so delete mode 100644 priv/btreelru_nif.dll delete mode 100644 priv/btreelru_nif.exp delete mode 100644 priv/btreelru_nif.lib delete mode 100644 priv/btreelru_nif.so delete mode 100644 priv/enlfq.dll delete mode 100644 priv/enlfq.exp delete mode 100644 priv/enlfq.lib delete mode 100644 priv/enlfq.so delete mode 100644 priv/etsq.dll delete mode 100644 priv/etsq.exp delete mode 100644 priv/etsq.lib delete mode 100644 priv/etsq.so delete mode 100644 priv/khash2.dll delete mode 100644 priv/khash2.exp delete mode 100644 priv/khash2.lib delete mode 100644 priv/khash2.so delete mode 100644 priv/mqtree.so delete mode 100644 priv/native_array_nif.dll delete mode 100644 priv/native_array_nif.exp delete mode 100644 priv/native_array_nif.lib delete mode 100644 priv/native_array_nif.so delete mode 100644 priv/neural.dll delete mode 100644 priv/neural.exp delete mode 100644 priv/neural.lib delete mode 100644 priv/neural.so create mode 100644 priv/nifHashb.dll create mode 100644 priv/nifHashb.exp create mode 100644 priv/nifHashb.lib delete mode 100644 src/nifSrc/bitmap_filter/bitmap_filter.erl delete mode 100644 src/nifSrc/bsn/bsn.erl delete mode 100644 src/nifSrc/bsn/bsn_ext.erl delete mode 100644 src/nifSrc/bsn/bsn_int.erl delete mode 100644 src/nifSrc/bsn/bsn_measure.erl delete mode 100644 src/nifSrc/enlfq/enlfq.erl delete mode 100644 src/nifSrc/enlfq/testing/benchmark.erl delete mode 100644 src/nifSrc/enlfq/testing/multi_spawn.erl delete mode 100644 src/nifSrc/enq/enq.erl delete mode 100644 src/nifSrc/enq/enq_nif.erl delete mode 100644 src/nifSrc/etsq/etsq.erl delete mode 100644 src/nifSrc/etsq/etsq_tests.erl delete mode 100644 src/nifSrc/gb_lru/btree_lru.erl delete mode 100644 src/nifSrc/gb_lru/btree_lru_test.erl delete mode 100644 src/nifSrc/gb_lru/gb_lru.app.src delete mode 100644 src/nifSrc/native_array/native_array.erl delete mode 100644 src/nifSrc/neural/neural.erl delete mode 100644 src/nifSrc/neural/neural_app.erl delete mode 100644 src/nifSrc/neural/neural_sup.erl delete mode 100644 src/nifSrc/nif_array/binary_tools.erl diff --git a/c_src/.enq/enq_nif.c b/c_src/.enq/enq_nif.c deleted file mode 100644 index a855a36..0000000 --- a/c_src/.enq/enq_nif.c +++ /dev/null @@ -1,442 +0,0 @@ -#define _GNU_SOURCE - -#include "erl_nif.h" - -#include -#include -#include -#include -#include - -// #include "fifo.h" -#include "lifo.h" - -typedef struct { - ERL_NIF_TERM ok; - ERL_NIF_TERM error; - ERL_NIF_TERM fifo; - ERL_NIF_TERM lifo; - ERL_NIF_TERM ttl; - ERL_NIF_TERM max_size; -} atoms_t; - -typedef struct { - ErlNifResourceType *queue; - atoms_t atoms; -} priv_t; - -typedef struct { - union { - fifo_handle_t fifo; - lifo_handle_t lifo; - } handle; - ErlNifBinary data; - struct timespec added; -} item_t; - -typedef enum { - QTYPE_FIFO = 0, - QTYPE_LIFO -} queue_type_t; - -typedef struct queue { - union { - fifo_t fifo; - lifo_t lifo; - } queue; - uint64_t ttl; - uint64_t max_size; - void (*push) (struct queue *inst, item_t *item); - item_t* (*pop) (struct queue *inst); - void (*free) (struct queue *inst); - uint64_t (*size) (struct queue *inst); - void (*cleanup) (struct queue *inst); -} queue_t; - -// returns tuple {error, atom()} -static inline ERL_NIF_TERM -make_error(ErlNifEnv* env, const char *error) { - priv_t *priv = (priv_t *) enif_priv_data(env); - - return enif_make_tuple2(env, priv->atoms.error, enif_make_atom(env, error)); -} - -// returns time diff in milliseconds -static inline int64_t -tdiff(struct timespec *t2, struct timespec *t1) { - return (t2->tv_sec * 1000 + t2->tv_nsec / 1000000UL) - - (t1->tv_sec * 1000 + t1->tv_nsec / 1000000UL); -} - -static inline void -gettime(struct timespec *tp) { - int rc = clock_gettime(CLOCK_MONOTONIC_RAW, tp); - assert(rc == 0); -} - -/******************************************************************************/ -/* FIFO callbacks */ -/******************************************************************************/ - -static void -cleanup_fifo(queue_t *inst) { - struct timespec now; - - gettime(&now); - - for (;;) { - item_t *item = NULL; - __fifo_peak(&inst->queue.fifo, item, handle.fifo); - - if (item == NULL) - return; - - int64_t diff = tdiff(&now, &item->added); - if (diff < inst->ttl) { - return; - } else { - __fifo_pop(&inst->queue.fifo, item, handle.fifo); - enif_release_binary(&item->data); - enif_free(item); - } - } -} - -static void -push_fifo(queue_t *inst, item_t *item) { - __fifo_push(&inst->queue.fifo, item, handle.fifo); -} - -static item_t * -pop_fifo(queue_t *inst) { - item_t *item = NULL; - - if (inst->ttl > 0) { - struct timespec now; - - gettime(&now); - - for (;;) { - __fifo_pop(&inst->queue.fifo, item, handle.fifo); - - if (item == NULL) - return NULL; - - int64_t diff = tdiff(&now, &item->added); - if (diff < inst->ttl) { - return item; - } else { - enif_release_binary(&item->data); - enif_free(item); - } - } - } else { - __fifo_pop(&inst->queue.fifo, item, handle.fifo); - } - - return item; -} - -static void -free_fifo(queue_t *inst) { - item_t *item; - - for(;;) { - __fifo_pop(&inst->queue.fifo, item, handle.fifo); - - if (item == NULL) - return; - - enif_release_binary(&item->data); - enif_free(item); - } -} - -static uint64_t -size_fifo(queue_t *inst) { - return fifo_length(&inst->queue.fifo); -} - -/******************************************************************************/ -/* LIFO callbacks */ -/******************************************************************************/ - -static void -cleanup_lifo(queue_t *inst) { - struct timespec now; - - gettime(&now); - - for(;;) { - item_t *item = inst->queue.lifo.tail; - - if (item == NULL) - return; - - int64_t diff = tdiff(&now, &item->added); - if (diff < inst->ttl) { - return; - } else { - item_t *prev = item->handle.lifo.prev; - - if (prev != NULL) - prev->handle.lifo.next = NULL; - - inst->queue.lifo.tail = prev; - - enif_release_binary(&item->data); - enif_free(item); - } - } -} - -static void -push_lifo(queue_t *inst, item_t *item) { - __lifo_push(&inst->queue.lifo, item, handle.lifo); -} - -static item_t * -pop_lifo(queue_t *inst) { - item_t *item = NULL; - - if (inst->ttl > 0) - cleanup_lifo(inst); - - __lifo_pop(&inst->queue.lifo, item, handle.lifo); - - return item; -} - -static void -free_lifo(queue_t *inst) { - item_t *item; - - for(;;) { - __lifo_pop(&inst->queue.lifo, item, handle.lifo); - - if (item == NULL) - return; - - enif_release_binary(&item->data); - enif_free(item); - } -} - -static uint64_t -size_lifo(queue_t *inst) { - return lifo_length(&inst->queue.lifo); -} - -/****************************************************************************** -** NIFs -*******************************************************************************/ - -static ERL_NIF_TERM -new_queue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_list(env, argv[0])) - return enif_make_badarg(env); - - priv_t *priv = (priv_t *) enif_priv_data(env); - - queue_type_t qtype = QTYPE_FIFO; - unsigned long ttl = 0; - unsigned long max_size = 0; - - ERL_NIF_TERM settings_list = argv[0]; - ERL_NIF_TERM head; - - // parses proplist [fifo, lifo, {ttl, non_neg_integer()}, {max_size, non_neg_integer()}] - while(enif_get_list_cell(env, settings_list, &head, &settings_list)) - { - const ERL_NIF_TERM *items; - int arity; - - if (enif_is_atom(env, head)) { - if (enif_is_identical(head, priv->atoms.fifo)) { - qtype = QTYPE_FIFO; - } else if (enif_is_identical(head, priv->atoms.lifo)) { - qtype = QTYPE_LIFO; - } else { - return enif_make_badarg(env); - } - } else if (enif_get_tuple(env, head, &arity, &items) && arity == 2) { - if (enif_is_identical(items[0], priv->atoms.ttl)) { - if (!enif_get_ulong(env, items[1], &ttl)) { - return enif_make_badarg(env); - } - } else if (enif_is_identical(items[0], priv->atoms.max_size)) { - if (!enif_get_ulong(env, items[1], &max_size)) { - return enif_make_badarg(env); - } - } else { - return enif_make_badarg(env); - } - } else { - return enif_make_badarg(env); - } - } - - queue_t *inst = (queue_t *) enif_alloc_resource(priv->queue, sizeof(*inst)); - - if (inst == NULL) - return make_error(env, "enif_alloc_resource"); - - inst->ttl = ttl; - inst->max_size = max_size; - - switch (qtype) { - case QTYPE_FIFO: - fifo_init(&inst->queue.fifo); - inst->push = &push_fifo; - inst->pop = &pop_fifo; - inst->free = &free_fifo; - inst->size = &size_fifo; - inst->cleanup = &cleanup_fifo; - break; - case QTYPE_LIFO: - lifo_init(&inst->queue.lifo); - inst->push = &push_lifo; - inst->pop = &pop_lifo; - inst->free = &free_lifo; - inst->size = &size_lifo; - inst->cleanup = &cleanup_lifo; - break; - } - - ERL_NIF_TERM result = enif_make_resource(env, inst); - enif_release_resource(inst); - - return enif_make_tuple2(env, priv->atoms.ok, result); -} - -static ERL_NIF_TERM -push_item(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - priv_t *priv = (priv_t *) enif_priv_data(env); - - queue_t *inst; - - if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst)) - return enif_make_badarg(env); - - // todo: check an owner of the queue - - ErlNifBinary bin; - if (!enif_inspect_binary(env, argv[1], &bin)) - return enif_make_badarg(env); - - if (inst->ttl > 0) { - inst->cleanup(inst); - } - - if (inst->max_size > 0 && inst->size(inst) >= inst->max_size) { - return enif_make_tuple2(env, priv->atoms.error, priv->atoms.max_size); - } - - item_t *item = (item_t *) enif_alloc(sizeof(*item)); - - if (item == NULL) - return make_error(env, "enif_alloc"); - - if (!enif_alloc_binary(bin.size, &item->data)) { - enif_free(item); - return make_error(env, "enif_alloc_binary"); - } - - memcpy(item->data.data, bin.data, bin.size); - - if (inst->ttl > 0) { - gettime(&item->added); - } - - inst->push(inst, item); - return priv->atoms.ok; -} - -static ERL_NIF_TERM -pop_item(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - priv_t *priv = (priv_t *) enif_priv_data(env); - - queue_t *inst; - item_t *item; - - if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst)) - return enif_make_badarg(env); - - // todo: check an owner of the queue - - item = inst->pop(inst); - - if (item == NULL) - return enif_make_list(env, 0); - - ERL_NIF_TERM result = enif_make_binary(env, &item->data); - - enif_free(item); - - return enif_make_list1(env, result); -} - -static ERL_NIF_TERM -queue_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - priv_t *priv = (priv_t *) enif_priv_data(env); - - queue_t *inst; - - if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst)) - return enif_make_badarg(env); - - return enif_make_uint64(env, inst->size(inst)); -} - -/****************************************************************************** -** NIF initialization -*******************************************************************************/ - -static void -enq_queue_free(ErlNifEnv* env, void* obj) { - queue_t *inst = obj; - inst->free(inst); -} - -static priv_t * -make_priv(ErlNifEnv *env) { - priv_t *priv = enif_alloc(sizeof(*priv)); - - if (priv == NULL) - return NULL; - - ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; - priv->queue = enif_open_resource_type(env, NULL, "enq_queue", enq_queue_free, flags, NULL); - - priv->atoms.ok = enif_make_atom(env, "ok"); - priv->atoms.error = enif_make_atom(env, "error"); - priv->atoms.fifo = enif_make_atom(env, "fifo"); - priv->atoms.lifo = enif_make_atom(env, "lifo"); - priv->atoms.ttl = enif_make_atom(env, "ttl"); - priv->atoms.max_size = enif_make_atom(env, "max_size"); - - return priv; -} - -static int -enq_nif_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { - *priv_data = make_priv(env); - - return 0; -} - -static int -enq_nif_upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM load_info) { - *priv_data = make_priv(env); - - return 0; -} - -static ErlNifFunc enq_nif_funcs[] = { - {"new", 1, new_queue}, - {"push", 2, push_item}, - {"pop", 1, pop_item}, - {"size", 1, queue_size}, -}; - -ERL_NIF_INIT(enq_nif, enq_nif_funcs, enq_nif_load, NULL, enq_nif_upgrade, NULL) \ No newline at end of file diff --git a/c_src/.enq/fifo.h b/c_src/.enq/fifo.h deleted file mode 100644 index ec45eed..0000000 --- a/c_src/.enq/fifo.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef _FIFO_H -#define _FIFO_H - -/* Main FIFO structure. Allocate memory for it yourself. */ -typedef struct fifo_t { - void *head; - void *tail; - unsigned long long count; -} fifo_t; - -typedef struct fifo_handle_t { - void *next; -} fifo_handle_t; - -/* Initializes fifo structure. */ -#define fifo_init(fifo) \ -do { \ - fifo_t *__q = fifo; \ - __q->head = NULL; \ - __q->tail = NULL; \ - __q->count = 0; \ -} while (0) - -#define __fifo_push(fifo, p, h) \ -do { \ - fifo_t *__q = fifo; \ - __typeof__ (p) e = p; \ - e->h.next = NULL; \ - if (__q->tail == NULL) { \ - __q->head = e; \ - } else { \ - __typeof__ (e) t = __q->tail; \ - t->h.next = e; \ - } \ - __q->tail = e; \ - __q->count++; \ -} while (0) - -/* Puts an element to the queue. */ -#define fifo_push(fifo, p) __fifo_push (fifo, p, fifo_handle) - -#define __fifo_pop(fifo, p, h) \ -do { \ - fifo_t *__q = fifo; \ - p = __q->head; \ - if (p != NULL) { \ - __q->count--; \ - __q->head = p->h.next; \ - if (__q->tail == p) \ - __q->tail = NULL; \ - } \ -} while (0) - -/* Pops the first element out of the queue. */ -#define fifo_pop(fifo, p) __fifo_pop (fifo, p, fifo_handle) - -#define __fifo_peak(fifo, p, h) \ -do { \ - p = (fifo)->head; \ -} while (0) - -/* Returns the first elemnt of the queue without removing. */ -#define fifo_peak(fifo, p) __fifo_peak (fifo, p, fifo_handle) - -/* Returns the length of the queue. */ -#define fifo_length(fifo) ((fifo)->count) - -/* Returns true if the queue is empty. */ -#define fifo_empty(fifo) ((fifo)->count == 0) - -#endif /* _FIFO_H */ diff --git a/c_src/.enq/lifo.h b/c_src/.enq/lifo.h deleted file mode 100644 index 8e57c06..0000000 --- a/c_src/.enq/lifo.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef _LIFO_H -#define _LIFO_H - -typedef struct lifo_t { - void *head; - void *tail; - unsigned long long count; -} lifo_t; - -typedef struct lifo_handle_t { - void *next; - void *prev; -} lifo_handle_t; - -#define lifo_init(lifo) \ -do { \ - lifo_t *__q = lifo; \ - __q->head = NULL; \ - __q->tail = NULL; \ - __q->count = 0; \ -} while (0) - -#define __lifo_push(lifo, p, h) \ -do { \ - lifo_t *__q = lifo; \ - __typeof__ (p) e = p; \ - e->h.next = __q->head; \ - e->h.prev = NULL; \ - if (__q->head == NULL) { \ - __q->tail = e; \ - } else { \ - __typeof__ (e) t = __q->head; \ - t->h.prev = e; \ - } \ - __q->head = e; \ - __q->count++; \ -} while (0) - -#define lifo_push(lifo, p) __lifo_push (lifo, p, lifo_handle) - -#define __lifo_pop(lifo, p, h) \ -do { \ - lifo_t *__q = lifo; \ - p = __q->head; \ - if (p != NULL) { \ - __q->count--; \ - __q->head = p->h.next; \ - if (__q->head != NULL) { \ - __typeof__ (p) t = __q->head; \ - t->h.prev = NULL; \ - } else { \ - __q->tail = NULL; \ - } \ - } \ -} while (0) - -#define lifo_pop(lifo, p) __lifo_pop (lifo, p, lifo_handle) - -#define lifo_length(lifo) ((lifo)->count) - -#define lifo_empty(lifo) ((lifo)->count == 0) - -#endif /* _LIFO_H */ diff --git a/c_src/.enq/rebar.config b/c_src/.enq/rebar.config deleted file mode 100644 index 95b16e5..0000000 --- a/c_src/.enq/rebar.config +++ /dev/null @@ -1,12 +0,0 @@ -{port_specs, [ - {"../../priv/enq_nif.so", ["*.c"]} -]}. - -% {port_env, [ -% {"LDFLAGS", "$ERL_LDFLAGS -lrt"}, -% {"CFLAGS", "$CFLAGS --std=gnu99 -Wall -O3"} -% ]}. - - - - diff --git a/c_src/.mqtree/mqtree.c b/c_src/.mqtree/mqtree.c deleted file mode 100644 index 0453f85..0000000 --- a/c_src/.mqtree/mqtree.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * @author Evgeny Khramtsov - * @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include "uthash.h" - -void __free(void *ptr, size_t size) { - enif_free(ptr); -} - -#undef uthash_malloc -#undef uthash_free -#define uthash_malloc enif_alloc -#define uthash_free __free - -/**************************************************************** - * Structures/Globals definitions * - ****************************************************************/ -typedef struct __tree_t { - char *key; - char *val; - int refc; - struct __tree_t *sub; - UT_hash_handle hh; -} tree_t; - -typedef struct { - tree_t *tree; - char *name; - ErlNifRWLock *lock; -} state_t; - -typedef struct { - char *name; - state_t *state; - UT_hash_handle hh; -} registry_t; - -static ErlNifResourceType *tree_state_t = NULL; -static registry_t *registry = NULL; -static ErlNifRWLock *registry_lock = NULL; - -/**************************************************************** - * MQTT Tree Manipulation * - ****************************************************************/ -tree_t *tree_new(char *key, size_t len) { - tree_t *tree = enif_alloc(sizeof(tree_t)); - if (tree) { - memset(tree, 0, sizeof(tree_t)); - if (key && len) { - tree->key = enif_alloc(len); - if (tree->key) { - memcpy(tree->key, key, len); - } else { - enif_free(tree); - tree = NULL; - } - } - } - return tree; -} - -void tree_free(tree_t *t) { - tree_t *found, *iter; - if (t) { - enif_free(t->key); - enif_free(t->val); - HASH_ITER(hh, t->sub, found, iter) { - HASH_DEL(t->sub, found); - tree_free(found); - } - memset(t, 0, sizeof(tree_t)); - enif_free(t); - } -} - -void tree_clear(tree_t *root) { - tree_t *found, *iter; - HASH_ITER(hh, root->sub, found, iter) { - HASH_DEL(root->sub, found); - tree_free(found); - } -} - -int tree_add(tree_t *root, char *path, size_t size) { - int i = 0; - size_t len; - tree_t *t = root; - tree_t *found, *new; - - while (i<=size) { - len = strlen(path+i) + 1; - HASH_FIND_STR(t->sub, path+i, found); - if (found) { - i += len; - t = found; - } else { - new = tree_new(path+i, len); - if (new) { - HASH_ADD_STR(t->sub, key, new); - i += len; - t = new; - } else - return ENOMEM; - } - } - - if (!t->val) { - t->val = enif_alloc(size+1); - if (t->val) { - t->val[size] = 0; - for (i=0; ival[i] = c ? c : '/'; - } - } else - return ENOMEM; - } - t->refc++; - return 0; -} - -int tree_del(tree_t *root, char *path, size_t i, size_t size) { - tree_t *found; - - if (i<=size) { - HASH_FIND_STR(root->sub, path+i, found); - if (found) { - i += strlen(path+i) + 1; - int deleted = tree_del(found, path, i, size); - if (deleted) { - HASH_DEL(root->sub, found); - tree_free(found); - } - } - } else if (root->refc) { - root->refc--; - if (!root->refc) { - enif_free(root->val); - root->val = NULL; - } - } - - return !root->refc && !root->sub; -} - -void tree_size(tree_t *tree, size_t *size) { - tree_t *found, *iter; - - HASH_ITER(hh, tree->sub, found, iter) { - if (found->refc) (*size)++; - tree_size(found, size); - } -} - -int tree_refc(tree_t *tree, char *path, size_t i, size_t size) { - tree_t *found; - - if (i<=size) { - HASH_FIND_STR(tree->sub, path+i, found); - if (found) { - i += strlen(path+i) + 1; - return tree_refc(found, path, i, size); - } else { - return 0; - } - } else - return tree->refc; -} - -/**************************************************************** - * Registration * - ****************************************************************/ -void delete_registry_entry(registry_t *entry) { - /* registry_lock must be RW-locked! */ - HASH_DEL(registry, entry); - entry->state->name = NULL; - enif_release_resource(entry->state); - enif_free(entry->name); - enif_free(entry); -} - -int register_tree(char *name, state_t *state) { - registry_t *entry, *found; - - entry = enif_alloc(sizeof(registry_t)); - if (!entry) return ENOMEM; - - entry->name = enif_alloc(strlen(name) + 1); - if (!entry->name) { - free(entry); - return ENOMEM; - } - - entry->state = state; - strcpy(entry->name, name); - enif_rwlock_rwlock(registry_lock); - HASH_FIND_STR(registry, name, found); - if (found) { - enif_rwlock_rwunlock(registry_lock); - enif_free(entry->name); - enif_free(entry); - return EINVAL; - } else { - if (state->name) { - /* Unregistering previously registered name */ - HASH_FIND_STR(registry, state->name, found); - if (found) - delete_registry_entry(found); - } - enif_keep_resource(state); - HASH_ADD_STR(registry, name, entry); - state->name = entry->name; - enif_rwlock_rwunlock(registry_lock); - return 0; - } -} - -int unregister_tree(char *name) { - registry_t *entry; - int ret; - - enif_rwlock_rwlock(registry_lock); - HASH_FIND_STR(registry, name, entry); - if (entry) { - delete_registry_entry(entry); - ret = 0; - } else { - ret = EINVAL; - } - enif_rwlock_rwunlock(registry_lock); - - return ret; -} - -/**************************************************************** - * NIF helpers * - ****************************************************************/ -static ERL_NIF_TERM cons(ErlNifEnv *env, char *str, ERL_NIF_TERM tail) -{ - if (str) { - size_t len = strlen(str); - ERL_NIF_TERM head; - unsigned char *buf = enif_make_new_binary(env, len, &head); - if (buf) { - memcpy(buf, str, len); - return enif_make_list_cell(env, head, tail); - } - } - return tail; -} - -static void match(ErlNifEnv *env, tree_t *root, - char *path, size_t i, size_t size, ERL_NIF_TERM *acc) -{ - tree_t *found; - size_t len = 0; - - if (i<=size) { - HASH_FIND_STR(root->sub, path+i, found); - if (found) { - len = strlen(path+i) + 1; - match(env, found, path, i+len, size, acc); - }; - if (i || path[0] != '$') { - HASH_FIND_STR(root->sub, "+", found); - if (found) { - len = strlen(path+i) + 1; - match(env, found, path, i+len, size, acc); - } - HASH_FIND_STR(root->sub, "#", found); - if (found) { - *acc = cons(env, found->val, *acc); - } - } - } else { - *acc = cons(env, root->val, *acc); - HASH_FIND_STR(root->sub, "#", found); - if (found) - *acc = cons(env, found->val, *acc); - } -} - -static void to_list(ErlNifEnv *env, tree_t *root, ERL_NIF_TERM *acc) -{ - tree_t *found, *iter; - - HASH_ITER(hh, root->sub, found, iter) { - if (found->val) { - size_t len = strlen(found->val); - ERL_NIF_TERM refc = enif_make_int(env, found->refc); - ERL_NIF_TERM val; - unsigned char *buf = enif_make_new_binary(env, len, &val); - if (buf) { - memcpy(buf, found->val, len); - *acc = enif_make_list_cell(env, enif_make_tuple2(env, val, refc), *acc); - } - }; - to_list(env, found, acc); - } -} - -static ERL_NIF_TERM dump(ErlNifEnv *env, tree_t *tree) -{ - tree_t *found, *iter; - ERL_NIF_TERM tail, head; - - tail = enif_make_list(env, 0); - HASH_ITER(hh, tree->sub, found, iter) { - head = dump(env, found); - tail = enif_make_list_cell(env, head, tail); - } - if (tree->key) { - ERL_NIF_TERM part, path; - part = enif_make_string(env, tree->key, ERL_NIF_LATIN1); - if (tree->val) - path = enif_make_string(env, tree->val, ERL_NIF_LATIN1); - else - path = enif_make_atom(env, "none"); - return enif_make_tuple4(env, part, path, enif_make_int(env, tree->refc), tail); - } else - return tail; -} - -static ERL_NIF_TERM raise(ErlNifEnv *env, int err) -{ - switch (err) { - case ENOMEM: - return enif_raise_exception(env, enif_make_atom(env, "enomem")); - default: - return enif_make_badarg(env); - } -} - -void prep_path(char *path, ErlNifBinary *bin) { - int i; - unsigned char c; - path[bin->size] = 0; - for (i=0; isize; i++) { - c = bin->data[i]; - path[i] = (c == '/') ? 0 : c; - } -} - -/**************************************************************** - * Constructors/Destructors * - ****************************************************************/ -static state_t *init_tree_state(ErlNifEnv *env) { - state_t *state = enif_alloc_resource(tree_state_t, sizeof(state_t)); - if (state) { - memset(state, 0, sizeof(state_t)); - state->tree = tree_new(NULL, 0); - state->lock = enif_rwlock_create("mqtree_lock"); - if (state->tree && state->lock) - return state; - else - enif_release_resource(state); - } - return NULL; -} - -static void destroy_tree_state(ErlNifEnv *env, void *data) { - state_t *state = (state_t *) data; - if (state) { - tree_free(state->tree); - if (state->lock) enif_rwlock_destroy(state->lock); - } - memset(state, 0, sizeof(state_t)); -} - -/**************************************************************** - * NIF definitions * - ****************************************************************/ -static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM max) { - registry_lock = enif_rwlock_create("mqtree_registry"); - if (registry_lock) { - ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; - tree_state_t = enif_open_resource_type(env, NULL, "mqtree_state", - destroy_tree_state, - flags, NULL); - return 0; - } - return ENOMEM; -} - -static void unload(ErlNifEnv* env, void* priv) { - if (registry_lock) { - enif_rwlock_destroy(registry_lock); - registry_lock = NULL; - } -} - -static ERL_NIF_TERM new_0(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - ERL_NIF_TERM result; - state_t *state = init_tree_state(env); - if (state) { - result = enif_make_resource(env, state); - enif_release_resource(state); - } else - result = raise(env, ENOMEM); - - return result; -} - -static ERL_NIF_TERM insert_2(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - ErlNifBinary path_bin; - - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || - !enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) - return raise(env, EINVAL); - - if (!path_bin.size) - return enif_make_atom(env, "ok"); - - char path[path_bin.size+1]; - prep_path(path, &path_bin); - enif_rwlock_rwlock(state->lock); - int ret = tree_add(state->tree, path, path_bin.size); - enif_rwlock_rwunlock(state->lock); - - if (!ret) - return enif_make_atom(env, "ok"); - else - return raise(env, ret); -} - -static ERL_NIF_TERM delete_2(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - ErlNifBinary path_bin; - - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || - !enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) - return raise(env, EINVAL); - - if (!path_bin.size) - return enif_make_atom(env, "ok"); - - char path[path_bin.size+1]; - prep_path(path, &path_bin); - enif_rwlock_rwlock(state->lock); - tree_del(state->tree, path, 0, path_bin.size); - enif_rwlock_rwunlock(state->lock); - - return enif_make_atom(env, "ok"); -} - -static ERL_NIF_TERM match_2(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - ErlNifBinary path_bin; - ERL_NIF_TERM result = enif_make_list(env, 0); - - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || - !enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) - return raise(env, EINVAL); - - if (!path_bin.size) - return result; - - - - char path[path_bin.size+1]; - prep_path(path, &path_bin); - enif_rwlock_rlock(state->lock); - match(env, state->tree, path, 0, path_bin.size, &result); - enif_rwlock_runlock(state->lock); - - return result; -} - -static ERL_NIF_TERM refc_2(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - ErlNifBinary path_bin; - - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || - !enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) - return raise(env, EINVAL); - - if (!path_bin.size) - return enif_make_int(env, 0); - - char path[path_bin.size+1]; - prep_path(path, &path_bin); - enif_rwlock_rlock(state->lock); - int refc = tree_refc(state->tree, path, 0, path_bin.size); - enif_rwlock_runlock(state->lock); - - return enif_make_int(env, refc); -} - -static ERL_NIF_TERM clear_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) - return raise(env, EINVAL); - - enif_rwlock_rwlock(state->lock); - tree_clear(state->tree); - enif_rwlock_rwunlock(state->lock); - - return enif_make_atom(env, "ok"); -} - -static ERL_NIF_TERM size_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - size_t size = 0; - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) - return raise(env, EINVAL); - - enif_rwlock_rlock(state->lock); - tree_size(state->tree, &size); - enif_rwlock_runlock(state->lock); - - return enif_make_uint64(env, (ErlNifUInt64) size); -} - -static ERL_NIF_TERM is_empty_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) - return raise(env, EINVAL); - - enif_rwlock_rlock(state->lock); - char *ret = state->tree->sub ? "false" : "true"; - enif_rwlock_runlock(state->lock); - - return enif_make_atom(env, ret); -} - -static ERL_NIF_TERM to_list_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - ERL_NIF_TERM result = enif_make_list(env, 0); - - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) - return raise(env, EINVAL); - - enif_rwlock_rlock(state->lock); - to_list(env, state->tree, &result); - enif_rwlock_runlock(state->lock); - - return result; -} - -static ERL_NIF_TERM dump_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) - return raise(env, EINVAL); - - enif_rwlock_rlock(state->lock); - ERL_NIF_TERM result = dump(env, state->tree); - enif_rwlock_runlock(state->lock); - - return result; -} - -static ERL_NIF_TERM register_2(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - state_t *state; - unsigned int len; - int ret; - - if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1) || - !enif_get_resource(env, argv[1], tree_state_t, (void *) &state)) - return raise(env, EINVAL); - - char name[len+1]; - enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1); - if (!strcmp(name, "undefined")) - return raise(env, EINVAL); - - ret = register_tree(name, state); - if (ret) - return raise(env, ret); - else - return enif_make_atom(env, "ok"); -} - -static ERL_NIF_TERM unregister_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - unsigned int len; - int ret; - - if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1)) - return raise(env, EINVAL); - - char name[len+1]; - enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1); - ret = unregister_tree(name); - if (ret) - return raise(env, ret); - else - return enif_make_atom(env, "ok"); -} - -static ERL_NIF_TERM whereis_1(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - unsigned int len; - registry_t *entry; - ERL_NIF_TERM result; - - if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1)) - return raise(env, EINVAL); - - char name[len+1]; - enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1); - enif_rwlock_rlock(registry_lock); - HASH_FIND_STR(registry, name, entry); - if (entry) - result = enif_make_resource(env, entry->state); - else - result = enif_make_atom(env, "undefined"); - enif_rwlock_runlock(registry_lock); - - return result; -} - -static ERL_NIF_TERM registered_0(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - registry_t *entry, *iter; - ERL_NIF_TERM result = enif_make_list(env, 0); - - enif_rwlock_rlock(registry_lock); - HASH_ITER(hh, registry, entry, iter) { - result = enif_make_list_cell(env, enif_make_atom(env, entry->name), result); - } - enif_rwlock_runlock(registry_lock); - - return result; -} - -static ErlNifFunc nif_funcs[] = - { - {"new", 0, new_0}, - {"insert", 2, insert_2}, - {"delete", 2, delete_2}, - {"match", 2, match_2}, - {"refc", 2, refc_2}, - {"clear", 1, clear_1}, - {"size", 1, size_1}, - {"is_empty", 1, is_empty_1}, - {"to_list", 1, to_list_1}, - {"dump", 1, dump_1}, - {"register", 2, register_2}, - {"unregister", 1, unregister_1}, - {"whereis", 1, whereis_1}, - {"registered", 0, registered_0} - }; - -ERL_NIF_INIT(mqtree, nif_funcs, load, NULL, NULL, unload) diff --git a/c_src/.mqtree/rebar.config b/c_src/.mqtree/rebar.config deleted file mode 100644 index 4e29233..0000000 --- a/c_src/.mqtree/rebar.config +++ /dev/null @@ -1,12 +0,0 @@ -{port_specs, [ - {"../../priv/mqtree.so", ["*.c"]} -]}. - -{port_env, [ - {"CFLAGS", "$CFLAGS -std=c99 -g -O2 -Wall"}, - {"LDFLAGS", "$LDFLAGS -lpthread"} -]}. - - - - diff --git a/c_src/.mqtree/uthash.h b/c_src/.mqtree/uthash.h deleted file mode 100644 index 46edea0..0000000 --- a/c_src/.mqtree/uthash.h +++ /dev/null @@ -1,1074 +0,0 @@ -/* -Copyright (c) 2003-2017, Troy D. Hanson http://troydhanson.github.com/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef UTHASH_H -#define UTHASH_H - -#define UTHASH_VERSION 2.0.2 - -#include /* memcmp,strlen */ -#include /* ptrdiff_t */ -#include /* exit() */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#if defined(_MSC_VER) /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#define DECLTYPE(x) -#endif -#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) -#define NO_DECLTYPE -#define DECLTYPE(x) -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while (0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while (0) -#endif - -/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ -#if defined(_WIN32) -#if defined(_MSC_VER) && _MSC_VER >= 1600 -#include -#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif -#elif defined(__GNUC__) && !defined(__VXWORKS__) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -#endif -#ifndef uthash_malloc -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#endif -#ifndef uthash_free -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -#endif -#ifndef uthash_strlen -#define uthash_strlen(s) strlen(s) -#endif -#ifndef uthash_memcmp -#define uthash_memcmp(a,b,n) memcmp(a,b,n) -#endif - -#ifndef uthash_noexpand_fyi -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#endif -#ifndef uthash_expand_fyi -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -#endif - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhp */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) -/* calculate the hash handle from element address elp */ -#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) - -#define HASH_VALUE(keyptr,keylen,hashv) \ -do { \ - HASH_FCN(keyptr, keylen, hashv); \ -} while (0) - -#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ -do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_bkt; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ - } \ - } \ -} while (0) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - unsigned _hf_hashv; \ - HASH_VALUE(keyptr, keylen, _hf_hashv); \ - HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -#define HASH_BLOOM_MAKE(tbl) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ - memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -} while (0) - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0) - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) - -#else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) (1) -#define HASH_BLOOM_BYTELEN 0U -#endif - -#define HASH_MAKE_TABLE(hh,head) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ - sizeof(UT_hash_table)); \ - if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl->buckets, 0, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ -} while (0) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ -} while (0) - -#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ -} while (0) - -#define HASH_APPEND_LIST(hh, head, add) \ -do { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail->next = (add); \ - (head)->hh.tbl->tail = &((add)->hh); \ -} while (0) - -#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - (head) = (add); \ - HASH_MAKE_TABLE(hh, head); \ - } else { \ - void *_hs_iter = (head); \ - (add)->hh.tbl = (head)->hh.tbl; \ - do { \ - if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) \ - break; \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ - if (_hs_iter) { \ - (add)->hh.next = _hs_iter; \ - if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ - HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ - } else { \ - (head) = (add); \ - } \ - HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ - } else { \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - } \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ - HASH_FSCK(hh, head); \ -} while (0) - -#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ -do { \ - unsigned _hs_hashv; \ - HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) - -#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ - HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) - -#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - (head) = (add); \ - HASH_MAKE_TABLE(hh, head); \ - } else { \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ - HASH_FSCK(hh, head); \ -} while (0) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_hashv; \ - HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) - -#define HASH_TO_BKT(hashv,num_bkts,bkt) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1U)); \ -} while (0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-order doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ -do { \ - struct UT_hash_handle *_hd_hh_del; \ - if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - head = NULL; \ - } else { \ - unsigned _hd_bkt; \ - _hd_hh_del = &((delptr)->hh); \ - if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ - (head)->hh.tbl->tail = \ - (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho); \ - } \ - if ((delptr)->hh.prev != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ - } else { \ - DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ - } \ - if (_hd_hh_del->next != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ - (head)->hh.tbl->hho))->prev = \ - _hd_hh_del->prev; \ - } \ - HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh,head); \ -} while (0) - - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ - HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) -#define HASH_ADD_STR(head,strfield,add) \ - HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) -#define HASH_REPLACE_STR(head,strfield,add,replaced) \ - HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_REPLACE_INT(head,intfield,add,replaced) \ - HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ - HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head) \ -do { \ - struct UT_hash_handle *_thh; \ - if (head) { \ - unsigned _bkt_i; \ - unsigned _count; \ - char *_prev; \ - _count = 0; \ - for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ - unsigned _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("invalid hh_prev %p, actual %p\n", \ - _thh->hh_prev, _prev ); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("invalid bucket count %u, actual %u\n", \ - (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid hh item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - /* traverse hh in app order; check next/prev integrity, count */ \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev !=(char*)(_thh->prev)) { \ - HASH_OOPS("invalid prev %p, actual %p\n", \ - _thh->prev, _prev ); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ - (head)->hh.tbl->hho) : NULL ); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid app item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION -#define HASH_FCN HASH_FUNCTION -#else -#define HASH_FCN HASH_JEN -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -#define HASH_BER(key,keylen,hashv) \ -do { \ - unsigned _hb_keylen=(unsigned)keylen; \ - const unsigned char *_hb_key=(const unsigned char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen-- != 0U) { \ - (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ - } \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,hashv) \ -do { \ - unsigned _sx_i; \ - const unsigned char *_hs_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - } \ -} while (0) -/* FNV-1a variation */ -#define HASH_FNV(key,keylen,hashv) \ -do { \ - unsigned _fn_i; \ - const unsigned char *_hf_key=(const unsigned char*)(key); \ - hashv = 2166136261U; \ - for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ - hashv = hashv ^ _hf_key[_fn_i]; \ - hashv = hashv * 16777619U; \ - } \ -} while (0) - -#define HASH_OAT(key,keylen,hashv) \ -do { \ - unsigned _ho_i; \ - const unsigned char *_ho_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ -} while (0) - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -#define HASH_JEN(key,keylen,hashv) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - unsigned const char *_hj_key=(unsigned const char*)(key); \ - hashv = 0xfeedbeefu; \ - _hj_i = _hj_j = 0x9e3779b9u; \ - _hj_k = (unsigned)(keylen); \ - while (_hj_k >= 12U) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12U; \ - } \ - hashv += (unsigned)(keylen); \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ - case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ - case 1: _hj_i += _hj_key[0]; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ -} while (0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,hashv) \ -do { \ - unsigned const char *_sfh_key=(unsigned const char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ - \ - unsigned _sfh_rem = _sfh_len & 3U; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabeu; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0U; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2U*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ -} while (0) - -#ifdef HASH_USING_NO_STRICT_ALIASING -/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * MurmurHash uses the faster approach only on CPU's where we know it's safe. - * - * Note the preprocessor built-in defines can be emitted using: - * - * gcc -m64 -dM -E - < /dev/null (on gcc) - * cc -## a.c (where a.c is a simple test file) (Sun Studio) - */ -#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) -#define MUR_GETBLOCK(p,i) p[i] -#else /* non intel */ -#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) -#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) -#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) -#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) -#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) -#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) -#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) -#else /* assume little endian non-intel */ -#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) -#endif -#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ - (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ - (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ - MUR_ONE_THREE(p)))) -#endif -#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -#define MUR_FMIX(_h) \ -do { \ - _h ^= _h >> 16; \ - _h *= 0x85ebca6bu; \ - _h ^= _h >> 13; \ - _h *= 0xc2b2ae35u; \ - _h ^= _h >> 16; \ -} while (0) - -#define HASH_MUR(key,keylen,hashv) \ -do { \ - const uint8_t *_mur_data = (const uint8_t*)(key); \ - const int _mur_nblocks = (int)(keylen) / 4; \ - uint32_t _mur_h1 = 0xf88D5353u; \ - uint32_t _mur_c1 = 0xcc9e2d51u; \ - uint32_t _mur_c2 = 0x1b873593u; \ - uint32_t _mur_k1 = 0; \ - const uint8_t *_mur_tail; \ - const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ - int _mur_i; \ - for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ - _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - \ - _mur_h1 ^= _mur_k1; \ - _mur_h1 = MUR_ROTL32(_mur_h1,13); \ - _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ - } \ - _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ - _mur_k1=0; \ - switch((keylen) & 3U) { \ - case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ - case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ - case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - _mur_h1 ^= _mur_k1; \ - } \ - _mur_h1 ^= (uint32_t)(keylen); \ - MUR_FMIX(_mur_h1); \ - hashv = _mur_h1; \ -} while (0) -#endif /* HASH_USING_NO_STRICT_ALIASING */ - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ -do { \ - if ((head).hh_head != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ - } else { \ - (out) = NULL; \ - } \ - while ((out) != NULL) { \ - if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ - if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ - break; \ - } \ - } \ - if ((out)->hh.hh_next != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ - } else { \ - (out) = NULL; \ - } \ - } \ -} while (0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,addhh) \ -do { \ - head.count++; \ - (addhh)->hh_next = head.hh_head; \ - (addhh)->hh_prev = NULL; \ - if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ - (head).hh_head=addhh; \ - if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ - && ((addhh)->tbl->noexpand != 1U)) { \ - HASH_EXPAND_BUCKETS((addhh)->tbl); \ - } \ -} while (0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(hh,head,hh_del) \ - (head).count--; \ - if ((head).hh_head == hh_del) { \ - (head).hh_head = hh_del->hh_next; \ - } \ - if (hh_del->hh_prev) { \ - hh_del->hh_prev->hh_next = hh_del->hh_next; \ - } \ - if (hh_del->hh_next) { \ - hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(tbl) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ - memset(_he_new_buckets, 0, \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - tbl->ideal_chain_maxlen = \ - (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ - (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ - tbl->nonideal_items = 0; \ - for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ - { \ - _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh != NULL) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ - if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ - tbl->nonideal_items++; \ - _he_newbkt->expand_mult = _he_newbkt->count / \ - tbl->ideal_chain_maxlen; \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ - _he_thh; } \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - tbl->num_buckets *= 2U; \ - tbl->log2_num_buckets++; \ - tbl->buckets = _he_new_buckets; \ - tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ - (tbl->ineff_expands+1U) : 0U; \ - if (tbl->ineff_expands > 1U) { \ - tbl->noexpand=1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ -} while (0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head != NULL) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping != 0U) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p != NULL) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ - _hs_psize++; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - if (! (_hs_q) ) { break; } \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ - if (_hs_psize == 0U) { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else if (( \ - cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ - ) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail != NULL ) { \ - _hs_tail->next = ((_hs_e != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e != NULL) { \ - _hs_e->prev = ((_hs_tail != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - if (_hs_tail != NULL){ \ - _hs_tail->next = NULL; \ - } \ - if ( _hs_nmerges <= 1U ) { \ - _hs_looping=0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2U; \ - } \ - HASH_FSCK(hh,head); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt=NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if (src != NULL) { \ - for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh != NULL; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ - if (dst == NULL) { \ - DECLTYPE_ASSIGN(dst,_elt); \ - HASH_MAKE_TABLE(hh_dst,dst); \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ - (dst)->hh_dst.tbl->num_items++; \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst,dst); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if (head != NULL) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)=NULL; \ - } \ -} while (0) - -#define HASH_OVERHEAD(hh,head) \ - ((head != NULL) ? ( \ - (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ - ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ - sizeof(UT_hash_table) + \ - (HASH_BLOOM_BYTELEN))) : 0U) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1u -#define HASH_BLOOM_SIGNATURE 0xb12220f2u - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - uint8_t bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app order */ - void *next; /* next element in app order */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ - struct UT_hash_handle *hh_next; /* next hh in bucket order */ - void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/c_src/bitmap_filter/bitmap_filter.c b/c_src/bitmap_filter/bitmap_filter.c deleted file mode 100644 index 8ea2fe7..0000000 --- a/c_src/bitmap_filter/bitmap_filter.c +++ /dev/null @@ -1,80 +0,0 @@ -#include - -/* - This function expects a list of list of tuples of type {int, _}. - It filters the tuples, using the first int field as a key, - and removing duplicating keys with precedence given the the order - in which they were seen (first given precedence). -*/ -static ERL_NIF_TERM -bitmap_filter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - size_t seen_forklift_id[3000] = { 0 }; - - if(argc != 1) - { - return enif_make_badarg(env); - } - - if(!enif_is_list(env, argv[0])) - { - return enif_make_badarg(env); - } - - ERL_NIF_TERM ret = enif_make_list(env, 0); - - ERL_NIF_TERM outer_list = argv[0]; - ERL_NIF_TERM inner_list; - - ERL_NIF_TERM inner_head; - - const ERL_NIF_TERM* tuple_elems; - int num_elems; - unsigned int key; - - while(enif_get_list_cell(env, outer_list, &inner_list, &outer_list)) - { - if(!enif_is_list(env, inner_list)) - { - return enif_make_badarg(env); - } - - while(enif_get_list_cell(env, inner_list, &inner_head, &inner_list)) - { - if(!enif_get_tuple(env, inner_head, &num_elems, &tuple_elems)) - { - return enif_make_badarg(env); - } - - if(num_elems != 2) - { - return enif_make_badarg(env); - } - - if(!enif_get_uint(env, tuple_elems[0], &key)) - { - return enif_make_badarg(env); - } - - if(key >= 3000) - { - return enif_make_badarg(env); - } - - if(!seen_forklift_id[key]) - { - seen_forklift_id[key] = 1; - ret = enif_make_list_cell(env, inner_head, ret); - } - } - } - - return ret; -} - -static ErlNifFunc nif_funcs[] = -{ - {"filter", 1, bitmap_filter, 0} -}; - -ERL_NIF_INIT(bitmap_filter, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/c_src/bitmap_filter/rebar.config b/c_src/bitmap_filter/rebar.config deleted file mode 100644 index 0dfa1a9..0000000 --- a/c_src/bitmap_filter/rebar.config +++ /dev/null @@ -1,30 +0,0 @@ -{port_specs, [ - {"../../priv/bitmap_filter.so", [ - "*.c" - ]} -]}. -%{port_specs, [{"../../priv/granderl.so", []}]}. - -%% {port_env, [ -%% {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", -%% "CFLAGS", "$CFLAGS -Ic_src/ -g -Wall -flto -Werror -O3"}, -%% {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", -%% "CXXFLAGS", "$CXXFLAGS -Ic_src/ -g -Wall -flto -Werror -O3"}, -%% -%% {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", -%% "LDFLAGS", "$LDFLAGS -flto -lstdc++"}, -%% -%% %% OS X Leopard flags for 64-bit -%% {"darwin9.*-64$", "CXXFLAGS", "-m64"}, -%% {"darwin9.*-64$", "LDFLAGS", "-arch x86_64"}, -%% -%% %% OS X Snow Leopard flags for 32-bit -%% {"darwin10.*-32$", "CXXFLAGS", "-m32"}, -%% {"darwin10.*-32$", "LDFLAGS", "-arch i386"}, -%% -%% {"win32", "CXXFLAGS", "$CXXFLAGS /O2 /DNDEBUG"} -%% ]}. - - - - diff --git a/c_src/bsn/bsn_ext.c b/c_src/bsn/bsn_ext.c deleted file mode 100644 index 2ea8d9a..0000000 --- a/c_src/bsn/bsn_ext.c +++ /dev/null @@ -1,448 +0,0 @@ -#include "erl_nif.h" - -ErlNifResourceType* bsn_type; -ERL_NIF_TERM ATOM_TRUE, ATOM_FALSE; - -/* -typedef struct { - unsigned size; - unsigned char* data; -} ErlNifBinary; - -*/ - -struct bsn_elem_struct { - ErlNifBinary bin; - struct bsn_elem_struct* next; -}; -typedef struct bsn_elem_struct bsn_elem; - -typedef bsn_elem* bsn_list; - -typedef struct { - unsigned int count; /* count of elements */ - unsigned int max; /* count of slots */ - ErlNifMutex *mutex; - bsn_list* list; -} bsn_res; - - -inline static ERL_NIF_TERM bool_to_term(int value) { - return value ? ATOM_TRUE : ATOM_FALSE; -} - -/* Calculate the sum of chars. */ -unsigned int -private_hash(const ErlNifBinary* b, unsigned int max) -{ - unsigned char* ptr; - unsigned int i, sum = 0; - - ptr = b->data; - i = b->size; - - for (; i; i--, ptr++) - sum += *ptr; - - return sum % max; -} - -inline void -private_clear_elem(bsn_elem* el) -{ - enif_release_binary(&(el->bin)); - enif_free(el); -} - -inline void -private_chain_clear_all(bsn_elem* ptr) -{ - bsn_elem* next; - - while (ptr != NULL) { - - next = ptr->next; - private_clear_elem(ptr); - ptr = next; - } -} - -inline int -private_compare(ErlNifBinary* b1, ErlNifBinary* b2) -{ - unsigned char* p1; - unsigned char* p2; - unsigned len; - - if (b1->size != b2->size) - return 0; - - p1 = b1->data; - p2 = b2->data; - len = b1->size; - - while (len) { - if ((*p1) != (*p2)) - return 0; - - len--; p1++; p2++; - } - return 1; -} - -/* Skip existing elements. If the element bin is not found, return last element. - * If el.bin == bin, return el. */ -bsn_elem* -private_chain_shift(bsn_elem* ptr, ErlNifBinary* bin, int* num_ptr) -{ - (*num_ptr)++; - if ((ptr) == NULL) - return ptr; - - while (1) { - if (private_compare(&(ptr->bin), bin)) { - /* found an equal binary. Invert num */ - (*num_ptr) *= -1; - return ptr; - } - if ((ptr->next) == NULL) - return ptr; - ptr = ptr->next; - (*num_ptr)++; - } -} - -/* Append the element `el' to the chain `chain' */ -void -private_chain_append(bsn_elem** chain, bsn_elem* el, int* num_ptr) -{ - bsn_elem* last; - - if ((*chain) == NULL) { - /* The new element is last */ - *chain = el; - } else { - last = private_chain_shift(*chain, &(el->bin), num_ptr); - if ((*num_ptr) < 0) { - /* Element was already added. */ - private_clear_elem(el); - } else { - last->next = el; - } - } -} - -bsn_elem* -private_chain_shift_clear(bsn_elem** ptr, ErlNifBinary* bin, int* num_ptr) -{ - bsn_elem** prev = NULL; - bsn_elem* el; - - while ((*ptr) != NULL) { - if (private_compare(&((*ptr)->bin), bin)) { - (*num_ptr) *= -1; - - /* found an equal binary. Delete elem. Invert num */ - if (prev == NULL) { - el = *ptr; - (*ptr) = (*ptr)->next; - return el; - } - *prev = (*ptr)->next; - return *ptr; - } - prev = ptr; - el = *ptr; - ptr = (bsn_elem**) &(el->next); - (*num_ptr)++; - } - - return NULL; -} - -static ERL_NIF_TERM -bsn_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - unsigned int max; - bsn_list* ptr; - bsn_res* r; - - if (!(enif_get_uint(env, argv[0], &max) && (max>0))) - return enif_make_badarg(env); - - ptr = enif_alloc(sizeof(bsn_list) * max); - if (ptr == NULL) - return enif_make_badarg(env); - - r = (bsn_res*) enif_alloc_resource(bsn_type, sizeof(bsn_res)); - r->mutex = enif_mutex_create("Mutex for the BSN writer"); - r->count = 0; - r->max = max; - r->list = ptr; - - for (; max; max--, ptr++) - *ptr = NULL; - - return enif_make_resource(env, r); -} - -static ERL_NIF_TERM -bsn_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos; - int num = 0; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - enif_realloc_binary(&bin, bin.size); - pos = private_hash(&bin, r->max); - - elem_ptr = enif_alloc(sizeof(bsn_elem)); - if (elem_ptr == NULL) - return enif_make_badarg(env); - - elem_ptr->next = NULL; - elem_ptr->bin = bin; - - enif_mutex_lock(r->mutex); - private_chain_append(&(r->list[pos]), elem_ptr, &num); - if (num >= 0) - (r->count)++; - enif_mutex_unlock(r->mutex); - - /* Already added */ - if (num < 0) - enif_release_binary(&(bin)); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_search(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos; - int num = 0; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - pos = private_hash(&bin, r->max); - - enif_mutex_lock(r->mutex); - private_chain_shift(r->list[pos], &bin, &num); - enif_mutex_unlock(r->mutex); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos; - int num = 0; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - pos = private_hash(&bin, r->max); - - enif_mutex_lock(r->mutex); - elem_ptr = private_chain_shift_clear(&(r->list[pos]), &bin, &num); - if (elem_ptr != NULL) { - private_clear_elem(elem_ptr); - (r->count)--; - } - enif_mutex_unlock(r->mutex); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_all_chain(ErlNifEnv* env, bsn_elem* e, ERL_NIF_TERM tail) -{ - ERL_NIF_TERM head; - ErlNifBinary bin; - while (e != NULL) { - bin = e->bin; - enif_realloc_binary(&bin, bin.size); - head = enif_make_binary(env, &bin); - tail = enif_make_list_cell(env, head, tail); - e = e->next; - } - return tail; -} - -static ERL_NIF_TERM -bsn_chains(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - unsigned int max; - bsn_list* ptr; - ERL_NIF_TERM tail, head; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - tail = enif_make_list(env, 0); - - ptr = r->list; - - enif_mutex_lock(r->mutex); - max = r->max; - - while (max) { - head = enif_make_list(env, 0); - head = bsn_all_chain(env, *ptr, head); - tail = enif_make_list_cell(env, head, tail); - - ptr++; - max--; - } - enif_mutex_unlock(r->mutex); - - return tail; -} - -static ERL_NIF_TERM -bsn_all(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - unsigned int max; - bsn_list* ptr; - ERL_NIF_TERM list; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - list = enif_make_list(env, 0); - - ptr = r->list; - - enif_mutex_lock(r->mutex); - max = r->max; - - while (max) { - list = bsn_all_chain(env, *ptr, list); - ptr++; - max--; - } - enif_mutex_unlock(r->mutex); - - return list; -} - - -static ERL_NIF_TERM -bsn_count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - - return enif_make_int(env, r->count); -} - - -static ERL_NIF_TERM -bsn_hash(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - unsigned int max; - - if (!(enif_inspect_binary(env, argv[0], &bin) - && enif_get_uint(env, argv[1], &max) && (max>0))) - return enif_make_badarg(env); - - return enif_make_uint(env, - private_hash(&bin, max)); -} - - -static ERL_NIF_TERM -bsn_compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary b1, b2; - - if (!(enif_inspect_binary(env, argv[0], &b1) - && enif_inspect_binary(env, argv[1], &b2))) - return enif_make_badarg(env); - - return bool_to_term(private_compare(&b1, &b2)); -} - -void private_clear_all(bsn_res* r) -{ - unsigned int max; - bsn_list* ptr; - max = r->max; - ptr = r->list; - - while (max) { - private_chain_clear_all(*ptr); - ptr++; - max--; - } -} - -void -bsn_type_dtor(ErlNifEnv* env, void* obj) -{ - bsn_res* r = (bsn_res*) obj; - private_clear_all(r); - enif_mutex_destroy(r->mutex); - enif_free(r->list); -} - - - -int -on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - ATOM_TRUE = enif_make_atom(env, "true"); - ATOM_FALSE = enif_make_atom(env, "false"); - - ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | - ERL_NIF_RT_TAKEOVER); - - bsn_type = enif_open_resource_type(env, NULL, "bsn_type", - bsn_type_dtor, flags, NULL); - - if (bsn_type == NULL) return 1; - - return 0; -} - - -int -on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) -{ - return 0; -} - - -static ErlNifFunc nif_functions[] = { - {"new", 1, bsn_new}, - {"add", 2, bsn_add}, - {"all", 1, bsn_all}, - {"chains", 1, bsn_chains}, - {"in", 2, bsn_search}, - {"clear", 2, bsn_clear}, - {"count", 1, bsn_count}, - - {"hash", 2, bsn_hash}, - {"compare", 2, bsn_compare}, -}; - - -ERL_NIF_INIT(bsn_ext, nif_functions, &on_load, &on_load, &on_upgrade, NULL); diff --git a/c_src/bsn/bsn_int.c b/c_src/bsn/bsn_int.c deleted file mode 100644 index 30e2944..0000000 --- a/c_src/bsn/bsn_int.c +++ /dev/null @@ -1,331 +0,0 @@ -#include "erl_nif.h" - - -ErlNifResourceType* bsn_type; -ERL_NIF_TERM ATOM_TRUE, ATOM_FALSE, ATOM_NO_MORE; - -struct bsn_elem_struct { - ErlNifBinary bin; - unsigned int hash; -}; -typedef struct bsn_elem_struct bsn_elem; - - -typedef struct { - unsigned int count; /* count of elements */ - unsigned int max; /* count of slots */ - ErlNifMutex *mutex; - bsn_elem* list; - unsigned int (*next_pos) - (void*, unsigned int, unsigned int); -} bsn_res; - - -inline static ERL_NIF_TERM bool_to_term(int value) { - return value ? ATOM_TRUE : ATOM_FALSE; -} - -unsigned int next_pos_linear(bsn_res* r, unsigned int hash, unsigned int step) { - return (hash + step) % (r->max); -} - -unsigned int next_pos_quadric(bsn_res* r, unsigned int hash, unsigned int step) { - return (hash + (step*step)) % (r->max); -} - -/* Calculate the sum of chars. */ -unsigned int -private_hash(const ErlNifBinary* b, unsigned int max) -{ - unsigned char* ptr; - unsigned int i, sum = 0; - - ptr = b->data; - i = b->size; - - for (; i; i--, ptr++) - sum += *ptr; - - return sum % max; -} - - - -inline int -private_compare(ErlNifBinary* b1, ErlNifBinary* b2) -{ - unsigned char* p1; - unsigned char* p2; - unsigned len; - - if (b1->size != b2->size) - return 0; - - p1 = b1->data; - p2 = b2->data; - len = b1->size; - - while (len) { - if ((*p1) != (*p2)) - return 0; - - len--; p1++; p2++; - } - return 1; -} - - -static ERL_NIF_TERM -bsn_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int max; /* This value will be set by a client: - if (max<0) -> use quadric algorithm */ - bsn_elem* ptr; - bsn_res* r; - - if (!enif_get_int(env, argv[0], &max) || (max == 0)) - return enif_make_badarg(env); - - - r = (bsn_res*) enif_alloc_resource(bsn_type, sizeof(bsn_res)); - r->mutex = enif_mutex_create("Mutex for the BSN writer"); - r->count = 0; - - /* Select an algorithm */ - if (max>0) { - r->next_pos = &next_pos_linear; - } else if (max<0) { - r->next_pos = &next_pos_quadric; - max *= -1; - } - /* Now max is cells' count in the array. */ - r->max = (unsigned int) max; - - ptr = enif_alloc(sizeof(bsn_elem) * max); - if (ptr == NULL) - return enif_make_badarg(env); - r->list = ptr; - - for (; max; max--, ptr++) - ptr->hash = r->max; - - - return enif_make_resource(env, r); -} - -static ERL_NIF_TERM -bsn_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos, hash, max; - int num = 0; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - enif_realloc_binary(&bin, bin.size); - hash = pos = private_hash(&bin, r->max); - - - enif_mutex_lock(r->mutex); - max = r->max; - - while (num < max) { - elem_ptr = &(r->list[pos]); - /* Found free space */ - if (elem_ptr->hash == max) { - elem_ptr->bin = bin; - elem_ptr->hash = hash; - break; - } - - - /* Found elem */ - if ((elem_ptr->hash == hash) - && private_compare(&bin, &(elem_ptr->bin))) { - num *= -1; - break; - } - - pos = (r->next_pos)(r, hash, num); - num++; - } - if ((num >= 0) && (num < max)) - (r->count)++; - - enif_mutex_unlock(r->mutex); - - /* Error: already added or owerflow */ - if (!((num >= 0) && (num < max))) - enif_release_binary(&bin); - - if (num >= max) - return ATOM_NO_MORE; - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_search(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos, max, hash; - int num = 1; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - hash = pos = private_hash(&bin, r->max); - - enif_mutex_lock(r->mutex); - max = r->max; - - while (num < max) { - elem_ptr = &(r->list[pos]); - /* Found free space */ - if (elem_ptr->hash == max) { - break; - } - - - /* Found elem */ - if ((elem_ptr->hash == hash) - && private_compare(&bin, &(elem_ptr->bin))) { - num *= -1; - break; - } - - pos = (r->next_pos)(r, hash, num); - num++; - } - enif_mutex_unlock(r->mutex); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - return enif_make_badarg(env); -} - - -static ERL_NIF_TERM -bsn_all(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - unsigned int max, pos = 0; - ERL_NIF_TERM head, tail; - ErlNifBinary bin; - bsn_elem* elem_ptr; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - tail = enif_make_list(env, 0); - - enif_mutex_lock(r->mutex); - max = r->max; - elem_ptr = r->list; - - do { - - if (elem_ptr->hash != max) { - bin = elem_ptr->bin; - enif_realloc_binary(&bin, bin.size); - head = enif_make_binary(env, &bin); - tail = enif_make_list_cell(env, head, tail); - } - - elem_ptr++; - pos++; - } while (pos < max); - - enif_mutex_unlock(r->mutex); - - return tail; -} - - -static ERL_NIF_TERM -bsn_count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - - return enif_make_int(env, r->count); -} - - -void private_clear_all(bsn_res* r) -{ - unsigned int max, num; - bsn_elem* ptr; - num = max = r->max; - ptr = r->list; - - while (num) { - if (ptr->hash != max) { - enif_release_binary(&(ptr->bin)); - } - ptr++; - num--; - } -} - -void -bsn_type_dtor(ErlNifEnv* env, void* obj) -{ - bsn_res* r = (bsn_res*) obj; - private_clear_all(r); - enif_mutex_destroy(r->mutex); - enif_free(r->list); -} - - - -int -on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - ATOM_TRUE = enif_make_atom(env, "true"); - ATOM_FALSE = enif_make_atom(env, "false"); - ATOM_NO_MORE = enif_make_atom(env, "no_more"); - - - ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | - ERL_NIF_RT_TAKEOVER); - - bsn_type = enif_open_resource_type(env, NULL, "bsn_type", - bsn_type_dtor, flags, NULL); - - if (bsn_type == NULL) return 1; - - return 0; -} - - -int -on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) -{ - return 0; -} - - -static ErlNifFunc nif_functions[] = { - {"new", 1, bsn_new}, - {"add", 2, bsn_add}, - {"all", 1, bsn_all}, - {"in", 2, bsn_search}, - {"clear", 2, bsn_clear}, - {"count", 1, bsn_count}, -}; - - -ERL_NIF_INIT(bsn_int, nif_functions, &on_load, &on_load, &on_upgrade, NULL); diff --git a/c_src/bsn/c_src/bsn_ext.c b/c_src/bsn/c_src/bsn_ext.c deleted file mode 100644 index 2ea8d9a..0000000 --- a/c_src/bsn/c_src/bsn_ext.c +++ /dev/null @@ -1,448 +0,0 @@ -#include "erl_nif.h" - -ErlNifResourceType* bsn_type; -ERL_NIF_TERM ATOM_TRUE, ATOM_FALSE; - -/* -typedef struct { - unsigned size; - unsigned char* data; -} ErlNifBinary; - -*/ - -struct bsn_elem_struct { - ErlNifBinary bin; - struct bsn_elem_struct* next; -}; -typedef struct bsn_elem_struct bsn_elem; - -typedef bsn_elem* bsn_list; - -typedef struct { - unsigned int count; /* count of elements */ - unsigned int max; /* count of slots */ - ErlNifMutex *mutex; - bsn_list* list; -} bsn_res; - - -inline static ERL_NIF_TERM bool_to_term(int value) { - return value ? ATOM_TRUE : ATOM_FALSE; -} - -/* Calculate the sum of chars. */ -unsigned int -private_hash(const ErlNifBinary* b, unsigned int max) -{ - unsigned char* ptr; - unsigned int i, sum = 0; - - ptr = b->data; - i = b->size; - - for (; i; i--, ptr++) - sum += *ptr; - - return sum % max; -} - -inline void -private_clear_elem(bsn_elem* el) -{ - enif_release_binary(&(el->bin)); - enif_free(el); -} - -inline void -private_chain_clear_all(bsn_elem* ptr) -{ - bsn_elem* next; - - while (ptr != NULL) { - - next = ptr->next; - private_clear_elem(ptr); - ptr = next; - } -} - -inline int -private_compare(ErlNifBinary* b1, ErlNifBinary* b2) -{ - unsigned char* p1; - unsigned char* p2; - unsigned len; - - if (b1->size != b2->size) - return 0; - - p1 = b1->data; - p2 = b2->data; - len = b1->size; - - while (len) { - if ((*p1) != (*p2)) - return 0; - - len--; p1++; p2++; - } - return 1; -} - -/* Skip existing elements. If the element bin is not found, return last element. - * If el.bin == bin, return el. */ -bsn_elem* -private_chain_shift(bsn_elem* ptr, ErlNifBinary* bin, int* num_ptr) -{ - (*num_ptr)++; - if ((ptr) == NULL) - return ptr; - - while (1) { - if (private_compare(&(ptr->bin), bin)) { - /* found an equal binary. Invert num */ - (*num_ptr) *= -1; - return ptr; - } - if ((ptr->next) == NULL) - return ptr; - ptr = ptr->next; - (*num_ptr)++; - } -} - -/* Append the element `el' to the chain `chain' */ -void -private_chain_append(bsn_elem** chain, bsn_elem* el, int* num_ptr) -{ - bsn_elem* last; - - if ((*chain) == NULL) { - /* The new element is last */ - *chain = el; - } else { - last = private_chain_shift(*chain, &(el->bin), num_ptr); - if ((*num_ptr) < 0) { - /* Element was already added. */ - private_clear_elem(el); - } else { - last->next = el; - } - } -} - -bsn_elem* -private_chain_shift_clear(bsn_elem** ptr, ErlNifBinary* bin, int* num_ptr) -{ - bsn_elem** prev = NULL; - bsn_elem* el; - - while ((*ptr) != NULL) { - if (private_compare(&((*ptr)->bin), bin)) { - (*num_ptr) *= -1; - - /* found an equal binary. Delete elem. Invert num */ - if (prev == NULL) { - el = *ptr; - (*ptr) = (*ptr)->next; - return el; - } - *prev = (*ptr)->next; - return *ptr; - } - prev = ptr; - el = *ptr; - ptr = (bsn_elem**) &(el->next); - (*num_ptr)++; - } - - return NULL; -} - -static ERL_NIF_TERM -bsn_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - unsigned int max; - bsn_list* ptr; - bsn_res* r; - - if (!(enif_get_uint(env, argv[0], &max) && (max>0))) - return enif_make_badarg(env); - - ptr = enif_alloc(sizeof(bsn_list) * max); - if (ptr == NULL) - return enif_make_badarg(env); - - r = (bsn_res*) enif_alloc_resource(bsn_type, sizeof(bsn_res)); - r->mutex = enif_mutex_create("Mutex for the BSN writer"); - r->count = 0; - r->max = max; - r->list = ptr; - - for (; max; max--, ptr++) - *ptr = NULL; - - return enif_make_resource(env, r); -} - -static ERL_NIF_TERM -bsn_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos; - int num = 0; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - enif_realloc_binary(&bin, bin.size); - pos = private_hash(&bin, r->max); - - elem_ptr = enif_alloc(sizeof(bsn_elem)); - if (elem_ptr == NULL) - return enif_make_badarg(env); - - elem_ptr->next = NULL; - elem_ptr->bin = bin; - - enif_mutex_lock(r->mutex); - private_chain_append(&(r->list[pos]), elem_ptr, &num); - if (num >= 0) - (r->count)++; - enif_mutex_unlock(r->mutex); - - /* Already added */ - if (num < 0) - enif_release_binary(&(bin)); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_search(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos; - int num = 0; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - pos = private_hash(&bin, r->max); - - enif_mutex_lock(r->mutex); - private_chain_shift(r->list[pos], &bin, &num); - enif_mutex_unlock(r->mutex); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos; - int num = 0; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - pos = private_hash(&bin, r->max); - - enif_mutex_lock(r->mutex); - elem_ptr = private_chain_shift_clear(&(r->list[pos]), &bin, &num); - if (elem_ptr != NULL) { - private_clear_elem(elem_ptr); - (r->count)--; - } - enif_mutex_unlock(r->mutex); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_all_chain(ErlNifEnv* env, bsn_elem* e, ERL_NIF_TERM tail) -{ - ERL_NIF_TERM head; - ErlNifBinary bin; - while (e != NULL) { - bin = e->bin; - enif_realloc_binary(&bin, bin.size); - head = enif_make_binary(env, &bin); - tail = enif_make_list_cell(env, head, tail); - e = e->next; - } - return tail; -} - -static ERL_NIF_TERM -bsn_chains(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - unsigned int max; - bsn_list* ptr; - ERL_NIF_TERM tail, head; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - tail = enif_make_list(env, 0); - - ptr = r->list; - - enif_mutex_lock(r->mutex); - max = r->max; - - while (max) { - head = enif_make_list(env, 0); - head = bsn_all_chain(env, *ptr, head); - tail = enif_make_list_cell(env, head, tail); - - ptr++; - max--; - } - enif_mutex_unlock(r->mutex); - - return tail; -} - -static ERL_NIF_TERM -bsn_all(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - unsigned int max; - bsn_list* ptr; - ERL_NIF_TERM list; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - list = enif_make_list(env, 0); - - ptr = r->list; - - enif_mutex_lock(r->mutex); - max = r->max; - - while (max) { - list = bsn_all_chain(env, *ptr, list); - ptr++; - max--; - } - enif_mutex_unlock(r->mutex); - - return list; -} - - -static ERL_NIF_TERM -bsn_count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - - return enif_make_int(env, r->count); -} - - -static ERL_NIF_TERM -bsn_hash(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - unsigned int max; - - if (!(enif_inspect_binary(env, argv[0], &bin) - && enif_get_uint(env, argv[1], &max) && (max>0))) - return enif_make_badarg(env); - - return enif_make_uint(env, - private_hash(&bin, max)); -} - - -static ERL_NIF_TERM -bsn_compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary b1, b2; - - if (!(enif_inspect_binary(env, argv[0], &b1) - && enif_inspect_binary(env, argv[1], &b2))) - return enif_make_badarg(env); - - return bool_to_term(private_compare(&b1, &b2)); -} - -void private_clear_all(bsn_res* r) -{ - unsigned int max; - bsn_list* ptr; - max = r->max; - ptr = r->list; - - while (max) { - private_chain_clear_all(*ptr); - ptr++; - max--; - } -} - -void -bsn_type_dtor(ErlNifEnv* env, void* obj) -{ - bsn_res* r = (bsn_res*) obj; - private_clear_all(r); - enif_mutex_destroy(r->mutex); - enif_free(r->list); -} - - - -int -on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - ATOM_TRUE = enif_make_atom(env, "true"); - ATOM_FALSE = enif_make_atom(env, "false"); - - ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | - ERL_NIF_RT_TAKEOVER); - - bsn_type = enif_open_resource_type(env, NULL, "bsn_type", - bsn_type_dtor, flags, NULL); - - if (bsn_type == NULL) return 1; - - return 0; -} - - -int -on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) -{ - return 0; -} - - -static ErlNifFunc nif_functions[] = { - {"new", 1, bsn_new}, - {"add", 2, bsn_add}, - {"all", 1, bsn_all}, - {"chains", 1, bsn_chains}, - {"in", 2, bsn_search}, - {"clear", 2, bsn_clear}, - {"count", 1, bsn_count}, - - {"hash", 2, bsn_hash}, - {"compare", 2, bsn_compare}, -}; - - -ERL_NIF_INIT(bsn_ext, nif_functions, &on_load, &on_load, &on_upgrade, NULL); diff --git a/c_src/bsn/c_src/bsn_int.c b/c_src/bsn/c_src/bsn_int.c deleted file mode 100644 index 30e2944..0000000 --- a/c_src/bsn/c_src/bsn_int.c +++ /dev/null @@ -1,331 +0,0 @@ -#include "erl_nif.h" - - -ErlNifResourceType* bsn_type; -ERL_NIF_TERM ATOM_TRUE, ATOM_FALSE, ATOM_NO_MORE; - -struct bsn_elem_struct { - ErlNifBinary bin; - unsigned int hash; -}; -typedef struct bsn_elem_struct bsn_elem; - - -typedef struct { - unsigned int count; /* count of elements */ - unsigned int max; /* count of slots */ - ErlNifMutex *mutex; - bsn_elem* list; - unsigned int (*next_pos) - (void*, unsigned int, unsigned int); -} bsn_res; - - -inline static ERL_NIF_TERM bool_to_term(int value) { - return value ? ATOM_TRUE : ATOM_FALSE; -} - -unsigned int next_pos_linear(bsn_res* r, unsigned int hash, unsigned int step) { - return (hash + step) % (r->max); -} - -unsigned int next_pos_quadric(bsn_res* r, unsigned int hash, unsigned int step) { - return (hash + (step*step)) % (r->max); -} - -/* Calculate the sum of chars. */ -unsigned int -private_hash(const ErlNifBinary* b, unsigned int max) -{ - unsigned char* ptr; - unsigned int i, sum = 0; - - ptr = b->data; - i = b->size; - - for (; i; i--, ptr++) - sum += *ptr; - - return sum % max; -} - - - -inline int -private_compare(ErlNifBinary* b1, ErlNifBinary* b2) -{ - unsigned char* p1; - unsigned char* p2; - unsigned len; - - if (b1->size != b2->size) - return 0; - - p1 = b1->data; - p2 = b2->data; - len = b1->size; - - while (len) { - if ((*p1) != (*p2)) - return 0; - - len--; p1++; p2++; - } - return 1; -} - - -static ERL_NIF_TERM -bsn_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int max; /* This value will be set by a client: - if (max<0) -> use quadric algorithm */ - bsn_elem* ptr; - bsn_res* r; - - if (!enif_get_int(env, argv[0], &max) || (max == 0)) - return enif_make_badarg(env); - - - r = (bsn_res*) enif_alloc_resource(bsn_type, sizeof(bsn_res)); - r->mutex = enif_mutex_create("Mutex for the BSN writer"); - r->count = 0; - - /* Select an algorithm */ - if (max>0) { - r->next_pos = &next_pos_linear; - } else if (max<0) { - r->next_pos = &next_pos_quadric; - max *= -1; - } - /* Now max is cells' count in the array. */ - r->max = (unsigned int) max; - - ptr = enif_alloc(sizeof(bsn_elem) * max); - if (ptr == NULL) - return enif_make_badarg(env); - r->list = ptr; - - for (; max; max--, ptr++) - ptr->hash = r->max; - - - return enif_make_resource(env, r); -} - -static ERL_NIF_TERM -bsn_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos, hash, max; - int num = 0; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - enif_realloc_binary(&bin, bin.size); - hash = pos = private_hash(&bin, r->max); - - - enif_mutex_lock(r->mutex); - max = r->max; - - while (num < max) { - elem_ptr = &(r->list[pos]); - /* Found free space */ - if (elem_ptr->hash == max) { - elem_ptr->bin = bin; - elem_ptr->hash = hash; - break; - } - - - /* Found elem */ - if ((elem_ptr->hash == hash) - && private_compare(&bin, &(elem_ptr->bin))) { - num *= -1; - break; - } - - pos = (r->next_pos)(r, hash, num); - num++; - } - if ((num >= 0) && (num < max)) - (r->count)++; - - enif_mutex_unlock(r->mutex); - - /* Error: already added or owerflow */ - if (!((num >= 0) && (num < max))) - enif_release_binary(&bin); - - if (num >= max) - return ATOM_NO_MORE; - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_search(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - bsn_res* r; - unsigned int pos, max, hash; - int num = 1; - bsn_elem* elem_ptr; - - if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r) - && enif_inspect_binary(env, argv[1], &bin))) - return enif_make_badarg(env); - - hash = pos = private_hash(&bin, r->max); - - enif_mutex_lock(r->mutex); - max = r->max; - - while (num < max) { - elem_ptr = &(r->list[pos]); - /* Found free space */ - if (elem_ptr->hash == max) { - break; - } - - - /* Found elem */ - if ((elem_ptr->hash == hash) - && private_compare(&bin, &(elem_ptr->bin))) { - num *= -1; - break; - } - - pos = (r->next_pos)(r, hash, num); - num++; - } - enif_mutex_unlock(r->mutex); - - return enif_make_int(env, num); -} - -static ERL_NIF_TERM -bsn_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - return enif_make_badarg(env); -} - - -static ERL_NIF_TERM -bsn_all(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - unsigned int max, pos = 0; - ERL_NIF_TERM head, tail; - ErlNifBinary bin; - bsn_elem* elem_ptr; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - tail = enif_make_list(env, 0); - - enif_mutex_lock(r->mutex); - max = r->max; - elem_ptr = r->list; - - do { - - if (elem_ptr->hash != max) { - bin = elem_ptr->bin; - enif_realloc_binary(&bin, bin.size); - head = enif_make_binary(env, &bin); - tail = enif_make_list_cell(env, head, tail); - } - - elem_ptr++; - pos++; - } while (pos < max); - - enif_mutex_unlock(r->mutex); - - return tail; -} - - -static ERL_NIF_TERM -bsn_count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - bsn_res* r; - - if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) - return enif_make_badarg(env); - - return enif_make_int(env, r->count); -} - - -void private_clear_all(bsn_res* r) -{ - unsigned int max, num; - bsn_elem* ptr; - num = max = r->max; - ptr = r->list; - - while (num) { - if (ptr->hash != max) { - enif_release_binary(&(ptr->bin)); - } - ptr++; - num--; - } -} - -void -bsn_type_dtor(ErlNifEnv* env, void* obj) -{ - bsn_res* r = (bsn_res*) obj; - private_clear_all(r); - enif_mutex_destroy(r->mutex); - enif_free(r->list); -} - - - -int -on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - ATOM_TRUE = enif_make_atom(env, "true"); - ATOM_FALSE = enif_make_atom(env, "false"); - ATOM_NO_MORE = enif_make_atom(env, "no_more"); - - - ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | - ERL_NIF_RT_TAKEOVER); - - bsn_type = enif_open_resource_type(env, NULL, "bsn_type", - bsn_type_dtor, flags, NULL); - - if (bsn_type == NULL) return 1; - - return 0; -} - - -int -on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) -{ - return 0; -} - - -static ErlNifFunc nif_functions[] = { - {"new", 1, bsn_new}, - {"add", 2, bsn_add}, - {"all", 1, bsn_all}, - {"in", 2, bsn_search}, - {"clear", 2, bsn_clear}, - {"count", 1, bsn_count}, -}; - - -ERL_NIF_INIT(bsn_int, nif_functions, &on_load, &on_load, &on_upgrade, NULL); diff --git a/c_src/bsn/rebar.config b/c_src/bsn/rebar.config deleted file mode 100644 index 77bc6d8..0000000 --- a/c_src/bsn/rebar.config +++ /dev/null @@ -1,29 +0,0 @@ -{port_specs, [ - {"../../priv/bsn_ext.so", ["bsn_ext.c"]}, - {"../../priv/bsn_int.so", ["bsn_int.c"]} -]}. -%{port_specs, [{"../../priv/granderl.so", []}]}. - -%% {port_env, [ -%% {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", -%% "CFLAGS", "$CFLAGS -Ic_src/ -g -Wall -flto -Werror -O3"}, -%% {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", -%% "CXXFLAGS", "$CXXFLAGS -Ic_src/ -g -Wall -flto -Werror -O3"}, -%% -%% {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", -%% "LDFLAGS", "$LDFLAGS -flto -lstdc++"}, -%% -%% %% OS X Leopard flags for 64-bit -%% {"darwin9.*-64$", "CXXFLAGS", "-m64"}, -%% {"darwin9.*-64$", "LDFLAGS", "-arch x86_64"}, -%% -%% %% OS X Snow Leopard flags for 32-bit -%% {"darwin10.*-32$", "CXXFLAGS", "-m32"}, -%% {"darwin10.*-32$", "LDFLAGS", "-arch i386"}, -%% -%% {"win32", "CXXFLAGS", "$CXXFLAGS /O2 /DNDEBUG"} -%% ]}. - - - - diff --git a/c_src/couchdb-khash2/hash.c b/c_src/couchdb-khash2/hash.c deleted file mode 100644 index e3da0da..0000000 --- a/c_src/couchdb-khash2/hash.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Hash Table Data Type - * Copyright (C) 1997 Kaz Kylheku - * - * Free Software License: - * - * All rights are reserved by the author, with the following exceptions: - * Permission is granted to freely reproduce and distribute this software, - * possibly in exchange for a fee, provided that this copyright notice appears - * intact. Permission is also granted to adapt this software to produce - * derivative works, as long as the modified versions carry this copyright - * notice and additional notices stating that the work has been modified. - * This source code may be translated into executable form and incorporated - * into proprietary software; there is no requirement for such software to - * contain a copyright notice related to this source. - * - * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $ - * $Name: kazlib_1_20 $ - */ - -#include -#include -#include -#include -#define HASH_IMPLEMENTATION -#include "hash.h" - -#ifdef KAZLIB_RCSID -static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $"; -#endif - -#define INIT_BITS 6 -#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ -#define INIT_MASK ((INIT_SIZE) - 1) - -#define next hash_next -#define key hash_key -#define data hash_data -#define hkey hash_hkey - -#define table hash_table -#define nchains hash_nchains -#define nodecount hash_nodecount -#define maxcount hash_maxcount -#define highmark hash_highmark -#define lowmark hash_lowmark -#define compare hash_compare -#define function hash_function -#define allocnode hash_allocnode -#define freenode hash_freenode -#define context hash_context -#define mask hash_mask -#define dynamic hash_dynamic - -#define table hash_table -#define chain hash_chain - -static hnode_t *kl_hnode_alloc(void *context); -static void kl_hnode_free(hnode_t *node, void *context); -static hash_val_t hash_fun_default(const void *key); -static int hash_comp_default(const void *key1, const void *key2); - -int hash_val_t_bit; - -/* - * Compute the number of bits in the hash_val_t type. We know that hash_val_t - * is an unsigned integral type. Thus the highest value it can hold is a - * Mersenne number (power of two, less one). We initialize a hash_val_t - * object with this value and then shift bits out one by one while counting. - * Notes: - * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power - * of two. This means that its binary representation consists of all one - * bits, and hence ``val'' is initialized to all one bits. - * 2. While bits remain in val, we increment the bit count and shift it to the - * right, replacing the topmost bit by zero. - */ - -static void compute_bits(void) -{ - hash_val_t val = HASH_VAL_T_MAX; /* 1 */ - int bits = 0; - - while (val) { /* 2 */ - bits++; - val >>= 1; - } - - hash_val_t_bit = bits; -} - -/* - * Verify whether the given argument is a power of two. - */ - -static int is_power_of_two(hash_val_t arg) -{ - if (arg == 0) - return 0; - while ((arg & 1) == 0) - arg >>= 1; - return (arg == 1); -} - -/* - * Compute a shift amount from a given table size - */ - -static hash_val_t compute_mask(hashcount_t size) -{ - assert (is_power_of_two(size)); - assert (size >= 2); - - return size - 1; -} - -/* - * Initialize the table of pointers to null. - */ - -static void clear_table(hash_t *hash) -{ - hash_val_t i; - - for (i = 0; i < hash->nchains; i++) - hash->table[i] = NULL; -} - -/* - * Double the size of a dynamic table. This works as follows. Each chain splits - * into two adjacent chains. The shift amount increases by one, exposing an - * additional bit of each hashed key. For each node in the original chain, the - * value of this newly exposed bit will decide which of the two new chains will - * receive the node: if the bit is 1, the chain with the higher index will have - * the node, otherwise the lower chain will receive the node. In this manner, - * the hash table will continue to function exactly as before without having to - * rehash any of the keys. - * Notes: - * 1. Overflow check. - * 2. The new number of chains is twice the old number of chains. - * 3. The new mask is one bit wider than the previous, revealing a - * new bit in all hashed keys. - * 4. Allocate a new table of chain pointers that is twice as large as the - * previous one. - * 5. If the reallocation was successful, we perform the rest of the growth - * algorithm, otherwise we do nothing. - * 6. The exposed_bit variable holds a mask with which each hashed key can be - * AND-ed to test the value of its newly exposed bit. - * 7. Now loop over each chain in the table and sort its nodes into two - * chains based on the value of each node's newly exposed hash bit. - * 8. The low chain replaces the current chain. The high chain goes - * into the corresponding sister chain in the upper half of the table. - * 9. We have finished dealing with the chains and nodes. We now update - * the various bookeeping fields of the hash structure. - */ - -static void grow_table(hash_t *hash) -{ - hnode_t **newtable; - - assert (2 * hash->nchains > hash->nchains); /* 1 */ - - newtable = realloc(hash->table, - sizeof *newtable * hash->nchains * 2); /* 4 */ - - if (newtable) { /* 5 */ - hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ - hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ - hash_val_t chain; - - assert (mask != hash->mask); - - for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ - hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; - - for (hptr = newtable[chain]; hptr != 0; hptr = next) { - next = hptr->next; - - if (hptr->hkey & exposed_bit) { - hptr->next = high_chain; - high_chain = hptr; - } else { - hptr->next = low_chain; - low_chain = hptr; - } - } - - newtable[chain] = low_chain; /* 8 */ - newtable[chain + hash->nchains] = high_chain; - } - - hash->table = newtable; /* 9 */ - hash->mask = mask; - hash->nchains *= 2; - hash->lowmark *= 2; - hash->highmark *= 2; - } - assert (kl_hash_verify(hash)); -} - -/* - * Cut a table size in half. This is done by folding together adjacent chains - * and populating the lower half of the table with these chains. The chains are - * simply spliced together. Once this is done, the whole table is reallocated - * to a smaller object. - * Notes: - * 1. It is illegal to have a hash table with one slot. This would mean that - * hash->shift is equal to hash_val_t_bit, an illegal shift value. - * Also, other things could go wrong, such as hash->lowmark becoming zero. - * 2. Looping over each pair of sister chains, the low_chain is set to - * point to the head node of the chain in the lower half of the table, - * and high_chain points to the head node of the sister in the upper half. - * 3. The intent here is to compute a pointer to the last node of the - * lower chain into the low_tail variable. If this chain is empty, - * low_tail ends up with a null value. - * 4. If the lower chain is not empty, we simply tack the upper chain onto it. - * If the upper chain is a null pointer, nothing happens. - * 5. Otherwise if the lower chain is empty but the upper one is not, - * If the low chain is empty, but the high chain is not, then the - * high chain is simply transferred to the lower half of the table. - * 6. Otherwise if both chains are empty, there is nothing to do. - * 7. All the chain pointers are in the lower half of the table now, so - * we reallocate it to a smaller object. This, of course, invalidates - * all pointer-to-pointers which reference into the table from the - * first node of each chain. - * 8. Though it's unlikely, the reallocation may fail. In this case we - * pretend that the table _was_ reallocated to a smaller object. - * 9. Finally, update the various table parameters to reflect the new size. - */ - -static void shrink_table(hash_t *hash) -{ - hash_val_t chain, nchains; - hnode_t **newtable, *low_tail, *low_chain, *high_chain; - - assert (hash->nchains >= 2); /* 1 */ - nchains = hash->nchains / 2; - - for (chain = 0; chain < nchains; chain++) { - low_chain = hash->table[chain]; /* 2 */ - high_chain = hash->table[chain + nchains]; - for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) - ; /* 3 */ - if (low_chain != 0) /* 4 */ - low_tail->next = high_chain; - else if (high_chain != 0) /* 5 */ - hash->table[chain] = high_chain; - else - assert (hash->table[chain] == NULL); /* 6 */ - } - newtable = realloc(hash->table, - sizeof *newtable * nchains); /* 7 */ - if (newtable) /* 8 */ - hash->table = newtable; - hash->mask >>= 1; /* 9 */ - hash->nchains = nchains; - hash->lowmark /= 2; - hash->highmark /= 2; - assert (kl_hash_verify(hash)); -} - - -/* - * Create a dynamic hash table. Both the hash table structure and the table - * itself are dynamically allocated. Furthermore, the table is extendible in - * that it will automatically grow as its load factor increases beyond a - * certain threshold. - * Notes: - * 1. If the number of bits in the hash_val_t type has not been computed yet, - * we do so here, because this is likely to be the first function that the - * user calls. - * 2. Allocate a hash table control structure. - * 3. If a hash table control structure is successfully allocated, we - * proceed to initialize it. Otherwise we return a null pointer. - * 4. We try to allocate the table of hash chains. - * 5. If we were able to allocate the hash chain table, we can finish - * initializing the hash structure and the table. Otherwise, we must - * backtrack by freeing the hash structure. - * 6. INIT_SIZE should be a power of two. The high and low marks are always set - * to be twice the table size and half the table size respectively. When the - * number of nodes in the table grows beyond the high size (beyond load - * factor 2), it will double in size to cut the load factor down to about - * about 1. If the table shrinks down to or beneath load factor 0.5, - * it will shrink, bringing the load up to about 1. However, the table - * will never shrink beneath INIT_SIZE even if it's emptied. - * 7. This indicates that the table is dynamically allocated and dynamically - * resized on the fly. A table that has this value set to zero is - * assumed to be statically allocated and will not be resized. - * 8. The table of chains must be properly reset to all null pointers. - */ - -hash_t *kl_hash_create(hashcount_t maxcount, hash_comp_t compfun, - hash_fun_t hashfun) -{ - hash_t *hash; - - if (hash_val_t_bit == 0) /* 1 */ - compute_bits(); - - hash = malloc(sizeof *hash); /* 2 */ - - if (hash) { /* 3 */ - hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ - if (hash->table) { /* 5 */ - hash->nchains = INIT_SIZE; /* 6 */ - hash->highmark = INIT_SIZE * 2; - hash->lowmark = INIT_SIZE / 2; - hash->nodecount = 0; - hash->maxcount = maxcount; - hash->compare = compfun ? compfun : hash_comp_default; - hash->function = hashfun ? hashfun : hash_fun_default; - hash->allocnode = kl_hnode_alloc; - hash->freenode = kl_hnode_free; - hash->context = NULL; - hash->mask = INIT_MASK; - hash->dynamic = 1; /* 7 */ - clear_table(hash); /* 8 */ - assert (kl_hash_verify(hash)); - return hash; - } - free(hash); - } - - return NULL; -} - -/* - * Select a different set of node allocator routines. - */ - -void kl_hash_set_allocator(hash_t *hash, hnode_alloc_t al, - hnode_free_t fr, void *context) -{ - assert (kl_hash_count(hash) == 0); - assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); - - hash->allocnode = al ? al : kl_hnode_alloc; - hash->freenode = fr ? fr : kl_hnode_free; - hash->context = context; -} - -/* - * Free every node in the hash using the hash->freenode() function pointer, and - * cause the hash to become empty. - */ - -void kl_hash_free_nodes(hash_t *hash) -{ - hscan_t hs; - hnode_t *node; - kl_hash_scan_begin(&hs, hash); - while ((node = kl_hash_scan_next(&hs))) { - kl_hash_scan_delete(hash, node); - hash->freenode(node, hash->context); - } - hash->nodecount = 0; - clear_table(hash); -} - -/* - * Obsolescent function for removing all nodes from a table, - * freeing them and then freeing the table all in one step. - */ - -void kl_hash_free(hash_t *hash) -{ -#ifdef KAZLIB_OBSOLESCENT_DEBUG - assert ("call to obsolescent function hash_free()" && 0); -#endif - kl_hash_free_nodes(hash); - kl_hash_destroy(hash); -} - -/* - * Free a dynamic hash table structure. - */ - -void kl_hash_destroy(hash_t *hash) -{ - assert (hash_val_t_bit != 0); - assert (kl_hash_isempty(hash)); - free(hash->table); - free(hash); -} - -/* - * Initialize a user supplied hash structure. The user also supplies a table of - * chains which is assigned to the hash structure. The table is static---it - * will not grow or shrink. - * 1. See note 1. in hash_create(). - * 2. The user supplied array of pointers hopefully contains nchains nodes. - * 3. See note 7. in hash_create(). - * 4. We must dynamically compute the mask from the given power of two table - * size. - * 5. The user supplied table can't be assumed to contain null pointers, - * so we reset it here. - */ - -hash_t *kl_hash_init(hash_t *hash, hashcount_t maxcount, - hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, - hashcount_t nchains) -{ - if (hash_val_t_bit == 0) /* 1 */ - compute_bits(); - - assert (is_power_of_two(nchains)); - - hash->table = table; /* 2 */ - hash->nchains = nchains; - hash->nodecount = 0; - hash->maxcount = maxcount; - hash->compare = compfun ? compfun : hash_comp_default; - hash->function = hashfun ? hashfun : hash_fun_default; - hash->dynamic = 0; /* 3 */ - hash->mask = compute_mask(nchains); /* 4 */ - clear_table(hash); /* 5 */ - - assert (kl_hash_verify(hash)); - - return hash; -} - -/* - * Reset the hash scanner so that the next element retrieved by - * hash_scan_next() shall be the first element on the first non-empty chain. - * Notes: - * 1. Locate the first non empty chain. - * 2. If an empty chain is found, remember which one it is and set the next - * pointer to refer to its first element. - * 3. Otherwise if a chain is not found, set the next pointer to NULL - * so that hash_scan_next() shall indicate failure. - */ - -void kl_hash_scan_begin(hscan_t *scan, hash_t *hash) -{ - hash_val_t nchains = hash->nchains; - hash_val_t chain; - - scan->table = hash; - - /* 1 */ - - for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) - ; - - if (chain < nchains) { /* 2 */ - scan->chain = chain; - scan->next = hash->table[chain]; - } else { /* 3 */ - scan->next = NULL; - } -} - -/* - * Retrieve the next node from the hash table, and update the pointer - * for the next invocation of hash_scan_next(). - * Notes: - * 1. Remember the next pointer in a temporary value so that it can be - * returned. - * 2. This assertion essentially checks whether the module has been properly - * initialized. The first point of interaction with the module should be - * either hash_create() or hash_init(), both of which set hash_val_t_bit to - * a non zero value. - * 3. If the next pointer we are returning is not NULL, then the user is - * allowed to call hash_scan_next() again. We prepare the new next pointer - * for that call right now. That way the user is allowed to delete the node - * we are about to return, since we will no longer be needing it to locate - * the next node. - * 4. If there is a next node in the chain (next->next), then that becomes the - * new next node, otherwise ... - * 5. We have exhausted the current chain, and must locate the next subsequent - * non-empty chain in the table. - * 6. If a non-empty chain is found, the first element of that chain becomes - * the new next node. Otherwise there is no new next node and we set the - * pointer to NULL so that the next time hash_scan_next() is called, a null - * pointer shall be immediately returned. - */ - - -hnode_t *kl_hash_scan_next(hscan_t *scan) -{ - hnode_t *next = scan->next; /* 1 */ - hash_t *hash = scan->table; - hash_val_t chain = scan->chain + 1; - hash_val_t nchains = hash->nchains; - - assert (hash_val_t_bit != 0); /* 2 */ - - if (next) { /* 3 */ - if (next->next) { /* 4 */ - scan->next = next->next; - } else { - while (chain < nchains && hash->table[chain] == 0) /* 5 */ - chain++; - if (chain < nchains) { /* 6 */ - scan->chain = chain; - scan->next = hash->table[chain]; - } else { - scan->next = NULL; - } - } - } - return next; -} - -/* - * Insert a node into the hash table. - * Notes: - * 1. It's illegal to insert more than the maximum number of nodes. The client - * should verify that the hash table is not full before attempting an - * insertion. - * 2. The same key may not be inserted into a table twice. - * 3. If the table is dynamic and the load factor is already at >= 2, - * grow the table. - * 4. We take the bottom N bits of the hash value to derive the chain index, - * where N is the base 2 logarithm of the size of the hash table. - */ - -void kl_hash_insert(hash_t *hash, hnode_t *node, const void *key) -{ - hash_val_t hkey, chain; - - assert (hash_val_t_bit != 0); - assert (node->next == NULL); - assert (hash->nodecount < hash->maxcount); /* 1 */ - assert (kl_hash_lookup(hash, key) == NULL); /* 2 */ - - if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ - grow_table(hash); - - hkey = hash->function(key); - chain = hkey & hash->mask; /* 4 */ - - node->key = key; - node->hkey = hkey; - node->next = hash->table[chain]; - hash->table[chain] = node; - hash->nodecount++; - - assert (kl_hash_verify(hash)); -} - -/* - * Find a node in the hash table and return a pointer to it. - * Notes: - * 1. We hash the key and keep the entire hash value. As an optimization, when - * we descend down the chain, we can compare hash values first and only if - * hash values match do we perform a full key comparison. - * 2. To locate the chain from among 2^N chains, we look at the lower N bits of - * the hash value by anding them with the current mask. - * 3. Looping through the chain, we compare the stored hash value inside each - * node against our computed hash. If they match, then we do a full - * comparison between the unhashed keys. If these match, we have located the - * entry. - */ - -hnode_t *kl_hash_lookup(hash_t *hash, const void *key) -{ - hash_val_t hkey, chain; - hnode_t *nptr; - - hkey = hash->function(key); /* 1 */ - chain = hkey & hash->mask; /* 2 */ - - for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ - if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) - return nptr; - } - - return NULL; -} - -/* - * Delete the given node from the hash table. Since the chains - * are singly linked, we must locate the start of the node's chain - * and traverse. - * Notes: - * 1. The node must belong to this hash table, and its key must not have - * been tampered with. - * 2. If this deletion will take the node count below the low mark, we - * shrink the table now. - * 3. Determine which chain the node belongs to, and fetch the pointer - * to the first node in this chain. - * 4. If the node being deleted is the first node in the chain, then - * simply update the chain head pointer. - * 5. Otherwise advance to the node's predecessor, and splice out - * by updating the predecessor's next pointer. - * 6. Indicate that the node is no longer in a hash table. - */ - -hnode_t *kl_hash_delete(hash_t *hash, hnode_t *node) -{ - hash_val_t chain; - hnode_t *hptr; - - assert (kl_hash_lookup(hash, node->key) == node); /* 1 */ - assert (hash_val_t_bit != 0); - - if (hash->dynamic && hash->nodecount <= hash->lowmark - && hash->nodecount > INIT_SIZE) - shrink_table(hash); /* 2 */ - - chain = node->hkey & hash->mask; /* 3 */ - hptr = hash->table[chain]; - - if (hptr == node) { /* 4 */ - hash->table[chain] = node->next; - } else { - while (hptr->next != node) { /* 5 */ - assert (hptr != 0); - hptr = hptr->next; - } - assert (hptr->next == node); - hptr->next = node->next; - } - - hash->nodecount--; - assert (kl_hash_verify(hash)); - - node->next = NULL; /* 6 */ - return node; -} - -int kl_hash_alloc_insert(hash_t *hash, const void *key, void *data) -{ - hnode_t *node = hash->allocnode(hash->context); - - if (node) { - kl_hnode_init(node, data); - kl_hash_insert(hash, node, key); - return 1; - } - return 0; -} - -void kl_hash_delete_free(hash_t *hash, hnode_t *node) -{ - kl_hash_delete(hash, node); - hash->freenode(node, hash->context); -} - -/* - * Exactly like hash_delete, except does not trigger table shrinkage. This is to be - * used from within a hash table scan operation. See notes for hash_delete. - */ - -hnode_t *kl_hash_scan_delete(hash_t *hash, hnode_t *node) -{ - hash_val_t chain; - hnode_t *hptr; - - assert (kl_hash_lookup(hash, node->key) == node); - assert (hash_val_t_bit != 0); - - chain = node->hkey & hash->mask; - hptr = hash->table[chain]; - - if (hptr == node) { - hash->table[chain] = node->next; - } else { - while (hptr->next != node) - hptr = hptr->next; - hptr->next = node->next; - } - - hash->nodecount--; - assert (kl_hash_verify(hash)); - node->next = NULL; - - return node; -} - -/* - * Like hash_delete_free but based on hash_scan_delete. - */ - -void kl_hash_scan_delfree(hash_t *hash, hnode_t *node) -{ - kl_hash_scan_delete(hash, node); - hash->freenode(node, hash->context); -} - -/* - * Verify whether the given object is a valid hash table. This means - * Notes: - * 1. If the hash table is dynamic, verify whether the high and - * low expansion/shrinkage thresholds are powers of two. - * 2. Count all nodes in the table, and test each hash value - * to see whether it is correct for the node's chain. - */ - -int kl_hash_verify(hash_t *hash) -{ - hashcount_t count = 0; - hash_val_t chain; - hnode_t *hptr; - - if (hash->dynamic) { /* 1 */ - if (hash->lowmark >= hash->highmark) - return 0; - if (!is_power_of_two(hash->highmark)) - return 0; - if (!is_power_of_two(hash->lowmark)) - return 0; - } - - for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ - for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { - if ((hptr->hkey & hash->mask) != chain) - return 0; - count++; - } - } - - if (count != hash->nodecount) - return 0; - - return 1; -} - -/* - * Test whether the hash table is full and return 1 if this is true, - * 0 if it is false. - */ - -#undef kl_hash_isfull -int kl_hash_isfull(hash_t *hash) -{ - return hash->nodecount == hash->maxcount; -} - -/* - * Test whether the hash table is empty and return 1 if this is true, - * 0 if it is false. - */ - -#undef kl_hash_isempty -int kl_hash_isempty(hash_t *hash) -{ - return hash->nodecount == 0; -} - -static hnode_t *kl_hnode_alloc(void *context) -{ - return malloc(sizeof *kl_hnode_alloc(NULL)); -} - -static void kl_hnode_free(hnode_t *node, void *context) -{ - free(node); -} - - -/* - * Create a hash table node dynamically and assign it the given data. - */ - -hnode_t *kl_hnode_create(void *data) -{ - hnode_t *node = malloc(sizeof *node); - if (node) { - node->data = data; - node->next = NULL; - } - return node; -} - -/* - * Initialize a client-supplied node - */ - -hnode_t *kl_hnode_init(hnode_t *hnode, void *data) -{ - hnode->data = data; - hnode->next = NULL; - return hnode; -} - -/* - * Destroy a dynamically allocated node. - */ - -void kl_hnode_destroy(hnode_t *hnode) -{ - free(hnode); -} - -#undef kl_hnode_put -void kl_hnode_put(hnode_t *node, void *data) -{ - node->data = data; -} - -#undef kl_hnode_get -void *kl_hnode_get(hnode_t *node) -{ - return node->data; -} - -#undef kl_hnode_getkey -const void *kl_hnode_getkey(hnode_t *node) -{ - return node->key; -} - -#undef kl_hash_count -hashcount_t kl_hash_count(hash_t *hash) -{ - return hash->nodecount; -} - -#undef kl_hash_size -hashcount_t kl_hash_size(hash_t *hash) -{ - return hash->nchains; -} - -static hash_val_t hash_fun_default(const void *key) -{ - static unsigned long randbox[] = { - 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, - 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, - 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, - 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, - }; - - const unsigned char *str = key; - hash_val_t acc = 0; - - while (*str) { - acc ^= randbox[(*str + acc) & 0xf]; - acc = (acc << 1) | (acc >> 31); - acc &= 0xffffffffU; - acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; - acc = (acc << 2) | (acc >> 30); - acc &= 0xffffffffU; - } - return acc; -} - -static int hash_comp_default(const void *key1, const void *key2) -{ - return strcmp(key1, key2); -} diff --git a/c_src/couchdb-khash2/hash.h b/c_src/couchdb-khash2/hash.h deleted file mode 100644 index 0c75ed0..0000000 --- a/c_src/couchdb-khash2/hash.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Hash Table Data Type - * Copyright (C) 1997 Kaz Kylheku - * - * Free Software License: - * - * All rights are reserved by the author, with the following exceptions: - * Permission is granted to freely reproduce and distribute this software, - * possibly in exchange for a fee, provided that this copyright notice appears - * intact. Permission is also granted to adapt this software to produce - * derivative works, as long as the modified versions carry this copyright - * notice and additional notices stating that the work has been modified. - * This source code may be translated into executable form and incorporated - * into proprietary software; there is no requirement for such software to - * contain a copyright notice related to this source. - * - * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $ - * $Name: kazlib_1_20 $ - */ - -#ifndef HASH_H -#define HASH_H - -#include -#ifdef KAZLIB_SIDEEFFECT_DEBUG -#include "sfx.h" -#endif - -/* - * Blurb for inclusion into C++ translation units - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned long hashcount_t; -#define HASHCOUNT_T_MAX ULONG_MAX - -typedef unsigned long hash_val_t; -#define HASH_VAL_T_MAX ULONG_MAX - -extern int hash_val_t_bit; - -#ifndef HASH_VAL_T_BIT -#define HASH_VAL_T_BIT ((int) hash_val_t_bit) -#endif - -/* - * Hash chain node structure. - * Notes: - * 1. This preprocessing directive is for debugging purposes. The effect is - * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the - * inclusion of this header, then the structure shall be declared as having - * the single member int __OPAQUE__. This way, any attempts by the - * client code to violate the principles of information hiding (by accessing - * the structure directly) can be diagnosed at translation time. However, - * note the resulting compiled unit is not suitable for linking. - * 2. This is a pointer to the next node in the chain. In the last node of a - * chain, this pointer is null. - * 3. The key is a pointer to some user supplied data that contains a unique - * identifier for each hash node in a given table. The interpretation of - * the data is up to the user. When creating or initializing a hash table, - * the user must supply a pointer to a function for comparing two keys, - * and a pointer to a function for hashing a key into a numeric value. - * 4. The value is a user-supplied pointer to void which may refer to - * any data object. It is not interpreted in any way by the hashing - * module. - * 5. The hashed key is stored in each node so that we don't have to rehash - * each key when the table must grow or shrink. - */ - -typedef struct hnode_t { - #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ - struct hnode_t *hash_next; /* 2 */ - const void *hash_key; /* 3 */ - void *hash_data; /* 4 */ - hash_val_t hash_hkey; /* 5 */ - #else - int hash_dummy; - #endif -} hnode_t; - -/* - * The comparison function pointer type. A comparison function takes two keys - * and produces a value of -1 if the left key is less than the right key, a - * value of 0 if the keys are equal, and a value of 1 if the left key is - * greater than the right key. - */ - -typedef int (*hash_comp_t)(const void *, const void *); - -/* - * The hashing function performs some computation on a key and produces an - * integral value of type hash_val_t based on that key. For best results, the - * function should have a good randomness properties in *all* significant bits - * over the set of keys that are being inserted into a given hash table. In - * particular, the most significant bits of hash_val_t are most significant to - * the hash module. Only as the hash table expands are less significant bits - * examined. Thus a function that has good distribution in its upper bits but - * not lower is preferrable to one that has poor distribution in the upper bits - * but not the lower ones. - */ - -typedef hash_val_t (*hash_fun_t)(const void *); - -/* - * allocator functions - */ - -typedef hnode_t *(*hnode_alloc_t)(void *); -typedef void (*hnode_free_t)(hnode_t *, void *); - -/* - * This is the hash table control structure. It keeps track of information - * about a hash table, as well as the hash table itself. - * Notes: - * 1. Pointer to the hash table proper. The table is an array of pointers to - * hash nodes (of type hnode_t). If the table is empty, every element of - * this table is a null pointer. A non-null entry points to the first - * element of a chain of nodes. - * 2. This member keeps track of the size of the hash table---that is, the - * number of chain pointers. - * 3. The count member maintains the number of elements that are presently - * in the hash table. - * 4. The maximum count is the greatest number of nodes that can populate this - * table. If the table contains this many nodes, no more can be inserted, - * and the hash_isfull() function returns true. - * 5. The high mark is a population threshold, measured as a number of nodes, - * which, if exceeded, will trigger a table expansion. Only dynamic hash - * tables are subject to this expansion. - * 6. The low mark is a minimum population threshold, measured as a number of - * nodes. If the table population drops below this value, a table shrinkage - * will occur. Only dynamic tables are subject to this reduction. No table - * will shrink beneath a certain absolute minimum number of nodes. - * 7. This is the a pointer to the hash table's comparison function. The - * function is set once at initialization or creation time. - * 8. Pointer to the table's hashing function, set once at creation or - * initialization time. - * 9. The current hash table mask. If the size of the hash table is 2^N, - * this value has its low N bits set to 1, and the others clear. It is used - * to select bits from the result of the hashing function to compute an - * index into the table. - * 10. A flag which indicates whether the table is to be dynamically resized. It - * is set to 1 in dynamically allocated tables, 0 in tables that are - * statically allocated. - */ - -typedef struct hash_t { - #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) - struct hnode_t **hash_table; /* 1 */ - hashcount_t hash_nchains; /* 2 */ - hashcount_t hash_nodecount; /* 3 */ - hashcount_t hash_maxcount; /* 4 */ - hashcount_t hash_highmark; /* 5 */ - hashcount_t hash_lowmark; /* 6 */ - hash_comp_t hash_compare; /* 7 */ - hash_fun_t hash_function; /* 8 */ - hnode_alloc_t hash_allocnode; - hnode_free_t hash_freenode; - void *hash_context; - hash_val_t hash_mask; /* 9 */ - int hash_dynamic; /* 10 */ - #else - int hash_dummy; - #endif -} hash_t; - -/* - * Hash scanner structure, used for traversals of the data structure. - * Notes: - * 1. Pointer to the hash table that is being traversed. - * 2. Reference to the current chain in the table being traversed (the chain - * that contains the next node that shall be retrieved). - * 3. Pointer to the node that will be retrieved by the subsequent call to - * hash_scan_next(). - */ - -typedef struct hscan_t { - #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) - hash_t *hash_table; /* 1 */ - hash_val_t hash_chain; /* 2 */ - hnode_t *hash_next; /* 3 */ - #else - int hash_dummy; - #endif -} hscan_t; - -extern hash_t *kl_hash_create(hashcount_t, hash_comp_t, hash_fun_t); -extern void kl_hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); -extern void kl_hash_destroy(hash_t *); -extern void kl_hash_free_nodes(hash_t *); -extern void kl_hash_free(hash_t *); -extern hash_t *kl_hash_init(hash_t *, hashcount_t, hash_comp_t, - hash_fun_t, hnode_t **, hashcount_t); -extern void kl_hash_insert(hash_t *, hnode_t *, const void *); -extern hnode_t *kl_hash_lookup(hash_t *, const void *); -extern hnode_t *kl_hash_delete(hash_t *, hnode_t *); -extern int kl_hash_alloc_insert(hash_t *, const void *, void *); -extern void kl_hash_delete_free(hash_t *, hnode_t *); - -extern void kl_hnode_put(hnode_t *, void *); -extern void *kl_hnode_get(hnode_t *); -extern const void *kl_hnode_getkey(hnode_t *); -extern hashcount_t kl_hash_count(hash_t *); -extern hashcount_t kl_hash_size(hash_t *); - -extern int kl_hash_isfull(hash_t *); -extern int kl_hash_isempty(hash_t *); - -extern void kl_hash_scan_begin(hscan_t *, hash_t *); -extern hnode_t *kl_hash_scan_next(hscan_t *); -extern hnode_t *kl_hash_scan_delete(hash_t *, hnode_t *); -extern void kl_hash_scan_delfree(hash_t *, hnode_t *); - -extern int kl_hash_verify(hash_t *); - -extern hnode_t *kl_hnode_create(void *); -extern hnode_t *kl_hnode_init(hnode_t *, void *); -extern void kl_hnode_destroy(hnode_t *); - -#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) -#ifdef KAZLIB_SIDEEFFECT_DEBUG -#define kl_hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) -#else -#define kl_hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) -#endif -#define kl_hash_isempty(H) ((H)->hash_nodecount == 0) -#define kl_hash_count(H) ((H)->hash_nodecount) -#define kl_hash_size(H) ((H)->hash_nchains) -#define kl_hnode_get(N) ((N)->hash_data) -#define kl_hnode_getkey(N) ((N)->hash_key) -#define kl_hnode_put(N, V) ((N)->hash_data = (V)) -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c_src/couchdb-khash2/khash.c b/c_src/couchdb-khash2/khash.c deleted file mode 100644 index b15a18e..0000000 --- a/c_src/couchdb-khash2/khash.c +++ /dev/null @@ -1,658 +0,0 @@ -// This file is part of khash released under the MIT license. -// See the LICENSE file for more information. -// Copyright 2013 Cloudant, Inc - -#include -#include -#include - -#include "erl_nif.h" -#include "hash.h" - -#ifdef _WIN32 -#define INLINE __inline -#else -#define INLINE inline -#endif - -#define KHASH_VERSION 0 - - -typedef struct -{ - ERL_NIF_TERM atom_ok; - ERL_NIF_TERM atom_error; - ERL_NIF_TERM atom_value; - ERL_NIF_TERM atom_not_found; - ERL_NIF_TERM atom_end_of_table; - ERL_NIF_TERM atom_expired_iterator; - ErlNifResourceType* res_hash; - ErlNifResourceType* res_iter; -} khash_priv; - - -typedef struct -{ - unsigned int hval; - ErlNifEnv* env; - ERL_NIF_TERM key; - ERL_NIF_TERM val; -} khnode_t; - - -typedef struct -{ - int version; - unsigned int gen; - hash_t* h; - ErlNifPid p; -} khash_t; - - -typedef struct -{ - int version; - unsigned int gen; - khash_t* khash; - hscan_t scan; -} khash_iter_t; - - -static INLINE ERL_NIF_TERM -make_atom(ErlNifEnv* env, const char* name) -{ - ERL_NIF_TERM ret; - if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) { - return ret; - } - return enif_make_atom(env, name); -} - - -static INLINE ERL_NIF_TERM -make_ok(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM value) -{ - return enif_make_tuple2(env, priv->atom_ok, value); -} - - -static INLINE ERL_NIF_TERM -make_error(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM reason) -{ - return enif_make_tuple2(env, priv->atom_error, reason); -} - - -static INLINE int -check_pid(ErlNifEnv* env, khash_t* khash) -{ - ErlNifPid pid; - enif_self(env, &pid); - - if(enif_compare(pid.pid, khash->p.pid) == 0) { - return 1; - } - - return 0; -} - - -hnode_t* -khnode_alloc(void* ctx) -{ - hnode_t* ret = (hnode_t*) enif_alloc(sizeof(hnode_t)); - khnode_t* node = (khnode_t*) enif_alloc(sizeof(khnode_t)); - - memset(ret, '\0', sizeof(hnode_t)); - memset(node, '\0', sizeof(khnode_t)); - - node->env = enif_alloc_env(); - ret->hash_key = node; - - return ret; -} - - -void -khnode_free(hnode_t* obj, void* ctx) -{ - khnode_t* node = (khnode_t*) kl_hnode_getkey(obj); - enif_free_env(node->env); - enif_free(node); - enif_free(obj); - return; -} - - -int -khash_cmp_fun(const void* l, const void* r) -{ - khnode_t* left = (khnode_t*) l; - khnode_t* right = (khnode_t*) r; - int cmp = enif_compare(left->key, right->key); - - if(cmp < 0) { - return -1; - } else if(cmp == 0) { - return 0; - } else { - return 1; - } -} - - -hash_val_t -khash_hash_fun(const void* obj) -{ - khnode_t* node = (khnode_t*) obj; - return (hash_val_t) node->hval; -} - - -static INLINE khash_t* -khash_create_int(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM opts) -{ - khash_t* khash = NULL; - - assert(priv != NULL && "missing private data member"); - - khash = (khash_t*) enif_alloc_resource(priv->res_hash, sizeof(khash_t)); - memset(khash, '\0', sizeof(khash_t)); - khash->version = KHASH_VERSION; - khash->gen = 0; - - khash->h = kl_hash_create(HASHCOUNT_T_MAX, khash_cmp_fun, khash_hash_fun); - - if(khash->h == NULL ) { - enif_release_resource(khash); - return NULL; - } - - kl_hash_set_allocator(khash->h, khnode_alloc, khnode_free, NULL); - enif_self(env, &(khash->p)); - - return khash; -} - - -static ERL_NIF_TERM -khash_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash; - ERL_NIF_TERM ret; - - if(argc != 1) { - return enif_make_badarg(env); - } - - khash = khash_create_int(env, priv, argv[0]); - if(khash == NULL) { - return enif_make_badarg(env); - } - - ret = enif_make_resource(env, khash); - enif_release_resource(khash); - - return make_ok(env, priv, ret); -} - - -static void -khash_free(ErlNifEnv* env, void* obj) -{ - khash_t* khash = (khash_t*) obj; - - if(khash->h != NULL) { - kl_hash_free_nodes(khash->h); - kl_hash_destroy(khash->h); - } - - return; -} - - -static ERL_NIF_TERM -khash_to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = (khash_priv*) enif_priv_data(env); - ERL_NIF_TERM ret = enif_make_list(env, 0); - khash_t* khash = NULL; - void* res = NULL; - hscan_t scan; - hnode_t* entry; - khnode_t* node; - ERL_NIF_TERM key; - ERL_NIF_TERM val; - ERL_NIF_TERM tuple; - - if(argc != 1) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - kl_hash_scan_begin(&scan, khash->h); - - while((entry = kl_hash_scan_next(&scan)) != NULL) { - node = (khnode_t*) kl_hnode_getkey(entry); - key = enif_make_copy(env, node->key); - val = enif_make_copy(env, node->val); - tuple = enif_make_tuple2(env, key, val); - ret = enif_make_list_cell(env, tuple, ret); - } - - return ret; -} - - -static ERL_NIF_TERM -khash_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash = NULL; - void* res = NULL; - - if(argc != 1) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - kl_hash_free_nodes(khash->h); - - khash->gen += 1; - - return priv->atom_ok; -} - - -static INLINE hnode_t* -khash_lookup_int(ErlNifEnv* env, uint32_t hv, ERL_NIF_TERM key, khash_t* khash) -{ - khnode_t node; - node.hval = hv; - node.env = env; - node.key = key; - return kl_hash_lookup(khash->h, &node); -} - - -static ERL_NIF_TERM -khash_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash = NULL; - void* res = NULL; - uint32_t hval; - hnode_t* entry; - khnode_t* node; - ERL_NIF_TERM ret; - - if(argc != 3) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - if(!enif_get_uint(env, argv[1], &hval)) { - return enif_make_badarg(env); - } - - entry = khash_lookup_int(env, hval, argv[2], khash); - if(entry == NULL) { - ret = priv->atom_not_found; - } else { - node = (khnode_t*) kl_hnode_getkey(entry); - ret = enif_make_copy(env, node->val); - ret = enif_make_tuple2(env, priv->atom_value, ret); - } - - return ret; -} - - -static ERL_NIF_TERM -khash_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash = NULL; - void* res = NULL; - uint32_t hval; - hnode_t* entry; - khnode_t* node; - ERL_NIF_TERM ret; - - if(argc != 4) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - if(!enif_get_uint(env, argv[1], &hval)) { - return enif_make_badarg(env); - } - - entry = khash_lookup_int(env, hval, argv[2], khash); - if(entry == NULL) { - ret = argv[3]; - } else { - node = (khnode_t*) kl_hnode_getkey(entry); - ret = enif_make_copy(env, node->val); - } - - return ret; -} - - -static ERL_NIF_TERM -khash_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash = NULL; - void* res = NULL; - uint32_t hval; - hnode_t* entry; - khnode_t* node; - - if(argc != 4) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - if(!enif_get_uint(env, argv[1], &hval)) { - return enif_make_badarg(env); - } - - entry = khash_lookup_int(env, hval, argv[2], khash); - if(entry == NULL) { - entry = khnode_alloc(NULL); - node = (khnode_t*) kl_hnode_getkey(entry); - node->hval = hval; - node->key = enif_make_copy(node->env, argv[2]); - node->val = enif_make_copy(node->env, argv[3]); - kl_hash_insert(khash->h, entry, node); - } else { - node = (khnode_t*) kl_hnode_getkey(entry); - enif_clear_env(node->env); - node->key = enif_make_copy(node->env, argv[2]); - node->val = enif_make_copy(node->env, argv[3]); - } - - khash->gen += 1; - - return priv->atom_ok; -} - - -static ERL_NIF_TERM -khash_del(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash = NULL; - void* res = NULL; - uint32_t hval; - hnode_t* entry; - ERL_NIF_TERM ret; - - if(argc != 3) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - if(!enif_get_uint(env, argv[1], &hval)) { - return enif_make_badarg(env); - } - - entry = khash_lookup_int(env, hval, argv[2], khash); - if(entry == NULL) { - ret = priv->atom_not_found; - } else { - kl_hash_delete_free(khash->h, entry); - ret = priv->atom_ok; - } - - khash->gen += 1; - - return ret; -} - - -static ERL_NIF_TERM -khash_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash; - - if(argc != 1) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, (void*) &khash)) { - return enif_make_badarg(env); - } - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - return enif_make_uint64(env, kl_hash_count(khash->h)); -} - - -static ERL_NIF_TERM -khash_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_t* khash = NULL; - void* res = NULL; - khash_iter_t* iter; - ERL_NIF_TERM ret; - - if(argc != 1) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { - return enif_make_badarg(env); - } - - khash = (khash_t*) res; - - if(!check_pid(env, khash)) { - return enif_make_badarg(env); - } - - iter = (khash_iter_t*) enif_alloc_resource( - priv->res_iter, sizeof(khash_iter_t)); - memset(iter, '\0', sizeof(khash_iter_t)); - iter->version = KHASH_VERSION; - iter->gen = khash->gen; - iter->khash = khash; - kl_hash_scan_begin(&(iter->scan), iter->khash->h); - - // The iterator needs to guarantee that the khash - // remains alive for the life of the iterator. - enif_keep_resource(khash); - - ret = enif_make_resource(env, iter); - enif_release_resource(iter); - - return make_ok(env, priv, ret); -} - - -static void -khash_iter_free(ErlNifEnv* env, void* obj) -{ - khash_iter_t* iter = (khash_iter_t*) obj; - enif_release_resource(iter->khash); -} - - -static ERL_NIF_TERM -khash_iter_next(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - khash_priv* priv = enif_priv_data(env); - khash_iter_t* iter = NULL; - void* res = NULL; - hnode_t* entry; - khnode_t* node; - ERL_NIF_TERM key; - ERL_NIF_TERM val; - - if(argc != 1) { - return enif_make_badarg(env); - } - - if(!enif_get_resource(env, argv[0], priv->res_iter, &res)) { - return enif_make_badarg(env); - } - - iter = (khash_iter_t*) res; - - if(!check_pid(env, iter->khash)) { - return enif_make_badarg(env); - } - - if(iter->gen != iter->khash->gen) { - return make_error(env, priv, priv->atom_expired_iterator); - } - - entry = kl_hash_scan_next(&(iter->scan)); - if(entry == NULL) { - return priv->atom_end_of_table; - } - - node = (khnode_t*) kl_hnode_getkey(entry); - key = enif_make_copy(env, node->key); - val = enif_make_copy(env, node->val); - return enif_make_tuple2(env, key, val); -} - - -static int -load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; - ErlNifResourceType* res; - - khash_priv* new_priv = (khash_priv*) enif_alloc(sizeof(khash_priv)); - if(new_priv == NULL) { - return 1; - } - - res = enif_open_resource_type( - env, NULL, "khash", khash_free, flags, NULL); - if(res == NULL) { - return 1; - } - new_priv->res_hash = res; - - res = enif_open_resource_type( - env, NULL, "khash_iter", khash_iter_free, flags, NULL); - if(res == NULL) { - return 1; - } - new_priv->res_iter = res; - - new_priv->atom_ok = make_atom(env, "ok"); - new_priv->atom_error = make_atom(env, "error"); - new_priv->atom_value = make_atom(env, "value"); - new_priv->atom_not_found = make_atom(env, "not_found"); - new_priv->atom_end_of_table = make_atom(env, "end_of_table"); - new_priv->atom_expired_iterator = make_atom(env, "expired_iterator"); - - *priv = (void*) new_priv; - - return 0; -} - - -static int -reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - return 0; -} - - -static int -upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) -{ - return load(env, priv, info); -} - - -static void -unload(ErlNifEnv* env, void* priv) -{ - enif_free(priv); - return; -} - - -static ErlNifFunc funcs[] = { - {"new", 1, khash_new}, - {"to_list", 1, khash_to_list}, - {"clear", 1, khash_clear}, - {"lookup_int", 3, khash_lookup}, - {"get_int", 4, khash_get}, - {"put_int", 4, khash_put}, - {"del_int", 3, khash_del}, - {"size", 1, khash_size}, - {"iter", 1, khash_iter}, - {"iter_next", 1, khash_iter_next} -}; - - -ERL_NIF_INIT(khash, funcs, &load, &reload, &upgrade, &unload); diff --git a/c_src/couchdb-khash2/rebar.config b/c_src/couchdb-khash2/rebar.config deleted file mode 100644 index 2e88bd2..0000000 --- a/c_src/couchdb-khash2/rebar.config +++ /dev/null @@ -1,12 +0,0 @@ -{port_specs, [ - {"../../priv/khash2.so", ["*.c"]} -]}. - -{port_env, [ - % Development compilation - % {".*", "CFLAGS", "$CFLAGS -g -Wall -Werror -fPIC"} - - % Production compilation - {"(linux|solaris|darwin|freebsd)", "CFLAGS", "$CFLAGS -Wall -Werror -DNDEBUG -O3"}, - {"win32", "CFLAGS", "$CFLAGS /O2 /DNDEBUG /Wall"} -]}. diff --git a/c_src/enlfq/Makefile b/c_src/enlfq/Makefile deleted file mode 100644 index d85d904..0000000 --- a/c_src/enlfq/Makefile +++ /dev/null @@ -1,80 +0,0 @@ - -PROJECT = enlfq -CXXFLAGS = -std=c++11 -O2 -Wextra -Werror -Wno-missing-field-initializers -fno-rtti -fno-exceptions -LDLIBS = -lstdc++ - - -# Based on c_src.mk from erlang.mk by Loic Hoguin - -CURDIR := $(shell pwd) -BASEDIR := $(abspath $(CURDIR)/..) - -PROJECT ?= $(notdir $(BASEDIR)) -PROJECT := $(strip $(PROJECT)) - -ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)]).") -ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]).") -ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]).") - -C_SRC_DIR = $(CURDIR) -C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so - -# System type and C compiler/flags. - -UNAME_SYS := $(shell uname -s) -ifeq ($(UNAME_SYS), Darwin) - CC ?= cc - CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall - LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress -else ifeq ($(UNAME_SYS), FreeBSD) - CC ?= cc - CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -finline-functions -Wall -else ifeq ($(UNAME_SYS), Linux) - CC ?= gcc - CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -finline-functions -Wall -endif - -CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) -CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) - -LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei -LDFLAGS += -shared - -# Verbosity. - -c_verbose_0 = @echo " C " $(?F); -c_verbose = $(c_verbose_$(V)) - -cpp_verbose_0 = @echo " CPP " $(?F); -cpp_verbose = $(cpp_verbose_$(V)) - -link_verbose_0 = @echo " LD " $(@F); -link_verbose = $(link_verbose_$(V)) - -SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) -OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) - -COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c -COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c - -$(C_SRC_OUTPUT): $(OBJECTS) - @mkdir -p $(BASEDIR)/priv/ - $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) - -%.o: %.c - $(COMPILE_C) $(OUTPUT_OPTION) $< - -%.o: %.cc - $(COMPILE_CPP) $(OUTPUT_OPTION) $< - -%.o: %.C - $(COMPILE_CPP) $(OUTPUT_OPTION) $< - -%.o: %.cpp - $(COMPILE_CPP) $(OUTPUT_OPTION) $< - -clean: - @rm -f $(C_SRC_OUTPUT) $(OBJECTS) diff --git a/c_src/enlfq/concurrentqueue.h b/c_src/enlfq/concurrentqueue.h deleted file mode 100644 index 68f66df..0000000 --- a/c_src/enlfq/concurrentqueue.h +++ /dev/null @@ -1,3637 +0,0 @@ -// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue. -// An overview, including benchmark results, is provided here: -// http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++ -// The full design is also described in excruciating detail at: -// http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue - -// Simplified BSD license: -// Copyright (c) 2013-2016, Cameron Desrochers. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, this list of -// conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#pragma once - -#if defined(__GNUC__) -// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and -// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings -// upon assigning any computed values) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" - -#ifdef MCDBGQ_USE_RELACY -#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" -#endif -#endif - -#if defined(__APPLE__) -#include "TargetConditionals.h" -#endif - -#ifdef MCDBGQ_USE_RELACY -#include "relacy/relacy_std.hpp" -#include "relacy_shims.h" -// We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations. -// We'll override the default trait malloc ourselves without a macro. -#undef new -#undef delete -#undef malloc -#undef free -#else -#include // Requires C++11. Sorry VS2010. -#include -#endif -#include // for max_align_t -#include -#include -#include -#include -#include -#include -#include // for CHAR_BIT -#include -#include // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading - -// Platform-specific definitions of a numeric thread ID type and an invalid value -namespace moodycamel { namespace details { - template struct thread_id_converter { - typedef thread_id_t thread_id_numeric_size_t; - typedef thread_id_t thread_id_hash_t; - static thread_id_hash_t prehash(thread_id_t const& x) { return x; } - }; -} } -#if defined(MCDBGQ_USE_RELACY) -namespace moodycamel { namespace details { - typedef std::uint32_t thread_id_t; - static const thread_id_t invalid_thread_id = 0xFFFFFFFFU; - static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU; - static inline thread_id_t thread_id() { return rl::thread_index(); } -} } -#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__) -// No sense pulling in windows.h in a header, we'll manually declare the function -// we use and rely on backwards-compatibility for this not to break -extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); -namespace moodycamel { namespace details { - static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows"); - typedef std::uint32_t thread_id_t; - static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx - static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4. - static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); } -} } -#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE) -namespace moodycamel { namespace details { - static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes"); - - typedef std::thread::id thread_id_t; - static const thread_id_t invalid_thread_id; // Default ctor creates invalid ID - - // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's - // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't - // be. - static inline thread_id_t thread_id() { return std::this_thread::get_id(); } - - template struct thread_id_size { }; - template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; }; - template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; }; - - template<> struct thread_id_converter { - typedef thread_id_size::numeric_t thread_id_numeric_size_t; -#ifndef __APPLE__ - typedef std::size_t thread_id_hash_t; -#else - typedef thread_id_numeric_size_t thread_id_hash_t; -#endif - - static thread_id_hash_t prehash(thread_id_t const& x) - { -#ifndef __APPLE__ - return std::hash()(x); -#else - return *reinterpret_cast(&x); -#endif - } - }; -} } -#else -// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475 -// In order to get a numeric thread ID in a platform-independent way, we use a thread-local -// static variable's address as a thread identifier :-) -#if defined(__GNUC__) || defined(__INTEL_COMPILER) -#define MOODYCAMEL_THREADLOCAL __thread -#elif defined(_MSC_VER) -#define MOODYCAMEL_THREADLOCAL __declspec(thread) -#else -// Assume C++11 compliant compiler -#define MOODYCAMEL_THREADLOCAL thread_local -#endif -namespace moodycamel { namespace details { - typedef std::uintptr_t thread_id_t; - static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr - static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned. - static inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast(&x); } -} } -#endif - -// Exceptions -#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED -#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) -#define MOODYCAMEL_EXCEPTIONS_ENABLED -#endif -#endif -#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED -#define MOODYCAMEL_TRY try -#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__) -#define MOODYCAMEL_RETHROW throw -#define MOODYCAMEL_THROW(expr) throw (expr) -#else -#define MOODYCAMEL_TRY if (true) -#define MOODYCAMEL_CATCH(...) else if (false) -#define MOODYCAMEL_RETHROW -#define MOODYCAMEL_THROW(expr) -#endif - -#ifndef MOODYCAMEL_NOEXCEPT -#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED) -#define MOODYCAMEL_NOEXCEPT -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true -#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800 -// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-( -// We have to assume *all* non-trivial constructors may throw on VS2012! -#define MOODYCAMEL_NOEXCEPT _NOEXCEPT -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value) -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) -#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900 -#define MOODYCAMEL_NOEXCEPT _NOEXCEPT -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value) -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) -#else -#define MOODYCAMEL_NOEXCEPT noexcept -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr) -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr) -#endif -#endif - -#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED -#ifdef MCDBGQ_USE_RELACY -#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED -#else -// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445 -// g++ <=4.7 doesn't support thread_local either. -// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work -#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) -// Assume `thread_local` is fully supported in all other C++11 compilers/platforms -//#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // always disabled for now since several users report having problems with it on -#endif -#endif -#endif - -// VS2012 doesn't support deleted functions. -// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called. -#ifndef MOODYCAMEL_DELETE_FUNCTION -#if defined(_MSC_VER) && _MSC_VER < 1800 -#define MOODYCAMEL_DELETE_FUNCTION -#else -#define MOODYCAMEL_DELETE_FUNCTION = delete -#endif -#endif - -// Compiler-specific likely/unlikely hints -namespace moodycamel { namespace details { -#if defined(__GNUC__) - static inline bool (likely)(bool x) { return __builtin_expect((x), true); } - static inline bool (unlikely)(bool x) { return __builtin_expect((x), false); } -#else - static inline bool (likely)(bool x) { return x; } - static inline bool (unlikely)(bool x) { return x; } -#endif -} } - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG -#include "internal/concurrentqueue_internal_debug.h" -#endif - -namespace moodycamel { -namespace details { - template - struct const_numeric_max { - static_assert(std::is_integral::value, "const_numeric_max can only be used with integers"); - static const T value = std::numeric_limits::is_signed - ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1) - : static_cast(-1); - }; - -#if defined(__GLIBCXX__) - typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while -#else - typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std:: -#endif - - // Some platforms have incorrectly set max_align_t to a type with <8 bytes alignment even while supporting - // 8-byte aligned scalar values (*cough* 32-bit iOS). Work around this with our own union. See issue #64. - typedef union { - std_max_align_t x; - long long y; - void* z; - } max_align_t; -} - -// Default traits for the ConcurrentQueue. To change some of the -// traits without re-implementing all of them, inherit from this -// struct and shadow the declarations you wish to be different; -// since the traits are used as a template type parameter, the -// shadowed declarations will be used where defined, and the defaults -// otherwise. -struct ConcurrentQueueDefaultTraits -{ - // General-purpose size type. std::size_t is strongly recommended. - typedef std::size_t size_t; - - // The type used for the enqueue and dequeue indices. Must be at least as - // large as size_t. Should be significantly larger than the number of elements - // you expect to hold at once, especially if you have a high turnover rate; - // for example, on 32-bit x86, if you expect to have over a hundred million - // elements or pump several million elements through your queue in a very - // short space of time, using a 32-bit type *may* trigger a race condition. - // A 64-bit int type is recommended in that case, and in practice will - // prevent a race condition no matter the usage of the queue. Note that - // whether the queue is lock-free with a 64-int type depends on the whether - // std::atomic is lock-free, which is platform-specific. - typedef std::size_t index_t; - - // Internally, all elements are enqueued and dequeued from multi-element - // blocks; this is the smallest controllable unit. If you expect few elements - // but many producers, a smaller block size should be favoured. For few producers - // and/or many elements, a larger block size is preferred. A sane default - // is provided. Must be a power of 2. - static const size_t BLOCK_SIZE = 32; - - // For explicit producers (i.e. when using a producer token), the block is - // checked for being empty by iterating through a list of flags, one per element. - // For large block sizes, this is too inefficient, and switching to an atomic - // counter-based approach is faster. The switch is made for block sizes strictly - // larger than this threshold. - static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32; - - // How many full blocks can be expected for a single explicit producer? This should - // reflect that number's maximum for optimal performance. Must be a power of 2. - static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32; - - // How many full blocks can be expected for a single implicit producer? This should - // reflect that number's maximum for optimal performance. Must be a power of 2. - static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32; - - // The initial size of the hash table mapping thread IDs to implicit producers. - // Note that the hash is resized every time it becomes half full. - // Must be a power of two, and either 0 or at least 1. If 0, implicit production - // (using the enqueue methods without an explicit producer token) is disabled. - static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32; - - // Controls the number of items that an explicit consumer (i.e. one with a token) - // must consume before it causes all consumers to rotate and move on to the next - // internal queue. - static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256; - - // The maximum number of elements (inclusive) that can be enqueued to a sub-queue. - // Enqueue operations that would cause this limit to be surpassed will fail. Note - // that this limit is enforced at the block level (for performance reasons), i.e. - // it's rounded up to the nearest block size. - static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value; - - -#ifndef MCDBGQ_USE_RELACY - // Memory allocation can be customized if needed. - // malloc should return nullptr on failure, and handle alignment like std::malloc. -#if defined(malloc) || defined(free) - // Gah, this is 2015, stop defining macros that break standard code already! - // Work around malloc/free being special macros: - static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); } - static inline void WORKAROUND_free(void* ptr) { return free(ptr); } - static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); } - static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); } -#else - static inline void* malloc(size_t size) { return std::malloc(size); } - static inline void free(void* ptr) { return std::free(ptr); } -#endif -#else - // Debug versions when running under the Relacy race detector (ignore - // these in user code) - static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); } - static inline void free(void* ptr) { return rl::rl_free(ptr, $); } -#endif -}; - - -// When producing or consuming many elements, the most efficient way is to: -// 1) Use one of the bulk-operation methods of the queue with a token -// 2) Failing that, use the bulk-operation methods without a token -// 3) Failing that, create a token and use that with the single-item methods -// 4) Failing that, use the single-parameter methods of the queue -// Having said that, don't create tokens willy-nilly -- ideally there should be -// a maximum of one token per thread (of each kind). -struct ProducerToken; -struct ConsumerToken; - -template class ConcurrentQueue; -template class BlockingConcurrentQueue; -class ConcurrentQueueTests; - - -namespace details -{ - struct ConcurrentQueueProducerTypelessBase - { - ConcurrentQueueProducerTypelessBase* next; - std::atomic inactive; - ProducerToken* token; - - ConcurrentQueueProducerTypelessBase() - : next(nullptr), inactive(false), token(nullptr) - { - } - }; - - template struct _hash_32_or_64 { - static inline std::uint32_t hash(std::uint32_t h) - { - // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - // Since the thread ID is already unique, all we really want to do is propagate that - // uniqueness evenly across all the bits, so that we can use a subset of the bits while - // reducing collisions significantly - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - return h ^ (h >> 16); - } - }; - template<> struct _hash_32_or_64<1> { - static inline std::uint64_t hash(std::uint64_t h) - { - h ^= h >> 33; - h *= 0xff51afd7ed558ccd; - h ^= h >> 33; - h *= 0xc4ceb9fe1a85ec53; - return h ^ (h >> 33); - } - }; - template struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { }; - - static inline size_t hash_thread_id(thread_id_t id) - { - static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values"); - return static_cast(hash_32_or_64::thread_id_hash_t)>::hash( - thread_id_converter::prehash(id))); - } - - template - static inline bool circular_less_than(T a, T b) - { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4554) -#endif - static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "circular_less_than is intended to be used only with unsigned integer types"); - return static_cast(a - b) > static_cast(static_cast(1) << static_cast(sizeof(T) * CHAR_BIT - 1)); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } - - template - static inline char* align_for(char* ptr) - { - const std::size_t alignment = std::alignment_of::value; - return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; - } - - template - static inline T ceil_to_pow_2(T x) - { - static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types"); - - // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - for (std::size_t i = 1; i < sizeof(T); i <<= 1) { - x |= x >> (i << 3); - } - ++x; - return x; - } - - template - static inline void swap_relaxed(std::atomic& left, std::atomic& right) - { - T temp = std::move(left.load(std::memory_order_relaxed)); - left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed); - right.store(std::move(temp), std::memory_order_relaxed); - } - - template - static inline T const& nomove(T const& x) - { - return x; - } - - template - struct nomove_if - { - template - static inline T const& eval(T const& x) - { - return x; - } - }; - - template<> - struct nomove_if - { - template - static inline auto eval(U&& x) - -> decltype(std::forward(x)) - { - return std::forward(x); - } - }; - - template - static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it) - { - return *it; - } - -#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - template struct is_trivially_destructible : std::is_trivially_destructible { }; -#else - template struct is_trivially_destructible : std::has_trivial_destructor { }; -#endif - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED -#ifdef MCDBGQ_USE_RELACY - typedef RelacyThreadExitListener ThreadExitListener; - typedef RelacyThreadExitNotifier ThreadExitNotifier; -#else - struct ThreadExitListener - { - typedef void (*callback_t)(void*); - callback_t callback; - void* userData; - - ThreadExitListener* next; // reserved for use by the ThreadExitNotifier - }; - - - class ThreadExitNotifier - { - public: - static void subscribe(ThreadExitListener* listener) - { - auto& tlsInst = instance(); - listener->next = tlsInst.tail; - tlsInst.tail = listener; - } - - static void unsubscribe(ThreadExitListener* listener) - { - auto& tlsInst = instance(); - ThreadExitListener** prev = &tlsInst.tail; - for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) { - if (ptr == listener) { - *prev = ptr->next; - break; - } - prev = &ptr->next; - } - } - - private: - ThreadExitNotifier() : tail(nullptr) { } - ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; - ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; - - ~ThreadExitNotifier() - { - // This thread is about to exit, let everyone know! - assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined."); - for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) { - ptr->callback(ptr->userData); - } - } - - // Thread-local - static inline ThreadExitNotifier& instance() - { - static thread_local ThreadExitNotifier notifier; - return notifier; - } - - private: - ThreadExitListener* tail; - }; -#endif -#endif - - template struct static_is_lock_free_num { enum { value = 0 }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_CHAR_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_SHORT_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_INT_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_LONG_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_LLONG_LOCK_FREE }; }; - template struct static_is_lock_free : static_is_lock_free_num::type> { }; - template<> struct static_is_lock_free { enum { value = ATOMIC_BOOL_LOCK_FREE }; }; - template struct static_is_lock_free { enum { value = ATOMIC_POINTER_LOCK_FREE }; }; -} - - -struct ProducerToken -{ - template - explicit ProducerToken(ConcurrentQueue& queue); - - template - explicit ProducerToken(BlockingConcurrentQueue& queue); - - ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT - : producer(other.producer) - { - other.producer = nullptr; - if (producer != nullptr) { - producer->token = this; - } - } - - inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT - { - swap(other); - return *this; - } - - void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT - { - std::swap(producer, other.producer); - if (producer != nullptr) { - producer->token = this; - } - if (other.producer != nullptr) { - other.producer->token = &other; - } - } - - // A token is always valid unless: - // 1) Memory allocation failed during construction - // 2) It was moved via the move constructor - // (Note: assignment does a swap, leaving both potentially valid) - // 3) The associated queue was destroyed - // Note that if valid() returns true, that only indicates - // that the token is valid for use with a specific queue, - // but not which one; that's up to the user to track. - inline bool valid() const { return producer != nullptr; } - - ~ProducerToken() - { - if (producer != nullptr) { - producer->token = nullptr; - producer->inactive.store(true, std::memory_order_release); - } - } - - // Disable copying and assignment - ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; - ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; - -private: - template friend class ConcurrentQueue; - friend class ConcurrentQueueTests; - -protected: - details::ConcurrentQueueProducerTypelessBase* producer; -}; - - -struct ConsumerToken -{ - template - explicit ConsumerToken(ConcurrentQueue& q); - - template - explicit ConsumerToken(BlockingConcurrentQueue& q); - - ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT - : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer) - { - } - - inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT - { - swap(other); - return *this; - } - - void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT - { - std::swap(initialOffset, other.initialOffset); - std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset); - std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent); - std::swap(currentProducer, other.currentProducer); - std::swap(desiredProducer, other.desiredProducer); - } - - // Disable copying and assignment - ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; - ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; - -private: - template friend class ConcurrentQueue; - friend class ConcurrentQueueTests; - -private: // but shared with ConcurrentQueue - std::uint32_t initialOffset; - std::uint32_t lastKnownGlobalOffset; - std::uint32_t itemsConsumedFromCurrent; - details::ConcurrentQueueProducerTypelessBase* currentProducer; - details::ConcurrentQueueProducerTypelessBase* desiredProducer; -}; - -// Need to forward-declare this swap because it's in a namespace. -// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces -template -inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT; - - -template -class ConcurrentQueue -{ -public: - typedef ::moodycamel::ProducerToken producer_token_t; - typedef ::moodycamel::ConsumerToken consumer_token_t; - - typedef typename Traits::index_t index_t; - typedef typename Traits::size_t size_t; - - static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE); - static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD); - static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE); - static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE); - static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE); - static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE); -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!) -#pragma warning(disable: 4309) // static_cast: Truncation of constant value -#endif - static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - static_cast(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) ? details::const_numeric_max::value : ((static_cast(Traits::MAX_SUBQUEUE_SIZE) + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::size_t must be an unsigned integral type"); - static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::index_t must be an unsigned integral type"); - static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t"); - static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)"); - static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD > 1) && !(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD & (EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)"); - static_assert((EXPLICIT_INITIAL_INDEX_SIZE > 1) && !(EXPLICIT_INITIAL_INDEX_SIZE & (EXPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); - static_assert((IMPLICIT_INITIAL_INDEX_SIZE > 1) && !(IMPLICIT_INITIAL_INDEX_SIZE & (IMPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); - static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) || !(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE & (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2"); - static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)"); - -public: - // Creates a queue with at least `capacity` element slots; note that the - // actual number of elements that can be inserted without additional memory - // allocation depends on the number of producers and the block size (e.g. if - // the block size is equal to `capacity`, only a single block will be allocated - // up-front, which means only a single producer will be able to enqueue elements - // without an extra allocation -- blocks aren't shared between producers). - // This method is not thread safe -- it is up to the user to ensure that the - // queue is fully constructed before it starts being used by other threads (this - // includes making the memory effects of construction visible, possibly with a - // memory barrier). - explicit ConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE) - : producerListTail(nullptr), - producerCount(0), - initialBlockPoolIndex(0), - nextExplicitConsumerId(0), - globalExplicitConsumerOffset(0) - { - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - populate_initial_implicit_producer_hash(); - populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1)); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - // Track all the producers using a fully-resolved typed list for - // each kind; this makes it possible to debug them starting from - // the root queue object (otherwise wacky casts are needed that - // don't compile in the debugger's expression evaluator). - explicitProducers.store(nullptr, std::memory_order_relaxed); - implicitProducers.store(nullptr, std::memory_order_relaxed); -#endif - } - - // Computes the correct amount of pre-allocated blocks for you based - // on the minimum number of elements you want available at any given - // time, and the maximum concurrent number of each type of producer. - ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers) - : producerListTail(nullptr), - producerCount(0), - initialBlockPoolIndex(0), - nextExplicitConsumerId(0), - globalExplicitConsumerOffset(0) - { - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - populate_initial_implicit_producer_hash(); - size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers); - populate_initial_block_list(blocks); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - explicitProducers.store(nullptr, std::memory_order_relaxed); - implicitProducers.store(nullptr, std::memory_order_relaxed); -#endif - } - - // Note: The queue should not be accessed concurrently while it's - // being deleted. It's up to the user to synchronize this. - // This method is not thread safe. - ~ConcurrentQueue() - { - // Destroy producers - auto ptr = producerListTail.load(std::memory_order_relaxed); - while (ptr != nullptr) { - auto next = ptr->next_prod(); - if (ptr->token != nullptr) { - ptr->token->producer = nullptr; - } - destroy(ptr); - ptr = next; - } - - // Destroy implicit producer hash tables - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) { - auto hash = implicitProducerHash.load(std::memory_order_relaxed); - while (hash != nullptr) { - auto prev = hash->prev; - if (prev != nullptr) { // The last hash is part of this object and was not allocated dynamically - for (size_t i = 0; i != hash->capacity; ++i) { - hash->entries[i].~ImplicitProducerKVP(); - } - hash->~ImplicitProducerHash(); - (Traits::free)(hash); - } - hash = prev; - } - } - - // Destroy global free list - auto block = freeList.head_unsafe(); - while (block != nullptr) { - auto next = block->freeListNext.load(std::memory_order_relaxed); - if (block->dynamicallyAllocated) { - destroy(block); - } - block = next; - } - - // Destroy initial free list - destroy_array(initialBlockPool, initialBlockPoolSize); - } - - // Disable copying and copy assignment - ConcurrentQueue(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; - ConcurrentQueue& operator=(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; - - // Moving is supported, but note that it is *not* a thread-safe operation. - // Nobody can use the queue while it's being moved, and the memory effects - // of that move must be propagated to other threads before they can use it. - // Note: When a queue is moved, its tokens are still valid but can only be - // used with the destination queue (i.e. semantically they are moved along - // with the queue itself). - ConcurrentQueue(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT - : producerListTail(other.producerListTail.load(std::memory_order_relaxed)), - producerCount(other.producerCount.load(std::memory_order_relaxed)), - initialBlockPoolIndex(other.initialBlockPoolIndex.load(std::memory_order_relaxed)), - initialBlockPool(other.initialBlockPool), - initialBlockPoolSize(other.initialBlockPoolSize), - freeList(std::move(other.freeList)), - nextExplicitConsumerId(other.nextExplicitConsumerId.load(std::memory_order_relaxed)), - globalExplicitConsumerOffset(other.globalExplicitConsumerOffset.load(std::memory_order_relaxed)) - { - // Move the other one into this, and leave the other one as an empty queue - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - populate_initial_implicit_producer_hash(); - swap_implicit_producer_hashes(other); - - other.producerListTail.store(nullptr, std::memory_order_relaxed); - other.producerCount.store(0, std::memory_order_relaxed); - other.nextExplicitConsumerId.store(0, std::memory_order_relaxed); - other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.explicitProducers.store(nullptr, std::memory_order_relaxed); - implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.implicitProducers.store(nullptr, std::memory_order_relaxed); -#endif - - other.initialBlockPoolIndex.store(0, std::memory_order_relaxed); - other.initialBlockPoolSize = 0; - other.initialBlockPool = nullptr; - - reown_producers(); - } - - inline ConcurrentQueue& operator=(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT - { - return swap_internal(other); - } - - // Swaps this queue's state with the other's. Not thread-safe. - // Swapping two queues does not invalidate their tokens, however - // the tokens that were created for one queue must be used with - // only the swapped queue (i.e. the tokens are tied to the - // queue's movable state, not the object itself). - inline void swap(ConcurrentQueue& other) MOODYCAMEL_NOEXCEPT - { - swap_internal(other); - } - -private: - ConcurrentQueue& swap_internal(ConcurrentQueue& other) - { - if (this == &other) { - return *this; - } - - details::swap_relaxed(producerListTail, other.producerListTail); - details::swap_relaxed(producerCount, other.producerCount); - details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex); - std::swap(initialBlockPool, other.initialBlockPool); - std::swap(initialBlockPoolSize, other.initialBlockPoolSize); - freeList.swap(other.freeList); - details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId); - details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset); - - swap_implicit_producer_hashes(other); - - reown_producers(); - other.reown_producers(); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - details::swap_relaxed(explicitProducers, other.explicitProducers); - details::swap_relaxed(implicitProducers, other.implicitProducers); -#endif - - return *this; - } - -public: - // Enqueues a single item (by copying it). - // Allocates memory if required. Only fails if memory allocation fails (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, - // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(T const& item) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - return inner_enqueue(item); - } - - // Enqueues a single item (by moving it, if possible). - // Allocates memory if required. Only fails if memory allocation fails (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, - // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(T&& item) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - return inner_enqueue(std::move(item)); - } - - // Enqueues a single item (by copying it) using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails (or - // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(producer_token_t const& token, T const& item) - { - return inner_enqueue(token, item); - } - - // Enqueues a single item (by moving it, if possible) using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails (or - // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(producer_token_t const& token, T&& item) - { - return inner_enqueue(token, std::move(item)); - } - - // Enqueues several items. - // Allocates memory if required. Only fails if memory allocation fails (or - // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Note: Use std::make_move_iterator if the elements should be moved instead of copied. - // Thread-safe. - template - bool enqueue_bulk(It itemFirst, size_t count) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - return inner_enqueue_bulk(itemFirst, count); - } - - // Enqueues several items using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails - // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - return inner_enqueue_bulk(token, itemFirst, count); - } - - // Enqueues a single item (by copying it). - // Does not allocate memory. Fails if not enough room to enqueue (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - // is 0). - // Thread-safe. - inline bool try_enqueue(T const& item) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - return inner_enqueue(item); - } - - // Enqueues a single item (by moving it, if possible). - // Does not allocate memory (except for one-time implicit producer). - // Fails if not enough room to enqueue (or implicit production is - // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). - // Thread-safe. - inline bool try_enqueue(T&& item) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - return inner_enqueue(std::move(item)); - } - - // Enqueues a single item (by copying it) using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Thread-safe. - inline bool try_enqueue(producer_token_t const& token, T const& item) - { - return inner_enqueue(token, item); - } - - // Enqueues a single item (by moving it, if possible) using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Thread-safe. - inline bool try_enqueue(producer_token_t const& token, T&& item) - { - return inner_enqueue(token, std::move(item)); - } - - // Enqueues several items. - // Does not allocate memory (except for one-time implicit producer). - // Fails if not enough room to enqueue (or implicit production is - // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - bool try_enqueue_bulk(It itemFirst, size_t count) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - return inner_enqueue_bulk(itemFirst, count); - } - - // Enqueues several items using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - return inner_enqueue_bulk(token, itemFirst, count); - } - - - - // Attempts to dequeue from the queue. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - bool try_dequeue(U& item) - { - // Instead of simply trying each producer in turn (which could cause needless contention on the first - // producer), we score them heuristically. - size_t nonEmptyCount = 0; - ProducerBase* best = nullptr; - size_t bestSize = 0; - for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) { - auto size = ptr->size_approx(); - if (size > 0) { - if (size > bestSize) { - bestSize = size; - best = ptr; - } - ++nonEmptyCount; - } - } - - // If there was at least one non-empty queue but it appears empty at the time - // we try to dequeue from it, we need to make sure every queue's been tried - if (nonEmptyCount > 0) { - if ((details::likely)(best->dequeue(item))) { - return true; - } - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - if (ptr != best && ptr->dequeue(item)) { - return true; - } - } - } - return false; - } - - // Attempts to dequeue from the queue. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // This differs from the try_dequeue(item) method in that this one does - // not attempt to reduce contention by interleaving the order that producer - // streams are dequeued from. So, using this method can reduce overall throughput - // under contention, but will give more predictable results in single-threaded - // consumer scenarios. This is mostly only useful for internal unit tests. - // Never allocates. Thread-safe. - template - bool try_dequeue_non_interleaved(U& item) - { - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - if (ptr->dequeue(item)) { - return true; - } - } - return false; - } - - // Attempts to dequeue from the queue using an explicit consumer token. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - bool try_dequeue(consumer_token_t& token, U& item) - { - // The idea is roughly as follows: - // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less - // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place - // If there's no items where you're supposed to be, keep moving until you find a producer with some items - // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it - - if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { - if (!update_current_producer_after_rotation(token)) { - return false; - } - } - - // If there was at least one non-empty queue but it appears empty at the time - // we try to dequeue from it, we need to make sure every queue's been tried - if (static_cast(token.currentProducer)->dequeue(item)) { - if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { - globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); - } - return true; - } - - auto tail = producerListTail.load(std::memory_order_acquire); - auto ptr = static_cast(token.currentProducer)->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - while (ptr != static_cast(token.currentProducer)) { - if (ptr->dequeue(item)) { - token.currentProducer = ptr; - token.itemsConsumedFromCurrent = 1; - return true; - } - ptr = ptr->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - } - return false; - } - - // Attempts to dequeue several elements from the queue. - // Returns the number of items actually dequeued. - // Returns 0 if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - size_t try_dequeue_bulk(It itemFirst, size_t max) - { - size_t count = 0; - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - count += ptr->dequeue_bulk(itemFirst, max - count); - if (count == max) { - break; - } - } - return count; - } - - // Attempts to dequeue several elements from the queue using an explicit consumer token. - // Returns the number of items actually dequeued. - // Returns 0 if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max) - { - if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { - if (!update_current_producer_after_rotation(token)) { - return 0; - } - } - - size_t count = static_cast(token.currentProducer)->dequeue_bulk(itemFirst, max); - if (count == max) { - if ((token.itemsConsumedFromCurrent += static_cast(max)) >= EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { - globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); - } - return max; - } - token.itemsConsumedFromCurrent += static_cast(count); - max -= count; - - auto tail = producerListTail.load(std::memory_order_acquire); - auto ptr = static_cast(token.currentProducer)->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - while (ptr != static_cast(token.currentProducer)) { - auto dequeued = ptr->dequeue_bulk(itemFirst, max); - count += dequeued; - if (dequeued != 0) { - token.currentProducer = ptr; - token.itemsConsumedFromCurrent = static_cast(dequeued); - } - if (dequeued == max) { - break; - } - max -= dequeued; - ptr = ptr->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - } - return count; - } - - - - // Attempts to dequeue from a specific producer's inner queue. - // If you happen to know which producer you want to dequeue from, this - // is significantly faster than using the general-case try_dequeue methods. - // Returns false if the producer's queue appeared empty at the time it - // was checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline bool try_dequeue_from_producer(producer_token_t const& producer, U& item) - { - return static_cast(producer.producer)->dequeue(item); - } - - // Attempts to dequeue several elements from a specific producer's inner queue. - // Returns the number of items actually dequeued. - // If you happen to know which producer you want to dequeue from, this - // is significantly faster than using the general-case try_dequeue methods. - // Returns 0 if the producer's queue appeared empty at the time it - // was checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline size_t try_dequeue_bulk_from_producer(producer_token_t const& producer, It itemFirst, size_t max) - { - return static_cast(producer.producer)->dequeue_bulk(itemFirst, max); - } - - - // Returns an estimate of the total number of elements currently in the queue. This - // estimate is only accurate if the queue has completely stabilized before it is called - // (i.e. all enqueue and dequeue operations have completed and their memory effects are - // visible on the calling thread, and no further operations start while this method is - // being called). - // Thread-safe. - size_t size_approx() const - { - size_t size = 0; - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - size += ptr->size_approx(); - } - return size; - } - - - // Returns true if the underlying atomic variables used by - // the queue are lock-free (they should be on most platforms). - // Thread-safe. - static bool is_lock_free() - { - return - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::thread_id_numeric_size_t>::value == 2; - } - - -private: - friend struct ProducerToken; - friend struct ConsumerToken; - struct ExplicitProducer; - friend struct ExplicitProducer; - struct ImplicitProducer; - friend struct ImplicitProducer; - friend class ConcurrentQueueTests; - - enum AllocationMode { CanAlloc, CannotAlloc }; - - - /////////////////////////////// - // Queue methods - /////////////////////////////// - - template - inline bool inner_enqueue(producer_token_t const& token, U&& element) - { - return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue(std::forward(element)); - } - - template - inline bool inner_enqueue(U&& element) - { - auto producer = get_or_add_implicit_producer(); - return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue(std::forward(element)); - } - - template - inline bool inner_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk(itemFirst, count); - } - - template - inline bool inner_enqueue_bulk(It itemFirst, size_t count) - { - auto producer = get_or_add_implicit_producer(); - return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk(itemFirst, count); - } - - inline bool update_current_producer_after_rotation(consumer_token_t& token) - { - // Ah, there's been a rotation, figure out where we should be! - auto tail = producerListTail.load(std::memory_order_acquire); - if (token.desiredProducer == nullptr && tail == nullptr) { - return false; - } - auto prodCount = producerCount.load(std::memory_order_relaxed); - auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed); - if ((details::unlikely)(token.desiredProducer == nullptr)) { - // Aha, first time we're dequeueing anything. - // Figure out our local position - // Note: offset is from start, not end, but we're traversing from end -- subtract from count first - std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount); - token.desiredProducer = tail; - for (std::uint32_t i = 0; i != offset; ++i) { - token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); - if (token.desiredProducer == nullptr) { - token.desiredProducer = tail; - } - } - } - - std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset; - if (delta >= prodCount) { - delta = delta % prodCount; - } - for (std::uint32_t i = 0; i != delta; ++i) { - token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); - if (token.desiredProducer == nullptr) { - token.desiredProducer = tail; - } - } - - token.lastKnownGlobalOffset = globalOffset; - token.currentProducer = token.desiredProducer; - token.itemsConsumedFromCurrent = 0; - return true; - } - - - /////////////////////////// - // Free list - /////////////////////////// - - template - struct FreeListNode - { - FreeListNode() : freeListRefs(0), freeListNext(nullptr) { } - - std::atomic freeListRefs; - std::atomic freeListNext; - }; - - // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but - // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly - // speedy under low contention. - template // N must inherit FreeListNode or have the same fields (and initialization of them) - struct FreeList - { - FreeList() : freeListHead(nullptr) { } - FreeList(FreeList&& other) : freeListHead(other.freeListHead.load(std::memory_order_relaxed)) { other.freeListHead.store(nullptr, std::memory_order_relaxed); } - void swap(FreeList& other) { details::swap_relaxed(freeListHead, other.freeListHead); } - - FreeList(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; - FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; - - inline void add(N* node) - { -#if MCDBGQ_NOLOCKFREE_FREELIST - debug::DebugLock lock(mutex); -#endif - // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to - // set it using a fetch_add - if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) { - // Oh look! We were the last ones referencing this node, and we know - // we want to add it to the free list, so let's do it! - add_knowing_refcount_is_zero(node); - } - } - - inline N* try_get() - { -#if MCDBGQ_NOLOCKFREE_FREELIST - debug::DebugLock lock(mutex); -#endif - auto head = freeListHead.load(std::memory_order_acquire); - while (head != nullptr) { - auto prevHead = head; - auto refs = head->freeListRefs.load(std::memory_order_relaxed); - if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) { - head = freeListHead.load(std::memory_order_acquire); - continue; - } - - // Good, reference count has been incremented (it wasn't at zero), which means we can read the - // next and not worry about it changing between now and the time we do the CAS - auto next = head->freeListNext.load(std::memory_order_relaxed); - if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) { - // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no - // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on). - assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0); - - // Decrease refcount twice, once for our ref, and once for the list's ref - head->freeListRefs.fetch_sub(2, std::memory_order_release); - return head; - } - - // OK, the head must have changed on us, but we still need to decrease the refcount we increased. - // Note that we don't need to release any memory effects, but we do need to ensure that the reference - // count decrement happens-after the CAS on the head. - refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel); - if (refs == SHOULD_BE_ON_FREELIST + 1) { - add_knowing_refcount_is_zero(prevHead); - } - } - - return nullptr; - } - - // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes) - N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); } - - private: - inline void add_knowing_refcount_is_zero(N* node) - { - // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run - // only one copy of this method per node at a time, i.e. the single thread case), then we know - // we can safely change the next pointer of the node; however, once the refcount is back above - // zero, then other threads could increase it (happens under heavy contention, when the refcount - // goes to zero in between a load and a refcount increment of a node in try_get, then back up to - // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS - // to add the node to the actual list fails, decrease the refcount and leave the add operation to - // the next thread who puts the refcount back at zero (which could be us, hence the loop). - auto head = freeListHead.load(std::memory_order_relaxed); - while (true) { - node->freeListNext.store(head, std::memory_order_relaxed); - node->freeListRefs.store(1, std::memory_order_release); - if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) { - // Hmm, the add failed, but we can only try again when the refcount goes back to zero - if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) { - continue; - } - } - return; - } - } - - private: - // Implemented like a stack, but where node order doesn't matter (nodes are inserted out of order under contention) - std::atomic freeListHead; - - static const std::uint32_t REFS_MASK = 0x7FFFFFFF; - static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000; - -#if MCDBGQ_NOLOCKFREE_FREELIST - debug::DebugMutex mutex; -#endif - }; - - - /////////////////////////// - // Block - /////////////////////////// - - enum InnerQueueContext { implicit_context = 0, explicit_context = 1 }; - - struct Block - { - Block() - : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true) - { -#if MCDBGQ_TRACKMEM - owner = nullptr; -#endif - } - - template - inline bool is_empty() const - { - if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Check flags - for (size_t i = 0; i < BLOCK_SIZE; ++i) { - if (!emptyFlags[i].load(std::memory_order_relaxed)) { - return false; - } - } - - // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set - std::atomic_thread_fence(std::memory_order_acquire); - return true; - } - else { - // Check counter - if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) { - std::atomic_thread_fence(std::memory_order_acquire); - return true; - } - assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE); - return false; - } - } - - // Returns true if the block is now empty (does not apply in explicit context) - template - inline bool set_empty(index_t i) - { - if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Set flag - assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].load(std::memory_order_relaxed)); - emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].store(true, std::memory_order_release); - return false; - } - else { - // Increment counter - auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release); - assert(prevVal < BLOCK_SIZE); - return prevVal == BLOCK_SIZE - 1; - } - } - - // Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0). - // Returns true if the block is now empty (does not apply in explicit context). - template - inline bool set_many_empty(index_t i, size_t count) - { - if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Set flags - std::atomic_thread_fence(std::memory_order_release); - i = BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1)) - count + 1; - for (size_t j = 0; j != count; ++j) { - assert(!emptyFlags[i + j].load(std::memory_order_relaxed)); - emptyFlags[i + j].store(true, std::memory_order_relaxed); - } - return false; - } - else { - // Increment counter - auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release); - assert(prevVal + count <= BLOCK_SIZE); - return prevVal + count == BLOCK_SIZE; - } - } - - template - inline void set_all_empty() - { - if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Set all flags - for (size_t i = 0; i != BLOCK_SIZE; ++i) { - emptyFlags[i].store(true, std::memory_order_relaxed); - } - } - else { - // Reset counter - elementsCompletelyDequeued.store(BLOCK_SIZE, std::memory_order_relaxed); - } - } - - template - inline void reset_empty() - { - if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Reset flags - for (size_t i = 0; i != BLOCK_SIZE; ++i) { - emptyFlags[i].store(false, std::memory_order_relaxed); - } - } - else { - // Reset counter - elementsCompletelyDequeued.store(0, std::memory_order_relaxed); - } - } - - inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return static_cast(static_cast(elements)) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } - inline T const* operator[](index_t idx) const MOODYCAMEL_NOEXCEPT { return static_cast(static_cast(elements)) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } - - private: - // IMPORTANT: This must be the first member in Block, so that if T depends on the alignment of - // addresses returned by malloc, that alignment will be preserved. Apparently clang actually - // generates code that uses this assumption for AVX instructions in some cases. Ideally, we - // should also align Block to the alignment of T in case it's higher than malloc's 16-byte - // alignment, but this is hard to do in a cross-platform way. Assert for this case: - static_assert(std::alignment_of::value <= std::alignment_of::value, "The queue does not support super-aligned types at this time"); - // Additionally, we need the alignment of Block itself to be a multiple of max_align_t since - // otherwise the appropriate padding will not be added at the end of Block in order to make - // arrays of Blocks all be properly aligned (not just the first one). We use a union to force - // this. - union { - char elements[sizeof(T) * BLOCK_SIZE]; - details::max_align_t dummy; - }; - public: - Block* next; - std::atomic elementsCompletelyDequeued; - std::atomic emptyFlags[BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD ? BLOCK_SIZE : 1]; - public: - std::atomic freeListRefs; - std::atomic freeListNext; - std::atomic shouldBeOnFreeList; - bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool' - -#if MCDBGQ_TRACKMEM - void* owner; -#endif - }; - static_assert(std::alignment_of::value >= std::alignment_of::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping"); - - -#if MCDBGQ_TRACKMEM -public: - struct MemStats; -private: -#endif - - /////////////////////////// - // Producer base - /////////////////////////// - - struct ProducerBase : public details::ConcurrentQueueProducerTypelessBase - { - ProducerBase(ConcurrentQueue* parent_, bool isExplicit_) : - tailIndex(0), - headIndex(0), - dequeueOptimisticCount(0), - dequeueOvercommit(0), - tailBlock(nullptr), - isExplicit(isExplicit_), - parent(parent_) - { - } - - virtual ~ProducerBase() { }; - - template - inline bool dequeue(U& element) - { - if (isExplicit) { - return static_cast(this)->dequeue(element); - } - else { - return static_cast(this)->dequeue(element); - } - } - - template - inline size_t dequeue_bulk(It& itemFirst, size_t max) - { - if (isExplicit) { - return static_cast(this)->dequeue_bulk(itemFirst, max); - } - else { - return static_cast(this)->dequeue_bulk(itemFirst, max); - } - } - - inline ProducerBase* next_prod() const { return static_cast(next); } - - inline size_t size_approx() const - { - auto tail = tailIndex.load(std::memory_order_relaxed); - auto head = headIndex.load(std::memory_order_relaxed); - return details::circular_less_than(head, tail) ? static_cast(tail - head) : 0; - } - - inline index_t getTail() const { return tailIndex.load(std::memory_order_relaxed); } - protected: - std::atomic tailIndex; // Where to enqueue to next - std::atomic headIndex; // Where to dequeue from next - - std::atomic dequeueOptimisticCount; - std::atomic dequeueOvercommit; - - Block* tailBlock; - - public: - bool isExplicit; - ConcurrentQueue* parent; - - protected: -#if MCDBGQ_TRACKMEM - friend struct MemStats; -#endif - }; - - - /////////////////////////// - // Explicit queue - /////////////////////////// - - struct ExplicitProducer : public ProducerBase - { - explicit ExplicitProducer(ConcurrentQueue* parent) : - ProducerBase(parent, true), - blockIndex(nullptr), - pr_blockIndexSlotsUsed(0), - pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1), - pr_blockIndexFront(0), - pr_blockIndexEntries(nullptr), - pr_blockIndexRaw(nullptr) - { - size_t poolBasedIndexSize = details::ceil_to_pow_2(parent->initialBlockPoolSize) >> 1; - if (poolBasedIndexSize > pr_blockIndexSize) { - pr_blockIndexSize = poolBasedIndexSize; - } - - new_block_index(0); // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE - } - - ~ExplicitProducer() - { - // Destruct any elements not yet dequeued. - // Since we're in the destructor, we can assume all elements - // are either completely dequeued or completely not (no halfways). - if (this->tailBlock != nullptr) { // Note this means there must be a block index too - // First find the block that's partially dequeued, if any - Block* halfDequeuedBlock = nullptr; - if ((this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) != 0) { - // The head's not on a block boundary, meaning a block somewhere is partially dequeued - // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary) - size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1); - while (details::circular_less_than(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) { - i = (i + 1) & (pr_blockIndexSize - 1); - } - assert(details::circular_less_than(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed))); - halfDequeuedBlock = pr_blockIndexEntries[i].block; - } - - // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration) - auto block = this->tailBlock; - do { - block = block->next; - if (block->ConcurrentQueue::Block::template is_empty()) { - continue; - } - - size_t i = 0; // Offset into block - if (block == halfDequeuedBlock) { - i = static_cast(this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); - } - - // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index - auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast(this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); - while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) { - (*block)[i++]->~T(); - } - } while (block != this->tailBlock); - } - - // Destroy all blocks that we own - if (this->tailBlock != nullptr) { - auto block = this->tailBlock; - do { - auto nextBlock = block->next; - if (block->dynamicallyAllocated) { - destroy(block); - } - else { - this->parent->add_block_to_free_list(block); - } - block = nextBlock; - } while (block != this->tailBlock); - } - - // Destroy the block indices - auto header = static_cast(pr_blockIndexRaw); - while (header != nullptr) { - auto prev = static_cast(header->prev); - header->~BlockIndexHeader(); - (Traits::free)(header); - header = prev; - } - } - - template - inline bool enqueue(U&& element) - { - index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); - index_t newTailIndex = 1 + currentTailIndex; - if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - // We reached the end of a block, start a new one - auto startBlock = this->tailBlock; - auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; - if (this->tailBlock != nullptr && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { - // We can re-use the block ahead of us, it's empty! - this->tailBlock = this->tailBlock->next; - this->tailBlock->ConcurrentQueue::Block::template reset_empty(); - - // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the - // last block from it first -- except instead of removing then adding, we can just overwrite). - // Note that there must be a valid block index here, since even if allocation failed in the ctor, - // it would have been re-attempted when adding the first block to the queue; since there is such - // a block, a block index must have been successfully allocated. - } - else { - // Whatever head value we see here is >= the last value we saw here (relatively), - // and <= its current value. Since we have the most recent tail, the head must be - // <= to it. - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) - || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { - // We can't enqueue in another block because there's not enough leeway -- the - // tail could surpass the head by the time the block fills up! (Or we'll exceed - // the size limit, if the second part of the condition was true.) - return false; - } - // We're going to need a new block; check that the block index has room - if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) { - // Hmm, the circular block index is already full -- we'll need - // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if - // the initial allocation failed in the constructor. - - if (allocMode == CannotAlloc || !new_block_index(pr_blockIndexSlotsUsed)) { - return false; - } - } - - // Insert a new block in the circular linked list - auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); - if (newBlock == nullptr) { - return false; - } -#if MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template reset_empty(); - if (this->tailBlock == nullptr) { - newBlock->next = newBlock; - } - else { - newBlock->next = this->tailBlock->next; - this->tailBlock->next = newBlock; - } - this->tailBlock = newBlock; - ++pr_blockIndexSlotsUsed; - } - - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { - // The constructor may throw. We want the element not to appear in the queue in - // that case (without corrupting the queue): - MOODYCAMEL_TRY { - new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); - } - MOODYCAMEL_CATCH (...) { - // Revert change to the current block, but leave the new block available - // for next time - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? this->tailBlock : startBlock; - MOODYCAMEL_RETHROW; - } - } - else { - (void)startBlock; - (void)originalBlockIndexSlotsUsed; - } - - // Add block to block index - auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; - entry.base = currentTailIndex; - entry.block = this->tailBlock; - blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release); - pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); - - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - } - - // Enqueue - new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); - - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - bool dequeue(U& element) - { - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { - // Might be something to dequeue, let's give it a try - - // Note that this if is purely for performance purposes in the common case when the queue is - // empty and the values are eventually consistent -- we may enter here spuriously. - - // Note that whatever the values of overcommit and tail are, they are not going to change (unless we - // change them) and must be the same value at this point (inside the if) as when the if condition was - // evaluated. - - // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below. - // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in - // the fetch_add below will result in a value at least as recent as that (and therefore at least as large). - // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all - // read-modify-write operations are guaranteed to work on the latest value in the modification order), but - // unfortunately that can't be shown to be correct using only the C++11 standard. - // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case - std::atomic_thread_fence(std::memory_order_acquire); - - // Increment optimistic counter, then check if it went over the boundary - auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); - - // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever - // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now - // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon - // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount. - assert(overcommit <= myDequeueCount); - - // Note that we reload tail here in case it changed; it will be the same value as before or greater, since - // this load is sequenced after (happens after) the earlier load above. This is supported by read-read - // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order - tail = this->tailIndex.load(std::memory_order_acquire); - if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { - // Guaranteed to be at least one element to dequeue! - - // Get the index. Note that since there's guaranteed to be at least one element, this - // will never exceed tail. We need to do an acquire-release fence here since it's possible - // that whatever condition got us to this point was for an earlier enqueued element (that - // we already see the memory effects for), but that by the time we increment somebody else - // has incremented it, and we need to see the memory effects for *that* element, which is - // in such a case is necessarily visible on the thread that incremented it in the first - // place with the more current condition (they must have acquired a tail that is at least - // as recent). - auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); - - - // Determine which block the element is in - - auto localBlockIndex = blockIndex.load(std::memory_order_acquire); - auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); - - // We need to be careful here about subtracting and dividing because of index wrap-around. - // When an index wraps, we need to preserve the sign of the offset when dividing it by the - // block size (in order to get a correct signed block count offset in all cases): - auto headBase = localBlockIndex->entries[localBlockIndexHead].base; - auto blockBaseIndex = index & ~static_cast(BLOCK_SIZE - 1); - auto offset = static_cast(static_cast::type>(blockBaseIndex - headBase) / BLOCK_SIZE); - auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & (localBlockIndex->size - 1)].block; - - // Dequeue - auto& el = *((*block)[index]); - if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { - // Make sure the element is still fully dequeued and destroyed even if the assignment - // throws - struct Guard { - Block* block; - index_t index; - - ~Guard() - { - (*block)[index]->~T(); - block->ConcurrentQueue::Block::template set_empty(index); - } - } guard = { block, index }; - - element = std::move(el); - } - else { - element = std::move(el); - el.~T(); - block->ConcurrentQueue::Block::template set_empty(index); - } - - return true; - } - else { - // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent - this->dequeueOvercommit.fetch_add(1, std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write - } - } - - return false; - } - - template - bool enqueue_bulk(It itemFirst, size_t count) - { - // First, we need to make sure we have enough room to enqueue all of the elements; - // this means pre-allocating blocks and putting them in the block index (but only if - // all the allocations succeeded). - index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); - auto startBlock = this->tailBlock; - auto originalBlockIndexFront = pr_blockIndexFront; - auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; - - Block* firstAllocatedBlock = nullptr; - - // Figure out how many blocks we'll need to allocate, and do so - size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); - index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - if (blockBaseDiff > 0) { - // Allocate as many blocks as possible from ahead - while (blockBaseDiff > 0 && this->tailBlock != nullptr && this->tailBlock->next != firstAllocatedBlock && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { - blockBaseDiff -= static_cast(BLOCK_SIZE); - currentTailIndex += static_cast(BLOCK_SIZE); - - this->tailBlock = this->tailBlock->next; - firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; - - auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; - entry.base = currentTailIndex; - entry.block = this->tailBlock; - pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); - } - - // Now allocate as many blocks as necessary from the block pool - while (blockBaseDiff > 0) { - blockBaseDiff -= static_cast(BLOCK_SIZE); - currentTailIndex += static_cast(BLOCK_SIZE); - - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); - if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) { - if (allocMode == CannotAlloc || full || !new_block_index(originalBlockIndexSlotsUsed)) { - // Failed to allocate, undo changes (but keep injected blocks) - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - return false; - } - - // pr_blockIndexFront is updated inside new_block_index, so we need to - // update our fallback value too (since we keep the new index even if we - // later fail) - originalBlockIndexFront = originalBlockIndexSlotsUsed; - } - - // Insert a new block in the circular linked list - auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); - if (newBlock == nullptr) { - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - return false; - } - -#if MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template set_all_empty(); - if (this->tailBlock == nullptr) { - newBlock->next = newBlock; - } - else { - newBlock->next = this->tailBlock->next; - this->tailBlock->next = newBlock; - } - this->tailBlock = newBlock; - firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; - - ++pr_blockIndexSlotsUsed; - - auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; - entry.base = currentTailIndex; - entry.block = this->tailBlock; - pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); - } - - // Excellent, all allocations succeeded. Reset each block's emptiness before we fill them up, and - // publish the new block index front - auto block = firstAllocatedBlock; - while (true) { - block->ConcurrentQueue::Block::template reset_empty(); - if (block == this->tailBlock) { - break; - } - block = block->next; - } - - if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { - blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); - } - } - - // Enqueue, one block at a time - index_t newTailIndex = startTailIndex + static_cast(count); - currentTailIndex = startTailIndex; - auto endBlock = this->tailBlock; - this->tailBlock = startBlock; - assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { - this->tailBlock = firstAllocatedBlock; - } - while (true) { - auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(newTailIndex, stopIndex)) { - stopIndex = newTailIndex; - } - if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { - while (currentTailIndex != stopIndex) { - new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); - } - } - else { - MOODYCAMEL_TRY { - while (currentTailIndex != stopIndex) { - // Must use copy constructor even if move constructor is available - // because we may have to revert if there's an exception. - // Sorry about the horrible templated next line, but it was the only way - // to disable moving *at compile time*, which is important because a type - // may only define a (noexcept) move constructor, and so calls to the - // cctor will not compile, even if they are in an if branch that will never - // be executed - new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); - ++currentTailIndex; - ++itemFirst; - } - } - MOODYCAMEL_CATCH (...) { - // Oh dear, an exception's been thrown -- destroy the elements that - // were enqueued so far and revert the entire bulk operation (we'll keep - // any allocated blocks in our linked list for later, though). - auto constructedStopIndex = currentTailIndex; - auto lastBlockEnqueued = this->tailBlock; - - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - - if (!details::is_trivially_destructible::value) { - auto block = startBlock; - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - block = firstAllocatedBlock; - } - currentTailIndex = startTailIndex; - while (true) { - stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(constructedStopIndex, stopIndex)) { - stopIndex = constructedStopIndex; - } - while (currentTailIndex != stopIndex) { - (*block)[currentTailIndex++]->~T(); - } - if (block == lastBlockEnqueued) { - break; - } - block = block->next; - } - } - MOODYCAMEL_RETHROW; - } - } - - if (this->tailBlock == endBlock) { - assert(currentTailIndex == newTailIndex); - break; - } - this->tailBlock = this->tailBlock->next; - } - - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst))) && firstAllocatedBlock != nullptr) { - blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); - } - - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - size_t dequeue_bulk(It& itemFirst, size_t max) - { - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); - if (details::circular_less_than(0, desiredCount)) { - desiredCount = desiredCount < max ? desiredCount : max; - std::atomic_thread_fence(std::memory_order_acquire); - - auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); - assert(overcommit <= myDequeueCount); - - tail = this->tailIndex.load(std::memory_order_acquire); - auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); - if (details::circular_less_than(0, actualCount)) { - actualCount = desiredCount < actualCount ? desiredCount : actualCount; - if (actualCount < desiredCount) { - this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); - } - - // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this - // will never exceed tail. - auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); - - // Determine which block the first element is in - auto localBlockIndex = blockIndex.load(std::memory_order_acquire); - auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); - - auto headBase = localBlockIndex->entries[localBlockIndexHead].base; - auto firstBlockBaseIndex = firstIndex & ~static_cast(BLOCK_SIZE - 1); - auto offset = static_cast(static_cast::type>(firstBlockBaseIndex - headBase) / BLOCK_SIZE); - auto indexIndex = (localBlockIndexHead + offset) & (localBlockIndex->size - 1); - - // Iterate the blocks and dequeue - auto index = firstIndex; - do { - auto firstIndexInBlock = index; - auto endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - auto block = localBlockIndex->entries[indexIndex].block; - if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst++ = std::move(el); - el.~T(); - ++index; - } - } - else { - MOODYCAMEL_TRY { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst = std::move(el); - ++itemFirst; - el.~T(); - ++index; - } - } - MOODYCAMEL_CATCH (...) { - // It's too late to revert the dequeue, but we can make sure that all - // the dequeued objects are properly destroyed and the block index - // (and empty count) are properly updated before we propagate the exception - do { - block = localBlockIndex->entries[indexIndex].block; - while (index != endIndex) { - (*block)[index++]->~T(); - } - block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); - indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); - - firstIndexInBlock = index; - endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - } while (index != firstIndex + actualCount); - - MOODYCAMEL_RETHROW; - } - } - block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); - indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); - } while (index != firstIndex + actualCount); - - return actualCount; - } - else { - // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent - this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); - } - } - - return 0; - } - - private: - struct BlockIndexEntry - { - index_t base; - Block* block; - }; - - struct BlockIndexHeader - { - size_t size; - std::atomic front; // Current slot (not next, like pr_blockIndexFront) - BlockIndexEntry* entries; - void* prev; - }; - - - bool new_block_index(size_t numberOfFilledSlotsToExpose) - { - auto prevBlockSizeMask = pr_blockIndexSize - 1; - - // Create the new block - pr_blockIndexSize <<= 1; - auto newRawPtr = static_cast((Traits::malloc)(sizeof(BlockIndexHeader) + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * pr_blockIndexSize)); - if (newRawPtr == nullptr) { - pr_blockIndexSize >>= 1; // Reset to allow graceful retry - return false; - } - - auto newBlockIndexEntries = reinterpret_cast(details::align_for(newRawPtr + sizeof(BlockIndexHeader))); - - // Copy in all the old indices, if any - size_t j = 0; - if (pr_blockIndexSlotsUsed != 0) { - auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask; - do { - newBlockIndexEntries[j++] = pr_blockIndexEntries[i]; - i = (i + 1) & prevBlockSizeMask; - } while (i != pr_blockIndexFront); - } - - // Update everything - auto header = new (newRawPtr) BlockIndexHeader; - header->size = pr_blockIndexSize; - header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed); - header->entries = newBlockIndexEntries; - header->prev = pr_blockIndexRaw; // we link the new block to the old one so we can free it later - - pr_blockIndexFront = j; - pr_blockIndexEntries = newBlockIndexEntries; - pr_blockIndexRaw = newRawPtr; - blockIndex.store(header, std::memory_order_release); - - return true; - } - - private: - std::atomic blockIndex; - - // To be used by producer only -- consumer must use the ones in referenced by blockIndex - size_t pr_blockIndexSlotsUsed; - size_t pr_blockIndexSize; - size_t pr_blockIndexFront; // Next slot (not current) - BlockIndexEntry* pr_blockIndexEntries; - void* pr_blockIndexRaw; - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - public: - ExplicitProducer* nextExplicitProducer; - private: -#endif - -#if MCDBGQ_TRACKMEM - friend struct MemStats; -#endif - }; - - - ////////////////////////////////// - // Implicit queue - ////////////////////////////////// - - struct ImplicitProducer : public ProducerBase - { - ImplicitProducer(ConcurrentQueue* parent) : - ProducerBase(parent, false), - nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE), - blockIndex(nullptr) - { - new_block_index(); - } - - ~ImplicitProducer() - { - // Note that since we're in the destructor we can assume that all enqueue/dequeue operations - // completed already; this means that all undequeued elements are placed contiguously across - // contiguous blocks, and that only the first and last remaining blocks can be only partially - // empty (all other remaining blocks must be completely full). - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - // Unregister ourselves for thread termination notification - if (!this->inactive.load(std::memory_order_relaxed)) { - details::ThreadExitNotifier::unsubscribe(&threadExitListener); - } -#endif - - // Destroy all remaining elements! - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto index = this->headIndex.load(std::memory_order_relaxed); - Block* block = nullptr; - assert(index == tail || details::circular_less_than(index, tail)); - bool forceFreeLastBlock = index != tail; // If we enter the loop, then the last (tail) block will not be freed - while (index != tail) { - if ((index & static_cast(BLOCK_SIZE - 1)) == 0 || block == nullptr) { - if (block != nullptr) { - // Free the old block - this->parent->add_block_to_free_list(block); - } - - block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed); - } - - ((*block)[index])->~T(); - ++index; - } - // Even if the queue is empty, there's still one block that's not on the free list - // (unless the head index reached the end of it, in which case the tail will be poised - // to create a new block). - if (this->tailBlock != nullptr && (forceFreeLastBlock || (tail & static_cast(BLOCK_SIZE - 1)) != 0)) { - this->parent->add_block_to_free_list(this->tailBlock); - } - - // Destroy block index - auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); - if (localBlockIndex != nullptr) { - for (size_t i = 0; i != localBlockIndex->capacity; ++i) { - localBlockIndex->index[i]->~BlockIndexEntry(); - } - do { - auto prev = localBlockIndex->prev; - localBlockIndex->~BlockIndexHeader(); - (Traits::free)(localBlockIndex); - localBlockIndex = prev; - } while (localBlockIndex != nullptr); - } - } - - template - inline bool enqueue(U&& element) - { - index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); - index_t newTailIndex = 1 + currentTailIndex; - if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - // We reached the end of a block, start a new one - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { - return false; - } -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - // Find out where we'll be inserting this block in the block index - BlockIndexEntry* idxEntry; - if (!insert_block_index_entry(idxEntry, currentTailIndex)) { - return false; - } - - // Get ahold of a new block - auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); - if (newBlock == nullptr) { - rewind_block_index_tail(); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - return false; - } -#if MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template reset_empty(); - - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { - // May throw, try to insert now before we publish the fact that we have this new block - MOODYCAMEL_TRY { - new ((*newBlock)[currentTailIndex]) T(std::forward(element)); - } - MOODYCAMEL_CATCH (...) { - rewind_block_index_tail(); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - this->parent->add_block_to_free_list(newBlock); - MOODYCAMEL_RETHROW; - } - } - - // Insert the new block into the index - idxEntry->value.store(newBlock, std::memory_order_relaxed); - - this->tailBlock = newBlock; - - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - } - - // Enqueue - new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); - - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - bool dequeue(U& element) - { - // See ExplicitProducer::dequeue for rationale and explanation - index_t tail = this->tailIndex.load(std::memory_order_relaxed); - index_t overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { - std::atomic_thread_fence(std::memory_order_acquire); - - index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); - assert(overcommit <= myDequeueCount); - tail = this->tailIndex.load(std::memory_order_acquire); - if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { - index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); - - // Determine which block the element is in - auto entry = get_block_index_entry_for_index(index); - - // Dequeue - auto block = entry->value.load(std::memory_order_relaxed); - auto& el = *((*block)[index]); - - if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - // Note: Acquiring the mutex with every dequeue instead of only when a block - // is released is very sub-optimal, but it is, after all, purely debug code. - debug::DebugLock lock(producer->mutex); -#endif - struct Guard { - Block* block; - index_t index; - BlockIndexEntry* entry; - ConcurrentQueue* parent; - - ~Guard() - { - (*block)[index]->~T(); - if (block->ConcurrentQueue::Block::template set_empty(index)) { - entry->value.store(nullptr, std::memory_order_relaxed); - parent->add_block_to_free_list(block); - } - } - } guard = { block, index, entry, this->parent }; - - element = std::move(el); - } - else { - element = std::move(el); - el.~T(); - - if (block->ConcurrentQueue::Block::template set_empty(index)) { - { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - // Add the block back into the global free pool (and remove from block index) - entry->value.store(nullptr, std::memory_order_relaxed); - } - this->parent->add_block_to_free_list(block); // releases the above store - } - } - - return true; - } - else { - this->dequeueOvercommit.fetch_add(1, std::memory_order_release); - } - } - - return false; - } - - template - bool enqueue_bulk(It itemFirst, size_t count) - { - // First, we need to make sure we have enough room to enqueue all of the elements; - // this means pre-allocating blocks and putting them in the block index (but only if - // all the allocations succeeded). - - // Note that the tailBlock we start off with may not be owned by us any more; - // this happens if it was filled up exactly to the top (setting tailIndex to - // the first index of the next block which is not yet allocated), then dequeued - // completely (putting it on the free list) before we enqueue again. - - index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); - auto startBlock = this->tailBlock; - Block* firstAllocatedBlock = nullptr; - auto endBlock = this->tailBlock; - - // Figure out how many blocks we'll need to allocate, and do so - size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); - index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - if (blockBaseDiff > 0) { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - do { - blockBaseDiff -= static_cast(BLOCK_SIZE); - currentTailIndex += static_cast(BLOCK_SIZE); - - // Find out where we'll be inserting this block in the block index - BlockIndexEntry* idxEntry = nullptr; // initialization here unnecessary but compiler can't always tell - Block* newBlock; - bool indexInserted = false; - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); - if (full || !(indexInserted = insert_block_index_entry(idxEntry, currentTailIndex)) || (newBlock = this->parent->ConcurrentQueue::template requisition_block()) == nullptr) { - // Index allocation or block allocation failed; revert any other allocations - // and index insertions done so far for this operation - if (indexInserted) { - rewind_block_index_tail(); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - } - currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { - currentTailIndex += static_cast(BLOCK_SIZE); - idxEntry = get_block_index_entry_for_index(currentTailIndex); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - rewind_block_index_tail(); - } - this->parent->add_blocks_to_free_list(firstAllocatedBlock); - this->tailBlock = startBlock; - - return false; - } - -#if MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template reset_empty(); - newBlock->next = nullptr; - - // Insert the new block into the index - idxEntry->value.store(newBlock, std::memory_order_relaxed); - - // Store the chain of blocks so that we can undo if later allocations fail, - // and so that we can find the blocks when we do the actual enqueueing - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr) { - assert(this->tailBlock != nullptr); - this->tailBlock->next = newBlock; - } - this->tailBlock = newBlock; - endBlock = newBlock; - firstAllocatedBlock = firstAllocatedBlock == nullptr ? newBlock : firstAllocatedBlock; - } while (blockBaseDiff > 0); - } - - // Enqueue, one block at a time - index_t newTailIndex = startTailIndex + static_cast(count); - currentTailIndex = startTailIndex; - this->tailBlock = startBlock; - assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { - this->tailBlock = firstAllocatedBlock; - } - while (true) { - auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(newTailIndex, stopIndex)) { - stopIndex = newTailIndex; - } - if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { - while (currentTailIndex != stopIndex) { - new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); - } - } - else { - MOODYCAMEL_TRY { - while (currentTailIndex != stopIndex) { - new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); - ++currentTailIndex; - ++itemFirst; - } - } - MOODYCAMEL_CATCH (...) { - auto constructedStopIndex = currentTailIndex; - auto lastBlockEnqueued = this->tailBlock; - - if (!details::is_trivially_destructible::value) { - auto block = startBlock; - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - block = firstAllocatedBlock; - } - currentTailIndex = startTailIndex; - while (true) { - stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(constructedStopIndex, stopIndex)) { - stopIndex = constructedStopIndex; - } - while (currentTailIndex != stopIndex) { - (*block)[currentTailIndex++]->~T(); - } - if (block == lastBlockEnqueued) { - break; - } - block = block->next; - } - } - - currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { - currentTailIndex += static_cast(BLOCK_SIZE); - auto idxEntry = get_block_index_entry_for_index(currentTailIndex); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - rewind_block_index_tail(); - } - this->parent->add_blocks_to_free_list(firstAllocatedBlock); - this->tailBlock = startBlock; - MOODYCAMEL_RETHROW; - } - } - - if (this->tailBlock == endBlock) { - assert(currentTailIndex == newTailIndex); - break; - } - this->tailBlock = this->tailBlock->next; - } - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - size_t dequeue_bulk(It& itemFirst, size_t max) - { - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); - if (details::circular_less_than(0, desiredCount)) { - desiredCount = desiredCount < max ? desiredCount : max; - std::atomic_thread_fence(std::memory_order_acquire); - - auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); - assert(overcommit <= myDequeueCount); - - tail = this->tailIndex.load(std::memory_order_acquire); - auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); - if (details::circular_less_than(0, actualCount)) { - actualCount = desiredCount < actualCount ? desiredCount : actualCount; - if (actualCount < desiredCount) { - this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); - } - - // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this - // will never exceed tail. - auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); - - // Iterate the blocks and dequeue - auto index = firstIndex; - BlockIndexHeader* localBlockIndex; - auto indexIndex = get_block_index_index_for_index(index, localBlockIndex); - do { - auto blockStartIndex = index; - auto endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - - auto entry = localBlockIndex->index[indexIndex]; - auto block = entry->value.load(std::memory_order_relaxed); - if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst++ = std::move(el); - el.~T(); - ++index; - } - } - else { - MOODYCAMEL_TRY { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst = std::move(el); - ++itemFirst; - el.~T(); - ++index; - } - } - MOODYCAMEL_CATCH (...) { - do { - entry = localBlockIndex->index[indexIndex]; - block = entry->value.load(std::memory_order_relaxed); - while (index != endIndex) { - (*block)[index++]->~T(); - } - - if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - entry->value.store(nullptr, std::memory_order_relaxed); - this->parent->add_block_to_free_list(block); - } - indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); - - blockStartIndex = index; - endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - } while (index != firstIndex + actualCount); - - MOODYCAMEL_RETHROW; - } - } - if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { - { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - // Note that the set_many_empty above did a release, meaning that anybody who acquires the block - // we're about to free can use it safely since our writes (and reads!) will have happened-before then. - entry->value.store(nullptr, std::memory_order_relaxed); - } - this->parent->add_block_to_free_list(block); // releases the above store - } - indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); - } while (index != firstIndex + actualCount); - - return actualCount; - } - else { - this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); - } - } - - return 0; - } - - private: - // The block size must be > 1, so any number with the low bit set is an invalid block base index - static const index_t INVALID_BLOCK_BASE = 1; - - struct BlockIndexEntry - { - std::atomic key; - std::atomic value; - }; - - struct BlockIndexHeader - { - size_t capacity; - std::atomic tail; - BlockIndexEntry* entries; - BlockIndexEntry** index; - BlockIndexHeader* prev; - }; - - template - inline bool insert_block_index_entry(BlockIndexEntry*& idxEntry, index_t blockStartIndex) - { - auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); // We're the only writer thread, relaxed is OK - if (localBlockIndex == nullptr) { - return false; // this can happen if new_block_index failed in the constructor - } - auto newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); - idxEntry = localBlockIndex->index[newTail]; - if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE || - idxEntry->value.load(std::memory_order_relaxed) == nullptr) { - - idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); - localBlockIndex->tail.store(newTail, std::memory_order_release); - return true; - } - - // No room in the old block index, try to allocate another one! - if (allocMode == CannotAlloc || !new_block_index()) { - return false; - } - localBlockIndex = blockIndex.load(std::memory_order_relaxed); - newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); - idxEntry = localBlockIndex->index[newTail]; - assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE); - idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); - localBlockIndex->tail.store(newTail, std::memory_order_release); - return true; - } - - inline void rewind_block_index_tail() - { - auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); - localBlockIndex->tail.store((localBlockIndex->tail.load(std::memory_order_relaxed) - 1) & (localBlockIndex->capacity - 1), std::memory_order_relaxed); - } - - inline BlockIndexEntry* get_block_index_entry_for_index(index_t index) const - { - BlockIndexHeader* localBlockIndex; - auto idx = get_block_index_index_for_index(index, localBlockIndex); - return localBlockIndex->index[idx]; - } - - inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const - { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - index &= ~static_cast(BLOCK_SIZE - 1); - localBlockIndex = blockIndex.load(std::memory_order_acquire); - auto tail = localBlockIndex->tail.load(std::memory_order_acquire); - auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed); - assert(tailBase != INVALID_BLOCK_BASE); - // Note: Must use division instead of shift because the index may wrap around, causing a negative - // offset, whose negativity we want to preserve - auto offset = static_cast(static_cast::type>(index - tailBase) / BLOCK_SIZE); - size_t idx = (tail + offset) & (localBlockIndex->capacity - 1); - assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr); - return idx; - } - - bool new_block_index() - { - auto prev = blockIndex.load(std::memory_order_relaxed); - size_t prevCapacity = prev == nullptr ? 0 : prev->capacity; - auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity; - auto raw = static_cast((Traits::malloc)( - sizeof(BlockIndexHeader) + - std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * entryCount + - std::alignment_of::value - 1 + sizeof(BlockIndexEntry*) * nextBlockIndexCapacity)); - if (raw == nullptr) { - return false; - } - - auto header = new (raw) BlockIndexHeader; - auto entries = reinterpret_cast(details::align_for(raw + sizeof(BlockIndexHeader))); - auto index = reinterpret_cast(details::align_for(reinterpret_cast(entries) + sizeof(BlockIndexEntry) * entryCount)); - if (prev != nullptr) { - auto prevTail = prev->tail.load(std::memory_order_relaxed); - auto prevPos = prevTail; - size_t i = 0; - do { - prevPos = (prevPos + 1) & (prev->capacity - 1); - index[i++] = prev->index[prevPos]; - } while (prevPos != prevTail); - assert(i == prevCapacity); - } - for (size_t i = 0; i != entryCount; ++i) { - new (entries + i) BlockIndexEntry; - entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed); - index[prevCapacity + i] = entries + i; - } - header->prev = prev; - header->entries = entries; - header->index = index; - header->capacity = nextBlockIndexCapacity; - header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed); - - blockIndex.store(header, std::memory_order_release); - - nextBlockIndexCapacity <<= 1; - - return true; - } - - private: - size_t nextBlockIndexCapacity; - std::atomic blockIndex; - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - public: - details::ThreadExitListener threadExitListener; - private: -#endif - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - public: - ImplicitProducer* nextImplicitProducer; - private: -#endif - -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - mutable debug::DebugMutex mutex; -#endif -#if MCDBGQ_TRACKMEM - friend struct MemStats; -#endif - }; - - - ////////////////////////////////// - // Block pool manipulation - ////////////////////////////////// - - void populate_initial_block_list(size_t blockCount) - { - initialBlockPoolSize = blockCount; - if (initialBlockPoolSize == 0) { - initialBlockPool = nullptr; - return; - } - - initialBlockPool = create_array(blockCount); - if (initialBlockPool == nullptr) { - initialBlockPoolSize = 0; - } - for (size_t i = 0; i < initialBlockPoolSize; ++i) { - initialBlockPool[i].dynamicallyAllocated = false; - } - } - - inline Block* try_get_block_from_initial_pool() - { - if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) { - return nullptr; - } - - auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed); - - return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr; - } - - inline void add_block_to_free_list(Block* block) - { -#if MCDBGQ_TRACKMEM - block->owner = nullptr; -#endif - freeList.add(block); - } - - inline void add_blocks_to_free_list(Block* block) - { - while (block != nullptr) { - auto next = block->next; - add_block_to_free_list(block); - block = next; - } - } - - inline Block* try_get_block_from_free_list() - { - return freeList.try_get(); - } - - // Gets a free block from one of the memory pools, or allocates a new one (if applicable) - template - Block* requisition_block() - { - auto block = try_get_block_from_initial_pool(); - if (block != nullptr) { - return block; - } - - block = try_get_block_from_free_list(); - if (block != nullptr) { - return block; - } - - if (canAlloc == CanAlloc) { - return create(); - } - - return nullptr; - } - - -#if MCDBGQ_TRACKMEM - public: - struct MemStats { - size_t allocatedBlocks; - size_t usedBlocks; - size_t freeBlocks; - size_t ownedBlocksExplicit; - size_t ownedBlocksImplicit; - size_t implicitProducers; - size_t explicitProducers; - size_t elementsEnqueued; - size_t blockClassBytes; - size_t queueClassBytes; - size_t implicitBlockIndexBytes; - size_t explicitBlockIndexBytes; - - friend class ConcurrentQueue; - - private: - static MemStats getFor(ConcurrentQueue* q) - { - MemStats stats = { 0 }; - - stats.elementsEnqueued = q->size_approx(); - - auto block = q->freeList.head_unsafe(); - while (block != nullptr) { - ++stats.allocatedBlocks; - ++stats.freeBlocks; - block = block->freeListNext.load(std::memory_order_relaxed); - } - - for (auto ptr = q->producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - bool implicit = dynamic_cast(ptr) != nullptr; - stats.implicitProducers += implicit ? 1 : 0; - stats.explicitProducers += implicit ? 0 : 1; - - if (implicit) { - auto prod = static_cast(ptr); - stats.queueClassBytes += sizeof(ImplicitProducer); - auto head = prod->headIndex.load(std::memory_order_relaxed); - auto tail = prod->tailIndex.load(std::memory_order_relaxed); - auto hash = prod->blockIndex.load(std::memory_order_relaxed); - if (hash != nullptr) { - for (size_t i = 0; i != hash->capacity; ++i) { - if (hash->index[i]->key.load(std::memory_order_relaxed) != ImplicitProducer::INVALID_BLOCK_BASE && hash->index[i]->value.load(std::memory_order_relaxed) != nullptr) { - ++stats.allocatedBlocks; - ++stats.ownedBlocksImplicit; - } - } - stats.implicitBlockIndexBytes += hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry); - for (; hash != nullptr; hash = hash->prev) { - stats.implicitBlockIndexBytes += sizeof(typename ImplicitProducer::BlockIndexHeader) + hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry*); - } - } - for (; details::circular_less_than(head, tail); head += BLOCK_SIZE) { - //auto block = prod->get_block_index_entry_for_index(head); - ++stats.usedBlocks; - } - } - else { - auto prod = static_cast(ptr); - stats.queueClassBytes += sizeof(ExplicitProducer); - auto tailBlock = prod->tailBlock; - bool wasNonEmpty = false; - if (tailBlock != nullptr) { - auto block = tailBlock; - do { - ++stats.allocatedBlocks; - if (!block->ConcurrentQueue::Block::template is_empty() || wasNonEmpty) { - ++stats.usedBlocks; - wasNonEmpty = wasNonEmpty || block != tailBlock; - } - ++stats.ownedBlocksExplicit; - block = block->next; - } while (block != tailBlock); - } - auto index = prod->blockIndex.load(std::memory_order_relaxed); - while (index != nullptr) { - stats.explicitBlockIndexBytes += sizeof(typename ExplicitProducer::BlockIndexHeader) + index->size * sizeof(typename ExplicitProducer::BlockIndexEntry); - index = static_cast(index->prev); - } - } - } - - auto freeOnInitialPool = q->initialBlockPoolIndex.load(std::memory_order_relaxed) >= q->initialBlockPoolSize ? 0 : q->initialBlockPoolSize - q->initialBlockPoolIndex.load(std::memory_order_relaxed); - stats.allocatedBlocks += freeOnInitialPool; - stats.freeBlocks += freeOnInitialPool; - - stats.blockClassBytes = sizeof(Block) * stats.allocatedBlocks; - stats.queueClassBytes += sizeof(ConcurrentQueue); - - return stats; - } - }; - - // For debugging only. Not thread-safe. - MemStats getMemStats() - { - return MemStats::getFor(this); - } - private: - friend struct MemStats; -#endif - - - ////////////////////////////////// - // Producer list manipulation - ////////////////////////////////// - - ProducerBase* recycle_or_create_producer(bool isExplicit) - { - bool recycled; - return recycle_or_create_producer(isExplicit, recycled); - } - - ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled) - { -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugLock lock(implicitProdMutex); -#endif - // Try to re-use one first - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) { - bool expected = true; - if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) { - // We caught one! It's been marked as activated, the caller can have it - recycled = true; - return ptr; - } - } - } - - recycled = false; - return add_producer(isExplicit ? static_cast(create(this)) : create(this)); - } - - ProducerBase* add_producer(ProducerBase* producer) - { - // Handle failed memory allocation - if (producer == nullptr) { - return nullptr; - } - - producerCount.fetch_add(1, std::memory_order_relaxed); - - // Add it to the lock-free list - auto prevTail = producerListTail.load(std::memory_order_relaxed); - do { - producer->next = prevTail; - } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed)); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - if (producer->isExplicit) { - auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed); - do { - static_cast(producer)->nextExplicitProducer = prevTailExplicit; - } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); - } - else { - auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed); - do { - static_cast(producer)->nextImplicitProducer = prevTailImplicit; - } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); - } -#endif - - return producer; - } - - void reown_producers() - { - // After another instance is moved-into/swapped-with this one, all the - // producers we stole still think their parents are the other queue. - // So fix them up! - for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) { - ptr->parent = this; - } - } - - - ////////////////////////////////// - // Implicit producer hash - ////////////////////////////////// - - struct ImplicitProducerKVP - { - std::atomic key; - ImplicitProducer* value; // No need for atomicity since it's only read by the thread that sets it in the first place - - ImplicitProducerKVP() : value(nullptr) { } - - ImplicitProducerKVP(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT - { - key.store(other.key.load(std::memory_order_relaxed), std::memory_order_relaxed); - value = other.value; - } - - inline ImplicitProducerKVP& operator=(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT - { - swap(other); - return *this; - } - - inline void swap(ImplicitProducerKVP& other) MOODYCAMEL_NOEXCEPT - { - if (this != &other) { - details::swap_relaxed(key, other.key); - std::swap(value, other.value); - } - } - }; - - template - friend void moodycamel::swap(typename ConcurrentQueue::ImplicitProducerKVP&, typename ConcurrentQueue::ImplicitProducerKVP&) MOODYCAMEL_NOEXCEPT; - - struct ImplicitProducerHash - { - size_t capacity; - ImplicitProducerKVP* entries; - ImplicitProducerHash* prev; - }; - - inline void populate_initial_implicit_producer_hash() - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return; - - implicitProducerHashCount.store(0, std::memory_order_relaxed); - auto hash = &initialImplicitProducerHash; - hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; - hash->entries = &initialImplicitProducerHashEntries[0]; - for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) { - initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); - } - hash->prev = nullptr; - implicitProducerHash.store(hash, std::memory_order_relaxed); - } - - void swap_implicit_producer_hashes(ConcurrentQueue& other) - { - if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return; - - // Swap (assumes our implicit producer hash is initialized) - initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries); - initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0]; - other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0]; - - details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount); - - details::swap_relaxed(implicitProducerHash, other.implicitProducerHash); - if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) { - implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed); - } - else { - ImplicitProducerHash* hash; - for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) { - continue; - } - hash->prev = &initialImplicitProducerHash; - } - if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) { - other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed); - } - else { - ImplicitProducerHash* hash; - for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) { - continue; - } - hash->prev = &other.initialImplicitProducerHash; - } - } - - // Only fails (returns nullptr) if memory allocation fails - ImplicitProducer* get_or_add_implicit_producer() - { - // Note that since the data is essentially thread-local (key is thread ID), - // there's a reduced need for fences (memory ordering is already consistent - // for any individual thread), except for the current table itself. - - // Start by looking for the thread ID in the current and all previous hash tables. - // If it's not found, it must not be in there yet, since this same thread would - // have added it previously to one of the tables that we traversed. - - // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table - -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugLock lock(implicitProdMutex); -#endif - - auto id = details::thread_id(); - auto hashedId = details::hash_thread_id(id); - - auto mainHash = implicitProducerHash.load(std::memory_order_acquire); - for (auto hash = mainHash; hash != nullptr; hash = hash->prev) { - // Look for the id in this hash - auto index = hashedId; - while (true) { // Not an infinite loop because at least one slot is free in the hash table - index &= hash->capacity - 1; - - auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed); - if (probedKey == id) { - // Found it! If we had to search several hashes deep, though, we should lazily add it - // to the current main hash table to avoid the extended search next time. - // Note there's guaranteed to be room in the current hash table since every subsequent - // table implicitly reserves space for all previous tables (there's only one - // implicitProducerHashCount). - auto value = hash->entries[index].value; - if (hash != mainHash) { - index = hashedId; - while (true) { - index &= mainHash->capacity - 1; - probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); - auto empty = details::invalid_thread_id; -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - auto reusable = details::invalid_thread_id2; - if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) || - (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) { -#else - if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed))) { -#endif - mainHash->entries[index].value = value; - break; - } - ++index; - } - } - - return value; - } - if (probedKey == details::invalid_thread_id) { - break; // Not in this hash table - } - ++index; - } - } - - // Insert! - auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed); - while (true) { - if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) { - // We've acquired the resize lock, try to allocate a bigger hash table. - // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when - // we reload implicitProducerHash it must be the most recent version (it only gets changed within this - // locked block). - mainHash = implicitProducerHash.load(std::memory_order_acquire); - if (newCount >= (mainHash->capacity >> 1)) { - auto newCapacity = mainHash->capacity << 1; - while (newCount >= (newCapacity >> 1)) { - newCapacity <<= 1; - } - auto raw = static_cast((Traits::malloc)(sizeof(ImplicitProducerHash) + std::alignment_of::value - 1 + sizeof(ImplicitProducerKVP) * newCapacity)); - if (raw == nullptr) { - // Allocation failed - implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - return nullptr; - } - - auto newHash = new (raw) ImplicitProducerHash; - newHash->capacity = newCapacity; - newHash->entries = reinterpret_cast(details::align_for(raw + sizeof(ImplicitProducerHash))); - for (size_t i = 0; i != newCapacity; ++i) { - new (newHash->entries + i) ImplicitProducerKVP; - newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); - } - newHash->prev = mainHash; - implicitProducerHash.store(newHash, std::memory_order_release); - implicitProducerHashResizeInProgress.clear(std::memory_order_release); - mainHash = newHash; - } - else { - implicitProducerHashResizeInProgress.clear(std::memory_order_release); - } - } - - // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table - // to finish being allocated by another thread (and if we just finished allocating above, the condition will - // always be true) - if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) { - bool recycled; - auto producer = static_cast(recycle_or_create_producer(false, recycled)); - if (producer == nullptr) { - implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); - return nullptr; - } - if (recycled) { - implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); - } - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback; - producer->threadExitListener.userData = producer; - details::ThreadExitNotifier::subscribe(&producer->threadExitListener); -#endif - - auto index = hashedId; - while (true) { - index &= mainHash->capacity - 1; - auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); - - auto empty = details::invalid_thread_id; -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - auto reusable = details::invalid_thread_id2; - if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) || - (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) { -#else - if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed))) { -#endif - mainHash->entries[index].value = producer; - break; - } - ++index; - } - return producer; - } - - // Hmm, the old hash is quite full and somebody else is busy allocating a new one. - // We need to wait for the allocating thread to finish (if it succeeds, we add, if not, - // we try to allocate ourselves). - mainHash = implicitProducerHash.load(std::memory_order_acquire); - } - } - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - void implicit_producer_thread_exited(ImplicitProducer* producer) - { - // Remove from thread exit listeners - details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener); - - // Remove from hash -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugLock lock(implicitProdMutex); -#endif - auto hash = implicitProducerHash.load(std::memory_order_acquire); - assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place - auto id = details::thread_id(); - auto hashedId = details::hash_thread_id(id); - details::thread_id_t probedKey; - - // We need to traverse all the hashes just in case other threads aren't on the current one yet and are - // trying to add an entry thinking there's a free slot (because they reused a producer) - for (; hash != nullptr; hash = hash->prev) { - auto index = hashedId; - do { - index &= hash->capacity - 1; - probedKey = hash->entries[index].key.load(std::memory_order_relaxed); - if (probedKey == id) { - hash->entries[index].key.store(details::invalid_thread_id2, std::memory_order_release); - break; - } - ++index; - } while (probedKey != details::invalid_thread_id); // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place - } - - // Mark the queue as being recyclable - producer->inactive.store(true, std::memory_order_release); - } - - static void implicit_producer_thread_exited_callback(void* userData) - { - auto producer = static_cast(userData); - auto queue = producer->parent; - queue->implicit_producer_thread_exited(producer); - } -#endif - - ////////////////////////////////// - // Utility functions - ////////////////////////////////// - - template - static inline U* create_array(size_t count) - { - assert(count > 0); - auto p = static_cast((Traits::malloc)(sizeof(U) * count)); - if (p == nullptr) { - return nullptr; - } - - for (size_t i = 0; i != count; ++i) { - new (p + i) U(); - } - return p; - } - - template - static inline void destroy_array(U* p, size_t count) - { - if (p != nullptr) { - assert(count > 0); - for (size_t i = count; i != 0; ) { - (p + --i)->~U(); - } - (Traits::free)(p); - } - } - - template - static inline U* create() - { - auto p = (Traits::malloc)(sizeof(U)); - return p != nullptr ? new (p) U : nullptr; - } - - template - static inline U* create(A1&& a1) - { - auto p = (Traits::malloc)(sizeof(U)); - return p != nullptr ? new (p) U(std::forward(a1)) : nullptr; - } - - template - static inline void destroy(U* p) - { - if (p != nullptr) { - p->~U(); - } - (Traits::free)(p); - } - -private: - std::atomic producerListTail; - std::atomic producerCount; - - std::atomic initialBlockPoolIndex; - Block* initialBlockPool; - size_t initialBlockPoolSize; - -#if !MCDBGQ_USEDEBUGFREELIST - FreeList freeList; -#else - debug::DebugFreeList freeList; -#endif - - std::atomic implicitProducerHash; - std::atomic implicitProducerHashCount; // Number of slots logically used - ImplicitProducerHash initialImplicitProducerHash; - std::array initialImplicitProducerHashEntries; - std::atomic_flag implicitProducerHashResizeInProgress; - - std::atomic nextExplicitConsumerId; - std::atomic globalExplicitConsumerOffset; - -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugMutex implicitProdMutex; -#endif - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - std::atomic explicitProducers; - std::atomic implicitProducers; -#endif -}; - - -template -ProducerToken::ProducerToken(ConcurrentQueue& queue) - : producer(queue.recycle_or_create_producer(true)) -{ - if (producer != nullptr) { - producer->token = this; - } -} - -template -ProducerToken::ProducerToken(BlockingConcurrentQueue& queue) - : producer(reinterpret_cast*>(&queue)->recycle_or_create_producer(true)) -{ - if (producer != nullptr) { - producer->token = this; - } -} - -template -ConsumerToken::ConsumerToken(ConcurrentQueue& queue) - : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) -{ - initialOffset = queue.nextExplicitConsumerId.fetch_add(1, std::memory_order_release); - lastKnownGlobalOffset = -1; -} - -template -ConsumerToken::ConsumerToken(BlockingConcurrentQueue& queue) - : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) -{ - initialOffset = reinterpret_cast*>(&queue)->nextExplicitConsumerId.fetch_add(1, std::memory_order_release); - lastKnownGlobalOffset = -1; -} - -template -inline void swap(ConcurrentQueue& a, ConcurrentQueue& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -inline void swap(ProducerToken& a, ProducerToken& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -inline void swap(ConsumerToken& a, ConsumerToken& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -template -inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -} - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/c_src/enlfq/enlfq.cc b/c_src/enlfq/enlfq.cc deleted file mode 100644 index 4ccc7a9..0000000 --- a/c_src/enlfq/enlfq.cc +++ /dev/null @@ -1,84 +0,0 @@ -#include "enlfq.h" -#include "enlfq_nif.h" - -#include "nif_utils.h" - -#include "concurrentqueue.h" - - -struct q_item { - ErlNifEnv *env; - ERL_NIF_TERM term; -}; - -struct squeue { - moodycamel::ConcurrentQueue *queue; -}; - - -void nif_enlfq_free(ErlNifEnv *, void *obj) { - squeue *inst = static_cast(obj); - - if (inst != nullptr) { - q_item item; - while (inst->queue->try_dequeue(item)) { - enif_free_env(item.env); - } - delete inst->queue; - } -} - -ERL_NIF_TERM nif_enlfq_new(ErlNifEnv *env, int, const ERL_NIF_TERM *) { - shared_data *data = static_cast(enif_priv_data(env)); - - - squeue *qinst = static_cast(enif_alloc_resource(data->resQueueInstance, sizeof(squeue))); - qinst->queue = new moodycamel::ConcurrentQueue; - - if (qinst == NULL) - return make_error(env, "enif_alloc_resource failed"); - - ERL_NIF_TERM term = enif_make_resource(env, qinst); - enif_release_resource(qinst); - return enif_make_tuple2(env, ATOMS.atomOk, term); -} - -ERL_NIF_TERM nif_enlfq_push(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]) { - shared_data *data = static_cast(enif_priv_data(env)); - - squeue *inst; - - if (!enif_get_resource(env, argv[0], data->resQueueInstance, (void **) &inst)) { - return enif_make_badarg(env); - } - - q_item item; - - item.env = enif_alloc_env(); - item.term = enif_make_copy(item.env, argv[1]); - - inst->queue->enqueue(item); - - return ATOMS.atomTrue; -} - -ERL_NIF_TERM nif_enlfq_pop(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]) { - shared_data *data = static_cast(enif_priv_data(env)); - squeue *inst = NULL; - - if (!enif_get_resource(env, argv[0], data->resQueueInstance, (void **) &inst)) { - return enif_make_badarg(env); - } - - ERL_NIF_TERM term; - q_item item; - - if (inst->queue->try_dequeue(item)) { - term = enif_make_copy(env, item.term); - enif_free_env(item.env); - return enif_make_tuple2(env, ATOMS.atomOk, term); - } else { - return ATOMS.atomEmpty; - } - -} diff --git a/c_src/enlfq/enlfq.h b/c_src/enlfq/enlfq.h deleted file mode 100644 index 08b2ca4..0000000 --- a/c_src/enlfq/enlfq.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "erl_nif.h" - -extern "C" { -void nif_enlfq_free(ErlNifEnv *env, void *obj); -ERL_NIF_TERM nif_enlfq_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -ERL_NIF_TERM nif_enlfq_push(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -ERL_NIF_TERM nif_enlfq_pop(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -} \ No newline at end of file diff --git a/c_src/enlfq/enlfq_nif.cc b/c_src/enlfq/enlfq_nif.cc deleted file mode 100644 index cf5c4f2..0000000 --- a/c_src/enlfq/enlfq_nif.cc +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include "enlfq_nif.h" -#include "enlfq.h" -#include "nif_utils.h" - -const char kAtomOk[] = "ok"; -const char kAtomError[] = "error"; -const char kAtomTrue[] = "true"; -//const char kAtomFalse[] = "false"; -//const char kAtomUndefined[] = "undefined"; -const char kAtomEmpty[] = "empty"; - -atoms ATOMS; - -void open_resources(ErlNifEnv *env, shared_data *data) { - ErlNifResourceFlags flags = static_cast(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); - data->resQueueInstance = enif_open_resource_type(env, NULL, "enlfq_instance", nif_enlfq_free, flags, NULL); -} - -int on_nif_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM) { - - ATOMS.atomOk = make_atom(env, kAtomOk); - ATOMS.atomError = make_atom(env, kAtomError); - ATOMS.atomTrue = make_atom(env, kAtomTrue); -// ATOMS.atomFalse = make_atom(env, kAtomFalse); -// ATOMS.atomUndefined = make_atom(env, kAtomUndefined); - ATOMS.atomEmpty = make_atom(env, kAtomEmpty); - - shared_data *data = static_cast(enif_alloc(sizeof(shared_data))); - open_resources(env, data); - - *priv_data = data; - return 0; -} - -void on_nif_unload(ErlNifEnv *, void *priv_data) { - shared_data *data = static_cast(priv_data); - enif_free(data); -} - -int on_nif_upgrade(ErlNifEnv *env, void **priv, void **, ERL_NIF_TERM) { - shared_data *data = static_cast(enif_alloc(sizeof(shared_data))); - open_resources(env, data); - - *priv = data; - return 0; -} - -static ErlNifFunc nif_funcs[] = - { - {"new", 0, nif_enlfq_new}, - {"push", 2, nif_enlfq_push}, - {"pop", 1, nif_enlfq_pop} - }; - -ERL_NIF_INIT(enlfq, nif_funcs, on_nif_load, NULL, on_nif_upgrade, on_nif_unload) - diff --git a/c_src/enlfq/enlfq_nif.h b/c_src/enlfq/enlfq_nif.h deleted file mode 100644 index 88f7da5..0000000 --- a/c_src/enlfq/enlfq_nif.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "erl_nif.h" - -struct atoms -{ - ERL_NIF_TERM atomOk; - ERL_NIF_TERM atomError; - ERL_NIF_TERM atomTrue; -// ERL_NIF_TERM atomFalse; -// ERL_NIF_TERM atomUndefined; - ERL_NIF_TERM atomEmpty; -}; - -struct shared_data -{ - ErlNifResourceType* resQueueInstance; -}; - -extern atoms ATOMS; diff --git a/c_src/enlfq/nif_utils.cc b/c_src/enlfq/nif_utils.cc deleted file mode 100644 index a32e17d..0000000 --- a/c_src/enlfq/nif_utils.cc +++ /dev/null @@ -1,27 +0,0 @@ -#include "nif_utils.h" -#include "enlfq_nif.h" - -#include - -ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name) -{ - ERL_NIF_TERM ret; - - if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) - return ret; - - return enif_make_atom(env, name); -} - -ERL_NIF_TERM make_binary(ErlNifEnv* env, const char* buff, size_t length) -{ - ERL_NIF_TERM term; - unsigned char *destination_buffer = enif_make_new_binary(env, length, &term); - memcpy(destination_buffer, buff, length); - return term; -} - -ERL_NIF_TERM make_error(ErlNifEnv* env, const char* error) -{ - return enif_make_tuple2(env, ATOMS.atomError, make_binary(env, error, strlen(error))); -} diff --git a/c_src/enlfq/nif_utils.h b/c_src/enlfq/nif_utils.h deleted file mode 100644 index 3b0a929..0000000 --- a/c_src/enlfq/nif_utils.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "erl_nif.h" - -ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name); -ERL_NIF_TERM make_error(ErlNifEnv* env, const char* error); -ERL_NIF_TERM make_binary(ErlNifEnv* env, const char* buff, size_t length); \ No newline at end of file diff --git a/c_src/enlfq/rebar.config b/c_src/enlfq/rebar.config deleted file mode 100644 index da73819..0000000 --- a/c_src/enlfq/rebar.config +++ /dev/null @@ -1,7 +0,0 @@ -{port_specs, [ - {"../../priv/enlfq.so", ["*.cc"]} -]}. - - - - diff --git a/c_src/etsq/etsq.cpp b/c_src/etsq/etsq.cpp deleted file mode 100644 index 15e3c67..0000000 --- a/c_src/etsq/etsq.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "etsq.h" - -ErlNifRWLock *qinfo_map_rwlock; -QInfoMap qinfo_map; - -// Function finds the queue from map and returns QueueInfo -// Not thread safe. -QueueInfo* get_q_info(char* name) -{ - //std::cout<<"Info: "<< name<second; - } - return NULL; -} - -void new_q(char* name) -{ - //std::cout<<"Create: " << name<pmutex); - pqueue_info->queue.push(erl_term); - return true; - } - return false; -} - -// Returns new ErlTerm. Caller should delete it -ErlTerm* pop(char* name, bool read_only) -{ - QueueInfo *pqueue_info = NULL; - ReadLock read_lock(qinfo_map_rwlock); - if (NULL != (pqueue_info = get_q_info(name))) - { - Mutex mutex(pqueue_info->pmutex); - if (!pqueue_info->queue.empty()) - { - ErlTerm *erl_term = pqueue_info->queue.front(); - if(read_only) - { - return new ErlTerm(erl_term); - } - pqueue_info->queue.pop(); - return erl_term; - } - return new ErlTerm("empty"); - } - return NULL; -} - -static ERL_NIF_TERM new_queue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int size = 100; - char *name = new char(size); - enif_get_atom(env, argv[0], name, size, ERL_NIF_LATIN1); - { - QueueInfo *pqueue_info = NULL; - ReadLock read_lock(qinfo_map_rwlock); - if (NULL != (pqueue_info = get_q_info(name))) - { - return enif_make_error(env, "already_exists"); - } - } - new_q(name); - return enif_make_atom(env, "ok"); -} - -static ERL_NIF_TERM info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int size = 100; - char name[100]; - enif_get_atom(env, argv[0], name, size, ERL_NIF_LATIN1); - int queue_size = 0; - { - QueueInfo *pqueue_info = NULL; - ReadLock read_lock(qinfo_map_rwlock); - if (NULL == (pqueue_info = get_q_info(name))) - return enif_make_badarg(env); - queue_size = pqueue_info->queue.size(); - } - return enif_make_list2(env, - enif_make_tuple2(env, enif_make_atom(env, "name"), enif_make_atom(env, name)), - enif_make_tuple2(env, enif_make_atom(env, "size"), enif_make_int(env, queue_size))); -} - -static ERL_NIF_TERM push_back(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int size = 100; - char name[100]; - enif_get_atom(env, argv[0], name, size, ERL_NIF_LATIN1); - ErlTerm *erl_term = new ErlTerm(argv[1]); - if (push(name, erl_term)) - return enif_make_atom(env, "ok"); - delete erl_term; - return enif_make_badarg(env); -} - -static ERL_NIF_TERM pop_front(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int size = 100; - char name[100]; - enif_get_atom(env, argv[0], name, size, ERL_NIF_LATIN1); - ErlTerm *erl_term = NULL; - if (NULL == (erl_term = pop(name, false))) - return enif_make_badarg(env); - ERL_NIF_TERM return_term = enif_make_copy(env, erl_term->term); - delete erl_term; - return return_term; -} - -static ERL_NIF_TERM get_front(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int size = 100; - char name[100]; - enif_get_atom(env, argv[0], name, size, ERL_NIF_LATIN1); - ErlTerm *erl_term = NULL; - if (NULL == (erl_term = pop(name, true))) - return enif_make_badarg(env); - ERL_NIF_TERM return_term = enif_make_copy(env, erl_term->term); - delete erl_term; - return return_term; -} - -static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) -{ - int i; - return enif_get_int(env, load_info, &i) && i == 1; -} - -static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -{ - if (!is_ok_load_info(env, load_info)) - return -1; - qinfo_map_rwlock = enif_rwlock_create((char*)"qinfo"); - return 0; -} - -static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) -{ - if (!is_ok_load_info(env, load_info)) - return -1; - return 0; -} - -static void unload(ErlNifEnv* env, void* priv_data) -{ - enif_rwlock_destroy(qinfo_map_rwlock); -} - -static ErlNifFunc nif_funcs[] = { - {"new", 1, new_queue}, - {"info", 1, info}, - {"push_back", 2, push_back}, - {"pop_front", 1, pop_front}, - {"get_front", 1, get_front} -}; - -ERL_NIF_INIT(etsq, nif_funcs, load, NULL, upgrade, unload) diff --git a/c_src/etsq/etsq.h b/c_src/etsq/etsq.h deleted file mode 100644 index ef0d346..0000000 --- a/c_src/etsq/etsq.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * etsq.h - * - * Created on: Mar 21, 2016 - * Author: Vinod - */ - -#ifndef ETSQ_H_ -#define ETSQ_H_ - -#include // std::cin, std::cout -#include // std::map -#include // std::queue -#include -#include "erl_nif.h" - -#define enif_make_error(env, error) enif_make_tuple2(env, \ - enif_make_atom(env, "error"), enif_make_atom(env, error)) - -struct cmp_str -{ - bool operator()(char *a, char *b) const - { - return strcmp(a, b) < 0; - } -}; - -class ErlTerm -{ -public: - ErlNifEnv *term_env; - ERL_NIF_TERM term; -public: - ErlTerm(ERL_NIF_TERM erl_nif_term) - { - term_env = enif_alloc_env(); - this->term = enif_make_copy(term_env, erl_nif_term); - } - ErlTerm(ErlTerm *erl_term) - { - term_env = enif_alloc_env(); - this->term = enif_make_copy(term_env, erl_term->term); - } - ErlTerm(int value) - { - term_env = enif_alloc_env(); - this->term = enif_make_int(term_env, value); - } - ErlTerm(const char *error) - { - term_env = enif_alloc_env(); - this->term = enif_make_error(term_env, error); - } - ~ErlTerm() - { - enif_free_env(term_env); - term_env = NULL; - } -}; - -typedef std::queue ErlQueue; - -class QueueInfo -{ -public: - ErlNifMutex* pmutex; - ErlQueue queue; -public: - QueueInfo(char* name) - { - pmutex = enif_mutex_create(name); - } - ~QueueInfo() - { - enif_mutex_destroy(pmutex); - } -}; - -typedef std::map QInfoMap; -typedef std::pair QInfoMapPair; - -// Class to handle Read lock -class ReadLock -{ - ErlNifRWLock *pread_lock; -public: - ReadLock(ErlNifRWLock *pread_lock) - { - this->pread_lock = pread_lock; - enif_rwlock_rlock(this->pread_lock); - }; - ~ReadLock() - { - enif_rwlock_runlock(pread_lock); - }; -}; - -// Class to handle Write lock -class WriteLock -{ - ErlNifRWLock *pwrite_lock; -public: - WriteLock(ErlNifRWLock *pwrite_lock) - { - this->pwrite_lock = pwrite_lock; - enif_rwlock_rwlock(this->pwrite_lock); - }; - ~WriteLock() - { - enif_rwlock_rwunlock(pwrite_lock); - }; -}; - -// Class to handle Mutex lock and unlock -class Mutex -{ - ErlNifMutex *pmtx; -public: - Mutex(ErlNifMutex *pmtx) - { - this->pmtx = pmtx; - enif_mutex_lock(this->pmtx); - }; - ~Mutex() - { - enif_mutex_unlock(pmtx); - }; -}; - -#endif /* ETSQ_H_ */ diff --git a/c_src/etsq/rebar.config b/c_src/etsq/rebar.config deleted file mode 100644 index f6093dd..0000000 --- a/c_src/etsq/rebar.config +++ /dev/null @@ -1,7 +0,0 @@ -{port_specs, [ - {"../../priv/etsq.so", ["*.cpp"]} -]}. - - - - diff --git a/c_src/gb_lru/binary.h b/c_src/gb_lru/binary.h deleted file mode 100644 index dd21ae6..0000000 --- a/c_src/gb_lru/binary.h +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include - -class Binary { - public: - unsigned char *bin; - size_t size; - bool allocated; - - Binary() : bin(NULL), size(0), allocated(false) { } - Binary(const char *data) { - bin = (unsigned char *) data; - size = strlen(data); - allocated = false; - } - - Binary(const Binary &b) { - bin = b.bin; - size = b.size; - allocated = false; - } - - ~Binary() { - if (allocated) { - delete bin; - } - } - - operator std::string() { - return (const char *) bin; - } - - friend std::ostream & operator<<(std::ostream & str, Binary const &b) { - return str << b.bin; - } - - bool operator<(const Binary &b) { - if(size < b.size) { - return true; - } else if (size > b.size) { - return false; - } else { - return memcmp(bin,b.bin,size) < 0; - } - } - - bool operator<(Binary &b) { - if(size < b.size) { - return true; - } else if (size > b.size) { - return false; - } else { - return memcmp(bin,b.bin,size) < 0; - } - } - - bool operator>(const Binary &b) { - if(size > b.size) { - return true; - } else if (size < b.size) { - return false; - } else { - return memcmp(bin,b.bin,size) > 0; - } - } - - bool operator== (const Binary &b) { - if (size == b.size ) { - return memcmp(bin,b.bin, std::min(size, b.size)) == 0; - } else { - return false; - } - } - operator std::string() const { - return (const char*) bin; - } - - Binary& set_data(const char *data) { - bin = (unsigned char *) data; - size = strlen(data); - return *this; - } - - void copy(char *inbin, size_t insize) { - bin = (unsigned char *) operator new(insize); - allocated = true; - size = insize; - memcpy(bin, inbin, size); - } -}; - -inline bool operator < (const Binary &a, const Binary &b) { - - if(a.size < b.size) { - return true; - } else if (a.size > b.size) { - return false; - } else { - return memcmp(a.bin,b.bin, std::min(a.size, b.size)) < 0; - } -} - diff --git a/c_src/gb_lru/btree.h b/c_src/gb_lru/btree.h deleted file mode 100644 index 5035835..0000000 --- a/c_src/gb_lru/btree.h +++ /dev/null @@ -1,2394 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A btree implementation of the STL set and map interfaces. A btree is both -// smaller and faster than STL set/map. The red-black tree implementation of -// STL set/map has an overhead of 3 pointers (left, right and parent) plus the -// node color information for each stored value. So a set consumes 20 -// bytes for each value stored. This btree implementation stores multiple -// values on fixed size nodes (usually 256 bytes) and doesn't store child -// pointers for leaf nodes. The result is that a btree_set may use much -// less memory per stored value. For the random insertion benchmark in -// btree_test.cc, a btree_set with node-size of 256 uses 4.9 bytes per -// stored value. -// -// The packing of multiple values on to each node of a btree has another effect -// besides better space utilization: better cache locality due to fewer cache -// lines being accessed. Better cache locality translates into faster -// operations. -// -// CAVEATS -// -// Insertions and deletions on a btree can cause splitting, merging or -// rebalancing of btree nodes. And even without these operations, insertions -// and deletions on a btree will move values around within a node. In both -// cases, the result is that insertions and deletions can invalidate iterators -// pointing to values other than the one being inserted/deleted. This is -// notably different from STL set/map which takes care to not invalidate -// iterators on insert/erase except, of course, for iterators pointing to the -// value being erased. A partial workaround when erasing is available: -// erase() returns an iterator pointing to the item just after the one that was -// erased (or end() if none exists). See also safe_btree. - -// PERFORMANCE -// -// btree_bench --benchmarks=. 2>&1 | ./benchmarks.awk -// -// Run on pmattis-warp.nyc (4 X 2200 MHz CPUs); 2010/03/04-15:23:06 -// Benchmark STL(ns) B-Tree(ns) @ -// -------------------------------------------------------- -// BM_set_int32_insert 1516 608 +59.89% <256> [40.0, 5.2] -// BM_set_int32_lookup 1160 414 +64.31% <256> [40.0, 5.2] -// BM_set_int32_fulllookup 960 410 +57.29% <256> [40.0, 4.4] -// BM_set_int32_delete 1741 528 +69.67% <256> [40.0, 5.2] -// BM_set_int32_queueaddrem 3078 1046 +66.02% <256> [40.0, 5.5] -// BM_set_int32_mixedaddrem 3600 1384 +61.56% <256> [40.0, 5.3] -// BM_set_int32_fifo 227 113 +50.22% <256> [40.0, 4.4] -// BM_set_int32_fwditer 158 26 +83.54% <256> [40.0, 5.2] -// BM_map_int32_insert 1551 636 +58.99% <256> [48.0, 10.5] -// BM_map_int32_lookup 1200 508 +57.67% <256> [48.0, 10.5] -// BM_map_int32_fulllookup 989 487 +50.76% <256> [48.0, 8.8] -// BM_map_int32_delete 1794 628 +64.99% <256> [48.0, 10.5] -// BM_map_int32_queueaddrem 3189 1266 +60.30% <256> [48.0, 11.6] -// BM_map_int32_mixedaddrem 3822 1623 +57.54% <256> [48.0, 10.9] -// BM_map_int32_fifo 151 134 +11.26% <256> [48.0, 8.8] -// BM_map_int32_fwditer 161 32 +80.12% <256> [48.0, 10.5] -// BM_set_int64_insert 1546 636 +58.86% <256> [40.0, 10.5] -// BM_set_int64_lookup 1200 512 +57.33% <256> [40.0, 10.5] -// BM_set_int64_fulllookup 971 487 +49.85% <256> [40.0, 8.8] -// BM_set_int64_delete 1745 616 +64.70% <256> [40.0, 10.5] -// BM_set_int64_queueaddrem 3163 1195 +62.22% <256> [40.0, 11.6] -// BM_set_int64_mixedaddrem 3760 1564 +58.40% <256> [40.0, 10.9] -// BM_set_int64_fifo 146 103 +29.45% <256> [40.0, 8.8] -// BM_set_int64_fwditer 162 31 +80.86% <256> [40.0, 10.5] -// BM_map_int64_insert 1551 720 +53.58% <256> [48.0, 20.7] -// BM_map_int64_lookup 1214 612 +49.59% <256> [48.0, 20.7] -// BM_map_int64_fulllookup 994 592 +40.44% <256> [48.0, 17.2] -// BM_map_int64_delete 1778 764 +57.03% <256> [48.0, 20.7] -// BM_map_int64_queueaddrem 3189 1547 +51.49% <256> [48.0, 20.9] -// BM_map_int64_mixedaddrem 3779 1887 +50.07% <256> [48.0, 21.6] -// BM_map_int64_fifo 147 145 +1.36% <256> [48.0, 17.2] -// BM_map_int64_fwditer 162 41 +74.69% <256> [48.0, 20.7] -// BM_set_string_insert 1989 1966 +1.16% <256> [64.0, 44.5] -// BM_set_string_lookup 1709 1600 +6.38% <256> [64.0, 44.5] -// BM_set_string_fulllookup 1573 1529 +2.80% <256> [64.0, 35.4] -// BM_set_string_delete 2520 1920 +23.81% <256> [64.0, 44.5] -// BM_set_string_queueaddrem 4706 4309 +8.44% <256> [64.0, 48.3] -// BM_set_string_mixedaddrem 5080 4654 +8.39% <256> [64.0, 46.7] -// BM_set_string_fifo 318 512 -61.01% <256> [64.0, 35.4] -// BM_set_string_fwditer 182 93 +48.90% <256> [64.0, 44.5] -// BM_map_string_insert 2600 2227 +14.35% <256> [72.0, 55.8] -// BM_map_string_lookup 2068 1730 +16.34% <256> [72.0, 55.8] -// BM_map_string_fulllookup 1859 1618 +12.96% <256> [72.0, 44.0] -// BM_map_string_delete 3168 2080 +34.34% <256> [72.0, 55.8] -// BM_map_string_queueaddrem 5840 4701 +19.50% <256> [72.0, 59.4] -// BM_map_string_mixedaddrem 6400 5200 +18.75% <256> [72.0, 57.8] -// BM_map_string_fifo 398 596 -49.75% <256> [72.0, 44.0] -// BM_map_string_fwditer 243 113 +53.50% <256> [72.0, 55.8] - -#ifndef UTIL_BTREE_BTREE_H__ -#define UTIL_BTREE_BTREE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef NDEBUG -#define NDEBUG 1 -#endif - -namespace btree { - -// Inside a btree method, if we just call swap(), it will choose the -// btree::swap method, which we don't want. And we can't say ::swap -// because then MSVC won't pickup any std::swap() implementations. We -// can't just use std::swap() directly because then we don't get the -// specialization for types outside the std namespace. So the solution -// is to have a special swap helper function whose name doesn't -// collide with other swap functions defined by the btree classes. -template -inline void btree_swap_helper(T &a, T &b) { - using std::swap; - swap(a, b); -} - -// A template helper used to select A or B based on a condition. -template -struct if_{ - typedef A type; -}; - -template -struct if_ { - typedef B type; -}; - -// Types small_ and big_ are promise that sizeof(small_) < sizeof(big_) -typedef char small_; - -struct big_ { - char dummy[2]; -}; - -// A compile-time assertion. -template -struct CompileAssert { -}; - -#define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : 0] - -// A helper type used to indicate that a key-compare-to functor has been -// provided. A user can specify a key-compare-to functor by doing: -// -// struct MyStringComparer -// : public util::btree::btree_key_compare_to_tag { -// int operator()(const string &a, const string &b) const { -// return a.compare(b); -// } -// }; -// -// Note that the return type is an int and not a bool. There is a -// COMPILE_ASSERT which enforces this return type. -struct btree_key_compare_to_tag { -}; - -// A helper class that indicates if the Compare parameter is derived from -// btree_key_compare_to_tag. -template -struct btree_is_key_compare_to - : public std::is_convertible { -}; - -// A helper class to convert a boolean comparison into a three-way -// "compare-to" comparison that returns a negative value to indicate -// less-than, zero to indicate equality and a positive value to -// indicate greater-than. This helper class is specialized for -// less and greater. The btree_key_compare_to_adapter -// class is provided so that btree users automatically get the more -// efficient compare-to code when using common google string types -// with common comparison functors. -template -struct btree_key_compare_to_adapter : Compare { - btree_key_compare_to_adapter() { } - btree_key_compare_to_adapter(const Compare &c) : Compare(c) { } - btree_key_compare_to_adapter(const btree_key_compare_to_adapter &c) - : Compare(c) { - } -}; - -template <> -struct btree_key_compare_to_adapter > - : public btree_key_compare_to_tag { - btree_key_compare_to_adapter() {} - btree_key_compare_to_adapter(const std::less&) {} - btree_key_compare_to_adapter( - const btree_key_compare_to_adapter >&) {} - int operator()(const std::string &a, const std::string &b) const { - return a.compare(b); - } -}; - -template <> -struct btree_key_compare_to_adapter > - : public btree_key_compare_to_tag { - btree_key_compare_to_adapter() {} - btree_key_compare_to_adapter(const std::greater&) {} - btree_key_compare_to_adapter( - const btree_key_compare_to_adapter >&) {} - int operator()(const std::string &a, const std::string &b) const { - return b.compare(a); - } -}; - -// A helper class that allows a compare-to functor to behave like a plain -// compare functor. This specialization is used when we do not have a -// compare-to functor. -template -struct btree_key_comparer { - btree_key_comparer() {} - btree_key_comparer(Compare c) : comp(c) {} - static bool bool_compare(const Compare &comp, const Key &x, const Key &y) { - return comp(x, y); - } - bool operator()(const Key &x, const Key &y) const { - return bool_compare(comp, x, y); - } - Compare comp; -}; - -// A specialization of btree_key_comparer when a compare-to functor is -// present. We need a plain (boolean) comparison in some parts of the btree -// code, such as insert-with-hint. -template -struct btree_key_comparer { - btree_key_comparer() {} - btree_key_comparer(Compare c) : comp(c) {} - static bool bool_compare(const Compare &comp, const Key &x, const Key &y) { - return comp(x, y) < 0; - } - bool operator()(const Key &x, const Key &y) const { - return bool_compare(comp, x, y); - } - Compare comp; -}; - -// A helper function to compare to keys using the specified compare -// functor. This dispatches to the appropriate btree_key_comparer comparison, -// depending on whether we have a compare-to functor or not (which depends on -// whether Compare is derived from btree_key_compare_to_tag). -template -static bool btree_compare_keys( - const Compare &comp, const Key &x, const Key &y) { - typedef btree_key_comparer::value> key_comparer; - return key_comparer::bool_compare(comp, x, y); -} - -template -struct btree_common_params { - // If Compare is derived from btree_key_compare_to_tag then use it as the - // key_compare type. Otherwise, use btree_key_compare_to_adapter<> which will - // fall-back to Compare if we don't have an appropriate specialization. - typedef typename if_< - btree_is_key_compare_to::value, - Compare, btree_key_compare_to_adapter >::type key_compare; - // A type which indicates if we have a key-compare-to functor or a plain old - // key-compare functor. - typedef btree_is_key_compare_to is_key_compare_to; - - typedef Alloc allocator_type; - typedef Key key_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - enum { - kTargetNodeSize = TargetNodeSize, - - // Available space for values. This is largest for leaf nodes, - // which has overhead no fewer than two pointers. - kNodeValueSpace = TargetNodeSize - 2 * sizeof(void*), - }; - - // This is an integral type large enough to hold as many - // ValueSize-values as will fit a node of TargetNodeSize bytes. - typedef typename if_< - (kNodeValueSpace / ValueSize) >= 256, - uint16_t, - uint8_t>::type node_count_type; -}; - -// A parameters structure for holding the type parameters for a btree_map. -template -struct btree_map_params - : public btree_common_params { - typedef Data data_type; - typedef Data mapped_type; - typedef std::pair value_type; - typedef std::pair mutable_value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - - enum { - kValueSize = sizeof(Key) + sizeof(data_type), - }; - - static const Key& key(const value_type &x) { return x.first; } - static const Key& key(const mutable_value_type &x) { return x.first; } - static void swap(mutable_value_type *a, mutable_value_type *b) { - btree_swap_helper(a->first, b->first); - btree_swap_helper(a->second, b->second); - } -}; - -// A parameters structure for holding the type parameters for a btree_set. -template -struct btree_set_params - : public btree_common_params { - typedef std::false_type data_type; - typedef std::false_type mapped_type; - typedef Key value_type; - typedef value_type mutable_value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - - enum { - kValueSize = sizeof(Key), - }; - - static const Key& key(const value_type &x) { return x; } - static void swap(mutable_value_type *a, mutable_value_type *b) { - btree_swap_helper(*a, *b); - } -}; - -// An adapter class that converts a lower-bound compare into an upper-bound -// compare. -template -struct btree_upper_bound_adapter : public Compare { - btree_upper_bound_adapter(Compare c) : Compare(c) {} - bool operator()(const Key &a, const Key &b) const { - return !static_cast(*this)(b, a); - } -}; - -template -struct btree_upper_bound_compare_to_adapter : public CompareTo { - btree_upper_bound_compare_to_adapter(CompareTo c) : CompareTo(c) {} - int operator()(const Key &a, const Key &b) const { - return static_cast(*this)(b, a); - } -}; - -// Dispatch helper class for using linear search with plain compare. -template -struct btree_linear_search_plain_compare { - static int lower_bound(const K &k, const N &n, Compare comp) { - return n.linear_search_plain_compare(k, 0, n.count(), comp); - } - static int upper_bound(const K &k, const N &n, Compare comp) { - typedef btree_upper_bound_adapter upper_compare; - return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// Dispatch helper class for using linear search with compare-to -template -struct btree_linear_search_compare_to { - static int lower_bound(const K &k, const N &n, CompareTo comp) { - return n.linear_search_compare_to(k, 0, n.count(), comp); - } - static int upper_bound(const K &k, const N &n, CompareTo comp) { - typedef btree_upper_bound_adapter > upper_compare; - return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// Dispatch helper class for using binary search with plain compare. -template -struct btree_binary_search_plain_compare { - static int lower_bound(const K &k, const N &n, Compare comp) { - return n.binary_search_plain_compare(k, 0, n.count(), comp); - } - static int upper_bound(const K &k, const N &n, Compare comp) { - typedef btree_upper_bound_adapter upper_compare; - return n.binary_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// Dispatch helper class for using binary search with compare-to. -template -struct btree_binary_search_compare_to { - static int lower_bound(const K &k, const N &n, CompareTo comp) { - return n.binary_search_compare_to(k, 0, n.count(), CompareTo()); - } - static int upper_bound(const K &k, const N &n, CompareTo comp) { - typedef btree_upper_bound_adapter > upper_compare; - return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// A node in the btree holding. The same node type is used for both internal -// and leaf nodes in the btree, though the nodes are allocated in such a way -// that the children array is only valid in internal nodes. -template -class btree_node { - public: - typedef Params params_type; - typedef btree_node self_type; - typedef typename Params::key_type key_type; - typedef typename Params::data_type data_type; - typedef typename Params::value_type value_type; - typedef typename Params::mutable_value_type mutable_value_type; - typedef typename Params::pointer pointer; - typedef typename Params::const_pointer const_pointer; - typedef typename Params::reference reference; - typedef typename Params::const_reference const_reference; - typedef typename Params::key_compare key_compare; - typedef typename Params::size_type size_type; - typedef typename Params::difference_type difference_type; - // Typedefs for the various types of node searches. - typedef btree_linear_search_plain_compare< - key_type, self_type, key_compare> linear_search_plain_compare_type; - typedef btree_linear_search_compare_to< - key_type, self_type, key_compare> linear_search_compare_to_type; - typedef btree_binary_search_plain_compare< - key_type, self_type, key_compare> binary_search_plain_compare_type; - typedef btree_binary_search_compare_to< - key_type, self_type, key_compare> binary_search_compare_to_type; - // If we have a valid key-compare-to type, use linear_search_compare_to, - // otherwise use linear_search_plain_compare. - typedef typename if_< - Params::is_key_compare_to::value, - linear_search_compare_to_type, - linear_search_plain_compare_type>::type linear_search_type; - // If we have a valid key-compare-to type, use binary_search_compare_to, - // otherwise use binary_search_plain_compare. - typedef typename if_< - Params::is_key_compare_to::value, - binary_search_compare_to_type, - binary_search_plain_compare_type>::type binary_search_type; - // If the key is an integral or floating point type, use linear search which - // is faster than binary search for such types. Might be wise to also - // configure linear search based on node-size. - typedef typename if_< - std::is_integral::value || - std::is_floating_point::value, - linear_search_type, binary_search_type>::type search_type; - - struct base_fields { - typedef typename Params::node_count_type field_type; - - // A boolean indicating whether the node is a leaf or not. - bool leaf; - // The position of the node in the node's parent. - field_type position; - // The maximum number of values the node can hold. - field_type max_count; - // The count of the number of values in the node. - field_type count; - // A pointer to the node's parent. - btree_node *parent; - }; - - enum { - kValueSize = params_type::kValueSize, - kTargetNodeSize = params_type::kTargetNodeSize, - - // Compute how many values we can fit onto a leaf node. - kNodeTargetValues = (kTargetNodeSize - sizeof(base_fields)) / kValueSize, - // We need a minimum of 3 values per internal node in order to perform - // splitting (1 value for the two nodes involved in the split and 1 value - // propagated to the parent as the delimiter for the split). - kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, - - kExactMatch = 1 << 30, - kMatchMask = kExactMatch - 1, - }; - - struct leaf_fields : public base_fields { - // The array of values. Only the first count of these values have been - // constructed and are valid. - mutable_value_type values[kNodeValues]; - }; - - struct internal_fields : public leaf_fields { - // The array of child pointers. The keys in children_[i] are all less than - // key(i). The keys in children_[i + 1] are all greater than key(i). There - // are always count + 1 children. - btree_node *children[kNodeValues + 1]; - }; - - struct root_fields : public internal_fields { - btree_node *rightmost; - size_type size; - }; - - public: - // Getter/setter for whether this is a leaf node or not. This value doesn't - // change after the node is created. - bool leaf() const { return fields_.leaf; } - - // Getter for the position of this node in its parent. - int position() const { return fields_.position; } - void set_position(int v) { fields_.position = v; } - - // Getter/setter for the number of values stored in this node. - int count() const { return fields_.count; } - void set_count(int v) { fields_.count = v; } - int max_count() const { return fields_.max_count; } - - // Getter for the parent of this node. - btree_node* parent() const { return fields_.parent; } - // Getter for whether the node is the root of the tree. The parent of the - // root of the tree is the leftmost node in the tree which is guaranteed to - // be a leaf. - bool is_root() const { return parent()->leaf(); } - void make_root() { - assert(parent()->is_root()); - fields_.parent = fields_.parent->parent(); - } - - // Getter for the rightmost root node field. Only valid on the root node. - btree_node* rightmost() const { return fields_.rightmost; } - btree_node** mutable_rightmost() { return &fields_.rightmost; } - - // Getter for the size root node field. Only valid on the root node. - size_type size() const { return fields_.size; } - size_type* mutable_size() { return &fields_.size; } - - // Getters for the key/value at position i in the node. - const key_type& key(int i) const { - return params_type::key(fields_.values[i]); - } - reference value(int i) { - return reinterpret_cast(fields_.values[i]); - } - const_reference value(int i) const { - return reinterpret_cast(fields_.values[i]); - } - mutable_value_type* mutable_value(int i) { - return &fields_.values[i]; - } - - // Swap value i in this node with value j in node x. - void value_swap(int i, btree_node *x, int j) { - params_type::swap(mutable_value(i), x->mutable_value(j)); - } - - // Getters/setter for the child at position i in the node. - btree_node* child(int i) const { return fields_.children[i]; } - btree_node** mutable_child(int i) { return &fields_.children[i]; } - void set_child(int i, btree_node *c) { - *mutable_child(i) = c; - c->fields_.parent = this; - c->fields_.position = i; - } - - // Returns the position of the first value whose key is not less than k. - template - int lower_bound(const key_type &k, const Compare &comp) const { - return search_type::lower_bound(k, *this, comp); - } - // Returns the position of the first value whose key is greater than k. - template - int upper_bound(const key_type &k, const Compare &comp) const { - return search_type::upper_bound(k, *this, comp); - } - - // Returns the position of the first value whose key is not less than k using - // linear search performed using plain compare. - template - int linear_search_plain_compare( - const key_type &k, int s, int e, const Compare &comp) const { - while (s < e) { - if (!btree_compare_keys(comp, key(s), k)) { - break; - } - ++s; - } - return s; - } - - // Returns the position of the first value whose key is not less than k using - // linear search performed using compare-to. - template - int linear_search_compare_to( - const key_type &k, int s, int e, const Compare &comp) const { - while (s < e) { - int c = comp(key(s), k); - if (c == 0) { - return s | kExactMatch; - } else if (c > 0) { - break; - } - ++s; - } - return s; - } - - // Returns the position of the first value whose key is not less than k using - // binary search performed using plain compare. - template - int binary_search_plain_compare( - const key_type &k, int s, int e, const Compare &comp) const { - while (s != e) { - int mid = (s + e) / 2; - if (btree_compare_keys(comp, key(mid), k)) { - s = mid + 1; - } else { - e = mid; - } - } - return s; - } - - // Returns the position of the first value whose key is not less than k using - // binary search performed using compare-to. - template - int binary_search_compare_to( - const key_type &k, int s, int e, const CompareTo &comp) const { - while (s != e) { - int mid = (s + e) / 2; - int c = comp(key(mid), k); - if (c < 0) { - s = mid + 1; - } else if (c > 0) { - e = mid; - } else { - // Need to return the first value whose key is not less than k, which - // requires continuing the binary search. Note that we are guaranteed - // that the result is an exact match because if "key(mid-1) < k" the - // call to binary_search_compare_to() will return "mid". - s = binary_search_compare_to(k, s, mid, comp); - return s | kExactMatch; - } - } - return s; - } - - // Inserts the value x at position i, shifting all existing values and - // children at positions >= i to the right by 1. - void insert_value(int i, const value_type &x); - - // Removes the value at position i, shifting all existing values and children - // at positions > i to the left by 1. - void remove_value(int i); - - // Rebalances a node with its right sibling. - void rebalance_right_to_left(btree_node *sibling, int to_move); - void rebalance_left_to_right(btree_node *sibling, int to_move); - - // Splits a node, moving a portion of the node's values to its right sibling. - void split(btree_node *sibling, int insert_position); - - // Merges a node with its right sibling, moving all of the values and the - // delimiting key in the parent node onto itself. - void merge(btree_node *sibling); - - // Swap the contents of "this" and "src". - void swap(btree_node *src); - - // Node allocation/deletion routines. - static btree_node* init_leaf( - leaf_fields *f, btree_node *parent, int max_count) { - btree_node *n = reinterpret_cast(f); - f->leaf = 1; - f->position = 0; - f->max_count = max_count; - f->count = 0; - f->parent = parent; - if (!NDEBUG) { - memset(&f->values, 0, max_count * sizeof(value_type)); - } - return n; - } - static btree_node* init_internal(internal_fields *f, btree_node *parent) { - btree_node *n = init_leaf(f, parent, kNodeValues); - f->leaf = 0; - if (!NDEBUG) { - memset(f->children, 0, sizeof(f->children)); - } - return n; - } - static btree_node* init_root(root_fields *f, btree_node *parent) { - btree_node *n = init_internal(f, parent); - f->rightmost = parent; - f->size = parent->count(); - return n; - } - void destroy() { - for (int i = 0; i < count(); ++i) { - value_destroy(i); - } - } - - private: - void value_init(int i) { - new (&fields_.values[i]) mutable_value_type; - } - void value_init(int i, const value_type &x) { - new (&fields_.values[i]) mutable_value_type(x); - } - void value_destroy(int i) { - fields_.values[i].~mutable_value_type(); - } - - private: - root_fields fields_; - - private: - btree_node(const btree_node&); - void operator=(const btree_node&); -}; - -template -struct btree_iterator { - typedef typename Node::key_type key_type; - typedef typename Node::size_type size_type; - typedef typename Node::difference_type difference_type; - typedef typename Node::params_type params_type; - - typedef Node node_type; - typedef typename std::remove_const::type normal_node; - typedef const Node const_node; - typedef typename params_type::value_type value_type; - typedef typename params_type::pointer normal_pointer; - typedef typename params_type::reference normal_reference; - typedef typename params_type::const_pointer const_pointer; - typedef typename params_type::const_reference const_reference; - - typedef Pointer pointer; - typedef Reference reference; - typedef std::bidirectional_iterator_tag iterator_category; - - typedef btree_iterator< - normal_node, normal_reference, normal_pointer> iterator; - typedef btree_iterator< - const_node, const_reference, const_pointer> const_iterator; - typedef btree_iterator self_type; - - btree_iterator() - : node(NULL), - position(-1) { - } - btree_iterator(Node *n, int p) - : node(n), - position(p) { - } - btree_iterator(const iterator &x) - : node(x.node), - position(x.position) { - } - - // Increment/decrement the iterator. - void increment() { - if (node->leaf() && ++position < node->count()) { - return; - } - increment_slow(); - } - void increment_by(int count); - void increment_slow(); - - void decrement() { - if (node->leaf() && --position >= 0) { - return; - } - decrement_slow(); - } - void decrement_slow(); - - bool operator==(const const_iterator &x) const { - return node == x.node && position == x.position; - } - bool operator!=(const const_iterator &x) const { - return node != x.node || position != x.position; - } - - // Accessors for the key/value the iterator is pointing at. - const key_type& key() const { - return node->key(position); - } - reference operator*() const { - return node->value(position); - } - pointer operator->() const { - return &node->value(position); - } - - self_type& operator++() { - increment(); - return *this; - } - self_type& operator--() { - decrement(); - return *this; - } - self_type operator++(int) { - self_type tmp = *this; - ++*this; - return tmp; - } - self_type operator--(int) { - self_type tmp = *this; - --*this; - return tmp; - } - - // The node in the tree the iterator is pointing at. - Node *node; - // The position within the node of the tree the iterator is pointing at. - int position; -}; - -// Dispatch helper class for using btree::internal_locate with plain compare. -struct btree_internal_locate_plain_compare { - template - static std::pair dispatch(const K &k, const T &t, Iter iter) { - return t.internal_locate_plain_compare(k, iter); - } -}; - -// Dispatch helper class for using btree::internal_locate with compare-to. -struct btree_internal_locate_compare_to { - template - static std::pair dispatch(const K &k, const T &t, Iter iter) { - return t.internal_locate_compare_to(k, iter); - } -}; - -template -class btree : public Params::key_compare { - typedef btree self_type; - typedef btree_node node_type; - typedef typename node_type::base_fields base_fields; - typedef typename node_type::leaf_fields leaf_fields; - typedef typename node_type::internal_fields internal_fields; - typedef typename node_type::root_fields root_fields; - typedef typename Params::is_key_compare_to is_key_compare_to; - - friend struct btree_internal_locate_plain_compare; - friend struct btree_internal_locate_compare_to; - typedef typename if_< - is_key_compare_to::value, - btree_internal_locate_compare_to, - btree_internal_locate_plain_compare>::type internal_locate_type; - - enum { - kNodeValues = node_type::kNodeValues, - kMinNodeValues = kNodeValues / 2, - kValueSize = node_type::kValueSize, - kExactMatch = node_type::kExactMatch, - kMatchMask = node_type::kMatchMask, - }; - - // A helper class to get the empty base class optimization for 0-size - // allocators. Base is internal_allocator_type. - // (e.g. empty_base_handle). If Base is - // 0-size, the compiler doesn't have to reserve any space for it and - // sizeof(empty_base_handle) will simply be sizeof(Data). Google [empty base - // class optimization] for more details. - template - struct empty_base_handle : public Base { - empty_base_handle(const Base &b, const Data &d) - : Base(b), - data(d) { - } - Data data; - }; - - struct node_stats { - node_stats(size_t l, size_t i) - : leaf_nodes(l), - internal_nodes(i) { - } - - node_stats& operator+=(const node_stats &x) { - leaf_nodes += x.leaf_nodes; - internal_nodes += x.internal_nodes; - return *this; - } - - size_t leaf_nodes; - size_t internal_nodes; - }; - - public: - typedef Params params_type; - typedef typename Params::key_type key_type; - typedef typename Params::data_type data_type; - typedef typename Params::mapped_type mapped_type; - typedef typename Params::value_type value_type; - typedef typename Params::key_compare key_compare; - typedef typename Params::pointer pointer; - typedef typename Params::const_pointer const_pointer; - typedef typename Params::reference reference; - typedef typename Params::const_reference const_reference; - typedef typename Params::size_type size_type; - typedef typename Params::difference_type difference_type; - typedef btree_iterator iterator; - typedef typename iterator::const_iterator const_iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; - - typedef typename Params::allocator_type allocator_type; - typedef typename allocator_type::template rebind::other - internal_allocator_type; - - public: - // Default constructor. - btree(const key_compare &comp, const allocator_type &alloc); - - // Copy constructor. - btree(const self_type &x); - - // Destructor. - ~btree() { - clear(); - } - - // Iterator routines. - iterator begin() { - return iterator(leftmost(), 0); - } - const_iterator begin() const { - return const_iterator(leftmost(), 0); - } - iterator end() { - return iterator(rightmost(), rightmost() ? rightmost()->count() : 0); - } - const_iterator end() const { - return const_iterator(rightmost(), rightmost() ? rightmost()->count() : 0); - } - reverse_iterator rbegin() { - return reverse_iterator(end()); - } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - reverse_iterator rend() { - return reverse_iterator(begin()); - } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - // Finds the first element whose key is not less than key. - iterator lower_bound(const key_type &key) { - return internal_end( - internal_lower_bound(key, iterator(root(), 0))); - } - const_iterator lower_bound(const key_type &key) const { - return internal_end( - internal_lower_bound(key, const_iterator(root(), 0))); - } - - // Finds the first element whose key is greater than key. - iterator upper_bound(const key_type &key) { - return internal_end( - internal_upper_bound(key, iterator(root(), 0))); - } - const_iterator upper_bound(const key_type &key) const { - return internal_end( - internal_upper_bound(key, const_iterator(root(), 0))); - } - - // Finds the range of values which compare equal to key. The first member of - // the returned pair is equal to lower_bound(key). The second member pair of - // the pair is equal to upper_bound(key). - std::pair equal_range(const key_type &key) { - return std::make_pair(lower_bound(key), upper_bound(key)); - } - std::pair equal_range(const key_type &key) const { - return std::make_pair(lower_bound(key), upper_bound(key)); - } - - // Inserts a value into the btree only if it does not already exist. The - // boolean return value indicates whether insertion succeeded or failed. The - // ValuePointer type is used to avoid instatiating the value unless the key - // is being inserted. Value is not dereferenced if the key already exists in - // the btree. See btree_map::operator[]. - template - std::pair insert_unique(const key_type &key, ValuePointer value); - - // Inserts a value into the btree only if it does not already exist. The - // boolean return value indicates whether insertion succeeded or failed. - std::pair insert_unique(const value_type &v) { - return insert_unique(params_type::key(v), &v); - } - - // Insert with hint. Check to see if the value should be placed immediately - // before position in the tree. If it does, then the insertion will take - // amortized constant time. If not, the insertion will take amortized - // logarithmic time as if a call to insert_unique(v) were made. - iterator insert_unique(iterator position, const value_type &v); - - // Insert a range of values into the btree. - template - void insert_unique(InputIterator b, InputIterator e); - - // Inserts a value into the btree. The ValuePointer type is used to avoid - // instatiating the value unless the key is being inserted. Value is not - // dereferenced if the key already exists in the btree. See - // btree_map::operator[]. - template - iterator insert_multi(const key_type &key, ValuePointer value); - - // Inserts a value into the btree. - iterator insert_multi(const value_type &v) { - return insert_multi(params_type::key(v), &v); - } - - // Insert with hint. Check to see if the value should be placed immediately - // before position in the tree. If it does, then the insertion will take - // amortized constant time. If not, the insertion will take amortized - // logarithmic time as if a call to insert_multi(v) were made. - iterator insert_multi(iterator position, const value_type &v); - - // Insert a range of values into the btree. - template - void insert_multi(InputIterator b, InputIterator e); - - void assign(const self_type &x); - - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(iterator iter); - - // Erases range. Returns the number of keys erased. - int erase(iterator begin, iterator end); - - // Erases the specified key from the btree. Returns 1 if an element was - // erased and 0 otherwise. - int erase_unique(const key_type &key); - - // Erases all of the entries matching the specified key from the - // btree. Returns the number of elements erased. - int erase_multi(const key_type &key); - - // Finds the iterator corresponding to a key or returns end() if the key is - // not present. - iterator find_unique(const key_type &key) { - return internal_end( - internal_find_unique(key, iterator(root(), 0))); - } - const_iterator find_unique(const key_type &key) const { - return internal_end( - internal_find_unique(key, const_iterator(root(), 0))); - } - iterator find_multi(const key_type &key) { - return internal_end( - internal_find_multi(key, iterator(root(), 0))); - } - const_iterator find_multi(const key_type &key) const { - return internal_end( - internal_find_multi(key, const_iterator(root(), 0))); - } - - // Returns a count of the number of times the key appears in the btree. - size_type count_unique(const key_type &key) const { - const_iterator begin = internal_find_unique( - key, const_iterator(root(), 0)); - if (!begin.node) { - // The key doesn't exist in the tree. - return 0; - } - return 1; - } - // Returns a count of the number of times the key appears in the btree. - size_type count_multi(const key_type &key) const { - return distance(lower_bound(key), upper_bound(key)); - } - - // Clear the btree, deleting all of the values it contains. - void clear(); - - // Swap the contents of *this and x. - void swap(self_type &x); - - // Assign the contents of x to *this. - self_type& operator=(const self_type &x) { - if (&x == this) { - // Don't copy onto ourselves. - return *this; - } - assign(x); - return *this; - } - - key_compare* mutable_key_comp() { - return this; - } - const key_compare& key_comp() const { - return *this; - } - bool compare_keys(const key_type &x, const key_type &y) const { - return btree_compare_keys(key_comp(), x, y); - } - - // Dump the btree to the specified ostream. Requires that operator<< is - // defined for Key and Value. - void dump(std::ostream &os) const { - if (root() != NULL) { - internal_dump(os, root(), 0); - } - } - - // Verifies the structure of the btree. - void verify() const; - - // Size routines. Note that empty() is slightly faster than doing size()==0. - size_type size() const { - if (empty()) return 0; - if (root()->leaf()) return root()->count(); - return root()->size(); - } - size_type max_size() const { return std::numeric_limits::max(); } - bool empty() const { return root() == NULL; } - - // The height of the btree. An empty tree will have height 0. - size_type height() const { - size_type h = 0; - if (root()) { - // Count the length of the chain from the leftmost node up to the - // root. We actually count from the root back around to the level below - // the root, but the calculation is the same because of the circularity - // of that traversal. - const node_type *n = root(); - do { - ++h; - n = n->parent(); - } while (n != root()); - } - return h; - } - - // The number of internal, leaf and total nodes used by the btree. - size_type leaf_nodes() const { - return internal_stats(root()).leaf_nodes; - } - size_type internal_nodes() const { - return internal_stats(root()).internal_nodes; - } - size_type nodes() const { - node_stats stats = internal_stats(root()); - return stats.leaf_nodes + stats.internal_nodes; - } - - // The total number of bytes used by the btree. - size_type bytes_used() const { - node_stats stats = internal_stats(root()); - if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { - return sizeof(*this) + - sizeof(base_fields) + root()->max_count() * sizeof(value_type); - } else { - return sizeof(*this) + - sizeof(root_fields) - sizeof(internal_fields) + - stats.leaf_nodes * sizeof(leaf_fields) + - stats.internal_nodes * sizeof(internal_fields); - } - } - - // The average number of bytes used per value stored in the btree. - static double average_bytes_per_value() { - // Returns the number of bytes per value on a leaf node that is 75% - // full. Experimentally, this matches up nicely with the computed number of - // bytes per value in trees that had their values inserted in random order. - return sizeof(leaf_fields) / (kNodeValues * 0.75); - } - - // The fullness of the btree. Computed as the number of elements in the btree - // divided by the maximum number of elements a tree with the current number - // of nodes could hold. A value of 1 indicates perfect space - // utilization. Smaller values indicate space wastage. - double fullness() const { - return double(size()) / (nodes() * kNodeValues); - } - // The overhead of the btree structure in bytes per node. Computed as the - // total number of bytes used by the btree minus the number of bytes used for - // storing elements divided by the number of elements. - double overhead() const { - if (empty()) { - return 0.0; - } - return (bytes_used() - size() * kValueSize) / double(size()); - } - - private: - // Internal accessor routines. - node_type* root() { return root_.data; } - const node_type* root() const { return root_.data; } - node_type** mutable_root() { return &root_.data; } - - // The rightmost node is stored in the root node. - node_type* rightmost() { - return (!root() || root()->leaf()) ? root() : root()->rightmost(); - } - const node_type* rightmost() const { - return (!root() || root()->leaf()) ? root() : root()->rightmost(); - } - node_type** mutable_rightmost() { return root()->mutable_rightmost(); } - - // The leftmost node is stored as the parent of the root node. - node_type* leftmost() { return root() ? root()->parent() : NULL; } - const node_type* leftmost() const { return root() ? root()->parent() : NULL; } - - // The size of the tree is stored in the root node. - size_type* mutable_size() { return root()->mutable_size(); } - - // Allocator routines. - internal_allocator_type* mutable_internal_allocator() { - return static_cast(&root_); - } - const internal_allocator_type& internal_allocator() const { - return *static_cast(&root_); - } - - // Node creation/deletion routines. - node_type* new_internal_node(node_type *parent) { - internal_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate(sizeof(internal_fields))); - return node_type::init_internal(p, parent); - } - node_type* new_internal_root_node() { - root_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate(sizeof(root_fields))); - return node_type::init_root(p, root()->parent()); - } - node_type* new_leaf_node(node_type *parent) { - leaf_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate(sizeof(leaf_fields))); - return node_type::init_leaf(p, parent, kNodeValues); - } - node_type* new_leaf_root_node(int max_count) { - leaf_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate( - sizeof(base_fields) + max_count * sizeof(value_type))); - return node_type::init_leaf(p, reinterpret_cast(p), max_count); - } - void delete_internal_node(node_type *node) { - node->destroy(); - assert(node != root()); - mutable_internal_allocator()->deallocate( - reinterpret_cast(node), sizeof(internal_fields)); - } - void delete_internal_root_node() { - root()->destroy(); - mutable_internal_allocator()->deallocate( - reinterpret_cast(root()), sizeof(root_fields)); - } - void delete_leaf_node(node_type *node) { - node->destroy(); - mutable_internal_allocator()->deallocate( - reinterpret_cast(node), - sizeof(base_fields) + node->max_count() * sizeof(value_type)); - } - - // Rebalances or splits the node iter points to. - void rebalance_or_split(iterator *iter); - - // Merges the values of left, right and the delimiting key on their parent - // onto left, removing the delimiting key and deleting right. - void merge_nodes(node_type *left, node_type *right); - - // Tries to merge node with its left or right sibling, and failing that, - // rebalance with its left or right sibling. Returns true if a merge - // occurred, at which point it is no longer valid to access node. Returns - // false if no merging took place. - bool try_merge_or_rebalance(iterator *iter); - - // Tries to shrink the height of the tree by 1. - void try_shrink(); - - iterator internal_end(iterator iter) { - return iter.node ? iter : end(); - } - const_iterator internal_end(const_iterator iter) const { - return iter.node ? iter : end(); - } - - // Inserts a value into the btree immediately before iter. Requires that - // key(v) <= iter.key() and (--iter).key() <= key(v). - iterator internal_insert(iterator iter, const value_type &v); - - // Returns an iterator pointing to the first value >= the value "iter" is - // pointing at. Note that "iter" might be pointing to an invalid location as - // iter.position == iter.node->count(). This routine simply moves iter up in - // the tree to a valid location. - template - static IterType internal_last(IterType iter); - - // Returns an iterator pointing to the leaf position at which key would - // reside in the tree. We provide 2 versions of internal_locate. The first - // version (internal_locate_plain_compare) always returns 0 for the second - // field of the pair. The second version (internal_locate_compare_to) is for - // the key-compare-to specialization and returns either kExactMatch (if the - // key was found in the tree) or -kExactMatch (if it wasn't) in the second - // field of the pair. The compare_to specialization allows the caller to - // avoid a subsequent comparison to determine if an exact match was made, - // speeding up string keys. - template - std::pair internal_locate( - const key_type &key, IterType iter) const; - template - std::pair internal_locate_plain_compare( - const key_type &key, IterType iter) const; - template - std::pair internal_locate_compare_to( - const key_type &key, IterType iter) const; - - // Internal routine which implements lower_bound(). - template - IterType internal_lower_bound( - const key_type &key, IterType iter) const; - - // Internal routine which implements upper_bound(). - template - IterType internal_upper_bound( - const key_type &key, IterType iter) const; - - // Internal routine which implements find_unique(). - template - IterType internal_find_unique( - const key_type &key, IterType iter) const; - - // Internal routine which implements find_multi(). - template - IterType internal_find_multi( - const key_type &key, IterType iter) const; - - // Deletes a node and all of its children. - void internal_clear(node_type *node); - - // Dumps a node and all of its children to the specified ostream. - void internal_dump(std::ostream &os, const node_type *node, int level) const; - - // Verifies the tree structure of node. - int internal_verify(const node_type *node, - const key_type *lo, const key_type *hi) const; - - node_stats internal_stats(const node_type *node) const { - if (!node) { - return node_stats(0, 0); - } - if (node->leaf()) { - return node_stats(1, 0); - } - node_stats res(0, 1); - for (int i = 0; i <= node->count(); ++i) { - res += internal_stats(node->child(i)); - } - return res; - } - - private: - empty_base_handle root_; - - private: - // A never instantiated helper function that returns big_ if we have a - // key-compare-to functor or if R is bool and small_ otherwise. - template - static typename if_< - if_, - std::is_same >::type::value, - big_, small_>::type key_compare_checker(R); - - // A never instantiated helper function that returns the key comparison - // functor. - static key_compare key_compare_helper(); - - // Verify that key_compare returns a bool. This is similar to the way - // is_convertible in base/type_traits.h works. Note that key_compare_checker - // is never actually invoked. The compiler will select which - // key_compare_checker() to instantiate and then figure out the size of the - // return type of key_compare_checker() at compile time which we then check - // against the sizeof of big_. - COMPILE_ASSERT( - sizeof(key_compare_checker(key_compare_helper()(key_type(), key_type()))) == - sizeof(big_), - key_comparison_function_must_return_bool); - - // Note: We insist on kTargetValues, which is computed from - // Params::kTargetNodeSize, must fit the base_fields::field_type. - COMPILE_ASSERT(kNodeValues < - (1 << (8 * sizeof(typename base_fields::field_type))), - target_node_size_too_large); - - // Test the assumption made in setting kNodeValueSpace. - COMPILE_ASSERT(sizeof(base_fields) >= 2 * sizeof(void*), - node_space_assumption_incorrect); -}; - -//// -// btree_node methods -template -inline void btree_node

::insert_value(int i, const value_type &x) { - assert(i <= count()); - value_init(count(), x); - for (int j = count(); j > i; --j) { - value_swap(j, this, j - 1); - } - set_count(count() + 1); - - if (!leaf()) { - ++i; - for (int j = count(); j > i; --j) { - *mutable_child(j) = child(j - 1); - child(j)->set_position(j); - } - *mutable_child(i) = NULL; - } -} - -template -inline void btree_node

::remove_value(int i) { - if (!leaf()) { - assert(child(i + 1)->count() == 0); - for (int j = i + 1; j < count(); ++j) { - *mutable_child(j) = child(j + 1); - child(j)->set_position(j); - } - *mutable_child(count()) = NULL; - } - - set_count(count() - 1); - for (; i < count(); ++i) { - value_swap(i, this, i + 1); - } - value_destroy(i); -} - -template -void btree_node

::rebalance_right_to_left(btree_node *src, int to_move) { - assert(parent() == src->parent()); - assert(position() + 1 == src->position()); - assert(src->count() >= count()); - assert(to_move >= 1); - assert(to_move <= src->count()); - - // Make room in the left node for the new values. - for (int i = 0; i < to_move; ++i) { - value_init(i + count()); - } - - // Move the delimiting value to the left node and the new delimiting value - // from the right node. - value_swap(count(), parent(), position()); - parent()->value_swap(position(), src, to_move - 1); - - // Move the values from the right to the left node. - for (int i = 1; i < to_move; ++i) { - value_swap(count() + i, src, i - 1); - } - // Shift the values in the right node to their correct position. - for (int i = to_move; i < src->count(); ++i) { - src->value_swap(i - to_move, src, i); - } - for (int i = 1; i <= to_move; ++i) { - src->value_destroy(src->count() - i); - } - - if (!leaf()) { - // Move the child pointers from the right to the left node. - for (int i = 0; i < to_move; ++i) { - set_child(1 + count() + i, src->child(i)); - } - for (int i = 0; i <= src->count() - to_move; ++i) { - assert(i + to_move <= src->max_count()); - src->set_child(i, src->child(i + to_move)); - *src->mutable_child(i + to_move) = NULL; - } - } - - // Fixup the counts on the src and dest nodes. - set_count(count() + to_move); - src->set_count(src->count() - to_move); -} - -template -void btree_node

::rebalance_left_to_right(btree_node *dest, int to_move) { - assert(parent() == dest->parent()); - assert(position() + 1 == dest->position()); - assert(count() >= dest->count()); - assert(to_move >= 1); - assert(to_move <= count()); - - // Make room in the right node for the new values. - for (int i = 0; i < to_move; ++i) { - dest->value_init(i + dest->count()); - } - for (int i = dest->count() - 1; i >= 0; --i) { - dest->value_swap(i, dest, i + to_move); - } - - // Move the delimiting value to the right node and the new delimiting value - // from the left node. - dest->value_swap(to_move - 1, parent(), position()); - parent()->value_swap(position(), this, count() - to_move); - value_destroy(count() - to_move); - - // Move the values from the left to the right node. - for (int i = 1; i < to_move; ++i) { - value_swap(count() - to_move + i, dest, i - 1); - value_destroy(count() - to_move + i); - } - - if (!leaf()) { - // Move the child pointers from the left to the right node. - for (int i = dest->count(); i >= 0; --i) { - dest->set_child(i + to_move, dest->child(i)); - *dest->mutable_child(i) = NULL; - } - for (int i = 1; i <= to_move; ++i) { - dest->set_child(i - 1, child(count() - to_move + i)); - *mutable_child(count() - to_move + i) = NULL; - } - } - - // Fixup the counts on the src and dest nodes. - set_count(count() - to_move); - dest->set_count(dest->count() + to_move); -} - -template -void btree_node

::split(btree_node *dest, int insert_position) { - assert(dest->count() == 0); - - // We bias the split based on the position being inserted. If we're - // inserting at the beginning of the left node then bias the split to put - // more values on the right node. If we're inserting at the end of the - // right node then bias the split to put more values on the left node. - if (insert_position == 0) { - dest->set_count(count() - 1); - } else if (insert_position == max_count()) { - dest->set_count(0); - } else { - dest->set_count(count() / 2); - } - set_count(count() - dest->count()); - assert(count() >= 1); - - // Move values from the left sibling to the right sibling. - for (int i = 0; i < dest->count(); ++i) { - dest->value_init(i); - value_swap(count() + i, dest, i); - value_destroy(count() + i); - } - - // The split key is the largest value in the left sibling. - set_count(count() - 1); - parent()->insert_value(position(), value_type()); - value_swap(count(), parent(), position()); - value_destroy(count()); - parent()->set_child(position() + 1, dest); - - if (!leaf()) { - for (int i = 0; i <= dest->count(); ++i) { - assert(child(count() + i + 1) != NULL); - dest->set_child(i, child(count() + i + 1)); - *mutable_child(count() + i + 1) = NULL; - } - } -} - -template -void btree_node

::merge(btree_node *src) { - assert(parent() == src->parent()); - assert(position() + 1 == src->position()); - - // Move the delimiting value to the left node. - value_init(count()); - value_swap(count(), parent(), position()); - - // Move the values from the right to the left node. - for (int i = 0; i < src->count(); ++i) { - value_init(1 + count() + i); - value_swap(1 + count() + i, src, i); - src->value_destroy(i); - } - - if (!leaf()) { - // Move the child pointers from the right to the left node. - for (int i = 0; i <= src->count(); ++i) { - set_child(1 + count() + i, src->child(i)); - *src->mutable_child(i) = NULL; - } - } - - // Fixup the counts on the src and dest nodes. - set_count(1 + count() + src->count()); - src->set_count(0); - - // Remove the value on the parent node. - parent()->remove_value(position()); -} - -template -void btree_node

::swap(btree_node *x) { - assert(leaf() == x->leaf()); - - // Swap the values. - for (int i = count(); i < x->count(); ++i) { - value_init(i); - } - for (int i = x->count(); i < count(); ++i) { - x->value_init(i); - } - int n = std::max(count(), x->count()); - for (int i = 0; i < n; ++i) { - value_swap(i, x, i); - } - for (int i = count(); i < x->count(); ++i) { - x->value_destroy(i); - } - for (int i = x->count(); i < count(); ++i) { - value_destroy(i); - } - - if (!leaf()) { - // Swap the child pointers. - for (int i = 0; i <= n; ++i) { - btree_swap_helper(*mutable_child(i), *x->mutable_child(i)); - } - for (int i = 0; i <= count(); ++i) { - x->child(i)->fields_.parent = x; - } - for (int i = 0; i <= x->count(); ++i) { - child(i)->fields_.parent = this; - } - } - - // Swap the counts. - btree_swap_helper(fields_.count, x->fields_.count); -} - -//// -// btree_iterator methods -template -void btree_iterator::increment_slow() { - if (node->leaf()) { - assert(position >= node->count()); - self_type save(*this); - while (position == node->count() && !node->is_root()) { - assert(node->parent()->child(node->position()) == node); - position = node->position(); - node = node->parent(); - } - if (position == node->count()) { - *this = save; - } - } else { - assert(position < node->count()); - node = node->child(position + 1); - while (!node->leaf()) { - node = node->child(0); - } - position = 0; - } -} - -template -void btree_iterator::increment_by(int count) { - while (count > 0) { - if (node->leaf()) { - int rest = node->count() - position; - position += std::min(rest, count); - count = count - rest; - if (position < node->count()) { - return; - } - } else { - --count; - } - increment_slow(); - } -} - -template -void btree_iterator::decrement_slow() { - if (node->leaf()) { - assert(position <= -1); - self_type save(*this); - while (position < 0 && !node->is_root()) { - assert(node->parent()->child(node->position()) == node); - position = node->position() - 1; - node = node->parent(); - } - if (position < 0) { - *this = save; - } - } else { - assert(position >= 0); - node = node->child(position); - while (!node->leaf()) { - node = node->child(node->count()); - } - position = node->count() - 1; - } -} - -//// -// btree methods -template -btree

::btree(const key_compare &comp, const allocator_type &alloc) - : key_compare(comp), - root_(alloc, NULL) { -} - -template -btree

::btree(const self_type &x) - : key_compare(x.key_comp()), - root_(x.internal_allocator(), NULL) { - assign(x); -} - -template template -std::pair::iterator, bool> -btree

::insert_unique(const key_type &key, ValuePointer value) { - if (empty()) { - *mutable_root() = new_leaf_root_node(1); - } - - std::pair res = internal_locate(key, iterator(root(), 0)); - iterator &iter = res.first; - if (res.second == kExactMatch) { - // The key already exists in the tree, do nothing. - return std::make_pair(internal_last(iter), false); - } else if (!res.second) { - iterator last = internal_last(iter); - if (last.node && !compare_keys(key, last.key())) { - // The key already exists in the tree, do nothing. - return std::make_pair(last, false); - } - } - - return std::make_pair(internal_insert(iter, *value), true); -} - -template -inline typename btree

::iterator -btree

::insert_unique(iterator position, const value_type &v) { - if (!empty()) { - const key_type &key = params_type::key(v); - if (position == end() || compare_keys(key, position.key())) { - iterator prev = position; - if (position == begin() || compare_keys((--prev).key(), key)) { - // prev.key() < key < position.key() - return internal_insert(position, v); - } - } else if (compare_keys(position.key(), key)) { - iterator next = position; - ++next; - if (next == end() || compare_keys(key, next.key())) { - // position.key() < key < next.key() - return internal_insert(next, v); - } - } else { - // position.key() == key - return position; - } - } - return insert_unique(v).first; -} - -template template -void btree

::insert_unique(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert_unique(end(), *b); - } -} - -template template -typename btree

::iterator -btree

::insert_multi(const key_type &key, ValuePointer value) { - if (empty()) { - *mutable_root() = new_leaf_root_node(1); - } - - iterator iter = internal_upper_bound(key, iterator(root(), 0)); - if (!iter.node) { - iter = end(); - } - return internal_insert(iter, *value); -} - -template -typename btree

::iterator -btree

::insert_multi(iterator position, const value_type &v) { - if (!empty()) { - const key_type &key = params_type::key(v); - if (position == end() || !compare_keys(position.key(), key)) { - iterator prev = position; - if (position == begin() || !compare_keys(key, (--prev).key())) { - // prev.key() <= key <= position.key() - return internal_insert(position, v); - } - } else { - iterator next = position; - ++next; - if (next == end() || !compare_keys(next.key(), key)) { - // position.key() < key <= next.key() - return internal_insert(next, v); - } - } - } - return insert_multi(v); -} - -template template -void btree

::insert_multi(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert_multi(end(), *b); - } -} - -template -void btree

::assign(const self_type &x) { - clear(); - - *mutable_key_comp() = x.key_comp(); - *mutable_internal_allocator() = x.internal_allocator(); - - // Assignment can avoid key comparisons because we know the order of the - // values is the same order we'll store them in. - for (const_iterator iter = x.begin(); iter != x.end(); ++iter) { - if (empty()) { - insert_multi(*iter); - } else { - // If the btree is not empty, we can just insert the new value at the end - // of the tree! - internal_insert(end(), *iter); - } - } -} - -template -typename btree

::iterator btree

::erase(iterator iter) { - bool internal_delete = false; - if (!iter.node->leaf()) { - // Deletion of a value on an internal node. Swap the key with the largest - // value of our left child. This is easy, we just decrement iter. - iterator tmp_iter(iter--); - assert(iter.node->leaf()); - assert(!compare_keys(tmp_iter.key(), iter.key())); - iter.node->value_swap(iter.position, tmp_iter.node, tmp_iter.position); - internal_delete = true; - --*mutable_size(); - } else if (!root()->leaf()) { - --*mutable_size(); - } - - // Delete the key from the leaf. - iter.node->remove_value(iter.position); - - // We want to return the next value after the one we just erased. If we - // erased from an internal node (internal_delete == true), then the next - // value is ++(++iter). If we erased from a leaf node (internal_delete == - // false) then the next value is ++iter. Note that ++iter may point to an - // internal node and the value in the internal node may move to a leaf node - // (iter.node) when rebalancing is performed at the leaf level. - - // Merge/rebalance as we walk back up the tree. - iterator res(iter); - for (;;) { - if (iter.node == root()) { - try_shrink(); - if (empty()) { - return end(); - } - break; - } - if (iter.node->count() >= kMinNodeValues) { - break; - } - bool merged = try_merge_or_rebalance(&iter); - if (iter.node->leaf()) { - res = iter; - } - if (!merged) { - break; - } - iter.node = iter.node->parent(); - } - - // Adjust our return value. If we're pointing at the end of a node, advance - // the iterator. - if (res.position == res.node->count()) { - res.position = res.node->count() - 1; - ++res; - } - // If we erased from an internal node, advance the iterator. - if (internal_delete) { - ++res; - } - return res; -} - -template -int btree

::erase(iterator begin, iterator end) { - int count = distance(begin, end); - for (int i = 0; i < count; i++) { - begin = erase(begin); - } - return count; -} - -template -int btree

::erase_unique(const key_type &key) { - iterator iter = internal_find_unique(key, iterator(root(), 0)); - if (!iter.node) { - // The key doesn't exist in the tree, return nothing done. - return 0; - } - erase(iter); - return 1; -} - -template -int btree

::erase_multi(const key_type &key) { - iterator begin = internal_lower_bound(key, iterator(root(), 0)); - if (!begin.node) { - // The key doesn't exist in the tree, return nothing done. - return 0; - } - // Delete all of the keys between begin and upper_bound(key). - iterator end = internal_end( - internal_upper_bound(key, iterator(root(), 0))); - return erase(begin, end); -} - -template -void btree

::clear() { - if (root() != NULL) { - internal_clear(root()); - } - *mutable_root() = NULL; -} - -template -void btree

::swap(self_type &x) { - std::swap(static_cast(*this), static_cast(x)); - std::swap(root_, x.root_); -} - -template -void btree

::verify() const { - if (root() != NULL) { - assert(size() == internal_verify(root(), NULL, NULL)); - assert(leftmost() == (++const_iterator(root(), -1)).node); - assert(rightmost() == (--const_iterator(root(), root()->count())).node); - assert(leftmost()->leaf()); - assert(rightmost()->leaf()); - } else { - assert(size() == 0); - assert(leftmost() == NULL); - assert(rightmost() == NULL); - } -} - -template -void btree

::rebalance_or_split(iterator *iter) { - node_type *&node = iter->node; - int &insert_position = iter->position; - assert(node->count() == node->max_count()); - - // First try to make room on the node by rebalancing. - node_type *parent = node->parent(); - if (node != root()) { - if (node->position() > 0) { - // Try rebalancing with our left sibling. - node_type *left = parent->child(node->position() - 1); - if (left->count() < left->max_count()) { - // We bias rebalancing based on the position being inserted. If we're - // inserting at the end of the right node then we bias rebalancing to - // fill up the left node. - int to_move = (left->max_count() - left->count()) / - (1 + (insert_position < left->max_count())); - to_move = std::max(1, to_move); - - if (((insert_position - to_move) >= 0) || - ((left->count() + to_move) < left->max_count())) { - left->rebalance_right_to_left(node, to_move); - - assert(node->max_count() - node->count() == to_move); - insert_position = insert_position - to_move; - if (insert_position < 0) { - insert_position = insert_position + left->count() + 1; - node = left; - } - - assert(node->count() < node->max_count()); - return; - } - } - } - - if (node->position() < parent->count()) { - // Try rebalancing with our right sibling. - node_type *right = parent->child(node->position() + 1); - if (right->count() < right->max_count()) { - // We bias rebalancing based on the position being inserted. If we're - // inserting at the beginning of the left node then we bias rebalancing - // to fill up the right node. - int to_move = (right->max_count() - right->count()) / - (1 + (insert_position > 0)); - to_move = std::max(1, to_move); - - if ((insert_position <= (node->count() - to_move)) || - ((right->count() + to_move) < right->max_count())) { - node->rebalance_left_to_right(right, to_move); - - if (insert_position > node->count()) { - insert_position = insert_position - node->count() - 1; - node = right; - } - - assert(node->count() < node->max_count()); - return; - } - } - } - - // Rebalancing failed, make sure there is room on the parent node for a new - // value. - if (parent->count() == parent->max_count()) { - iterator parent_iter(node->parent(), node->position()); - rebalance_or_split(&parent_iter); - } - } else { - // Rebalancing not possible because this is the root node. - if (root()->leaf()) { - // The root node is currently a leaf node: create a new root node and set - // the current root node as the child of the new root. - parent = new_internal_root_node(); - parent->set_child(0, root()); - *mutable_root() = parent; - assert(*mutable_rightmost() == parent->child(0)); - } else { - // The root node is an internal node. We do not want to create a new root - // node because the root node is special and holds the size of the tree - // and a pointer to the rightmost node. So we create a new internal node - // and move all of the items on the current root into the new node. - parent = new_internal_node(parent); - parent->set_child(0, parent); - parent->swap(root()); - node = parent; - } - } - - // Split the node. - node_type *split_node; - if (node->leaf()) { - split_node = new_leaf_node(parent); - node->split(split_node, insert_position); - if (rightmost() == node) { - *mutable_rightmost() = split_node; - } - } else { - split_node = new_internal_node(parent); - node->split(split_node, insert_position); - } - - if (insert_position > node->count()) { - insert_position = insert_position - node->count() - 1; - node = split_node; - } -} - -template -void btree

::merge_nodes(node_type *left, node_type *right) { - left->merge(right); - if (right->leaf()) { - if (rightmost() == right) { - *mutable_rightmost() = left; - } - delete_leaf_node(right); - } else { - delete_internal_node(right); - } -} - -template -bool btree

::try_merge_or_rebalance(iterator *iter) { - node_type *parent = iter->node->parent(); - if (iter->node->position() > 0) { - // Try merging with our left sibling. - node_type *left = parent->child(iter->node->position() - 1); - if ((1 + left->count() + iter->node->count()) <= left->max_count()) { - iter->position += 1 + left->count(); - merge_nodes(left, iter->node); - iter->node = left; - return true; - } - } - if (iter->node->position() < parent->count()) { - // Try merging with our right sibling. - node_type *right = parent->child(iter->node->position() + 1); - if ((1 + iter->node->count() + right->count()) <= right->max_count()) { - merge_nodes(iter->node, right); - return true; - } - // Try rebalancing with our right sibling. We don't perform rebalancing if - // we deleted the first element from iter->node and the node is not - // empty. This is a small optimization for the common pattern of deleting - // from the front of the tree. - if ((right->count() > kMinNodeValues) && - ((iter->node->count() == 0) || - (iter->position > 0))) { - int to_move = (right->count() - iter->node->count()) / 2; - to_move = std::min(to_move, right->count() - 1); - iter->node->rebalance_right_to_left(right, to_move); - return false; - } - } - if (iter->node->position() > 0) { - // Try rebalancing with our left sibling. We don't perform rebalancing if - // we deleted the last element from iter->node and the node is not - // empty. This is a small optimization for the common pattern of deleting - // from the back of the tree. - node_type *left = parent->child(iter->node->position() - 1); - if ((left->count() > kMinNodeValues) && - ((iter->node->count() == 0) || - (iter->position < iter->node->count()))) { - int to_move = (left->count() - iter->node->count()) / 2; - to_move = std::min(to_move, left->count() - 1); - left->rebalance_left_to_right(iter->node, to_move); - iter->position += to_move; - return false; - } - } - return false; -} - -template -void btree

::try_shrink() { - if (root()->count() > 0) { - return; - } - // Deleted the last item on the root node, shrink the height of the tree. - if (root()->leaf()) { - assert(size() == 0); - delete_leaf_node(root()); - *mutable_root() = NULL; - } else { - node_type *child = root()->child(0); - if (child->leaf()) { - // The child is a leaf node so simply make it the root node in the tree. - child->make_root(); - delete_internal_root_node(); - *mutable_root() = child; - } else { - // The child is an internal node. We want to keep the existing root node - // so we move all of the values from the child node into the existing - // (empty) root node. - child->swap(root()); - delete_internal_node(child); - } - } -} - -template template -inline IterType btree

::internal_last(IterType iter) { - while (iter.node && iter.position == iter.node->count()) { - iter.position = iter.node->position(); - iter.node = iter.node->parent(); - if (iter.node->leaf()) { - iter.node = NULL; - } - } - return iter; -} - -template -inline typename btree

::iterator -btree

::internal_insert(iterator iter, const value_type &v) { - if (!iter.node->leaf()) { - // We can't insert on an internal node. Instead, we'll insert after the - // previous value which is guaranteed to be on a leaf node. - --iter; - ++iter.position; - } - if (iter.node->count() == iter.node->max_count()) { - // Make room in the leaf for the new item. - if (iter.node->max_count() < kNodeValues) { - // Insertion into the root where the root is smaller that the full node - // size. Simply grow the size of the root node. - assert(iter.node == root()); - iter.node = new_leaf_root_node( - std::min(kNodeValues, 2 * iter.node->max_count())); - iter.node->swap(root()); - delete_leaf_node(root()); - *mutable_root() = iter.node; - } else { - rebalance_or_split(&iter); - ++*mutable_size(); - } - } else if (!root()->leaf()) { - ++*mutable_size(); - } - iter.node->insert_value(iter.position, v); - return iter; -} - -template template -inline std::pair btree

::internal_locate( - const key_type &key, IterType iter) const { - return internal_locate_type::dispatch(key, *this, iter); -} - -template template -inline std::pair btree

::internal_locate_plain_compare( - const key_type &key, IterType iter) const { - for (;;) { - iter.position = iter.node->lower_bound(key, key_comp()); - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - return std::make_pair(iter, 0); -} - -template template -inline std::pair btree

::internal_locate_compare_to( - const key_type &key, IterType iter) const { - for (;;) { - int res = iter.node->lower_bound(key, key_comp()); - iter.position = res & kMatchMask; - if (res & kExactMatch) { - return std::make_pair(iter, static_cast(kExactMatch)); - } - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - return std::make_pair(iter, -kExactMatch); -} - -template template -IterType btree

::internal_lower_bound( - const key_type &key, IterType iter) const { - if (iter.node) { - for (;;) { - iter.position = - iter.node->lower_bound(key, key_comp()) & kMatchMask; - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - iter = internal_last(iter); - } - return iter; -} - -template template -IterType btree

::internal_upper_bound( - const key_type &key, IterType iter) const { - if (iter.node) { - for (;;) { - iter.position = iter.node->upper_bound(key, key_comp()); - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - iter = internal_last(iter); - } - return iter; -} - -template template -IterType btree

::internal_find_unique( - const key_type &key, IterType iter) const { - if (iter.node) { - std::pair res = internal_locate(key, iter); - if (res.second == kExactMatch) { - return res.first; - } - if (!res.second) { - iter = internal_last(res.first); - if (iter.node && !compare_keys(key, iter.key())) { - return iter; - } - } - } - return IterType(NULL, 0); -} - -template template -IterType btree

::internal_find_multi( - const key_type &key, IterType iter) const { - if (iter.node) { - iter = internal_lower_bound(key, iter); - if (iter.node) { - iter = internal_last(iter); - if (iter.node && !compare_keys(key, iter.key())) { - return iter; - } - } - } - return IterType(NULL, 0); -} - -template -void btree

::internal_clear(node_type *node) { - if (!node->leaf()) { - for (int i = 0; i <= node->count(); ++i) { - internal_clear(node->child(i)); - } - if (node == root()) { - delete_internal_root_node(); - } else { - delete_internal_node(node); - } - } else { - delete_leaf_node(node); - } -} - -template -void btree

::internal_dump( - std::ostream &os, const node_type *node, int level) const { - for (int i = 0; i < node->count(); ++i) { - if (!node->leaf()) { - internal_dump(os, node->child(i), level + 1); - } - for (int j = 0; j < level; ++j) { - os << " "; - } - os << node->key(i) << " [" << level << "]\n"; - } - if (!node->leaf()) { - internal_dump(os, node->child(node->count()), level + 1); - } -} - -template -int btree

::internal_verify( - const node_type *node, const key_type *lo, const key_type *hi) const { - assert(node->count() > 0); - assert(node->count() <= node->max_count()); - if (lo) { - assert(!compare_keys(node->key(0), *lo)); - } - if (hi) { - assert(!compare_keys(*hi, node->key(node->count() - 1))); - } - for (int i = 1; i < node->count(); ++i) { - assert(!compare_keys(node->key(i), node->key(i - 1))); - } - int count = node->count(); - if (!node->leaf()) { - for (int i = 0; i <= node->count(); ++i) { - assert(node->child(i) != NULL); - assert(node->child(i)->parent() == node); - assert(node->child(i)->position() == i); - count += internal_verify( - node->child(i), - (i == 0) ? lo : &node->key(i - 1), - (i == node->count()) ? hi : &node->key(i)); - } - } - return count; -} - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_H__ diff --git a/c_src/gb_lru/btree_container.h b/c_src/gb_lru/btree_container.h deleted file mode 100644 index fb617ab..0000000 --- a/c_src/gb_lru/btree_container.h +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef UTIL_BTREE_BTREE_CONTAINER_H__ -#define UTIL_BTREE_BTREE_CONTAINER_H__ - -#include -#include - -#include "btree.h" - -namespace btree { - -// A common base class for btree_set, btree_map, btree_multiset and -// btree_multimap. -template -class btree_container { - typedef btree_container self_type; - - public: - typedef typename Tree::params_type params_type; - typedef typename Tree::key_type key_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - typedef typename Tree::pointer pointer; - typedef typename Tree::const_pointer const_pointer; - typedef typename Tree::reference reference; - typedef typename Tree::const_reference const_reference; - typedef typename Tree::size_type size_type; - typedef typename Tree::difference_type difference_type; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - typedef typename Tree::reverse_iterator reverse_iterator; - typedef typename Tree::const_reverse_iterator const_reverse_iterator; - - public: - // Default constructor. - btree_container(const key_compare &comp, const allocator_type &alloc) - : tree_(comp, alloc) { - } - - // Copy constructor. - btree_container(const self_type &x) - : tree_(x.tree_) { - } - - // Iterator routines. - iterator begin() { return tree_.begin(); } - const_iterator begin() const { return tree_.begin(); } - iterator end() { return tree_.end(); } - const_iterator end() const { return tree_.end(); } - reverse_iterator rbegin() { return tree_.rbegin(); } - const_reverse_iterator rbegin() const { return tree_.rbegin(); } - reverse_iterator rend() { return tree_.rend(); } - const_reverse_iterator rend() const { return tree_.rend(); } - - // Lookup routines. - iterator lower_bound(const key_type &key) { - return tree_.lower_bound(key); - } - const_iterator lower_bound(const key_type &key) const { - return tree_.lower_bound(key); - } - iterator upper_bound(const key_type &key) { - return tree_.upper_bound(key); - } - const_iterator upper_bound(const key_type &key) const { - return tree_.upper_bound(key); - } - std::pair equal_range(const key_type &key) { - return tree_.equal_range(key); - } - std::pair equal_range(const key_type &key) const { - return tree_.equal_range(key); - } - - // Utility routines. - void clear() { - tree_.clear(); - } - void swap(self_type &x) { - tree_.swap(x.tree_); - } - void dump(std::ostream &os) const { - tree_.dump(os); - } - void verify() const { - tree_.verify(); - } - - // Size routines. - size_type size() const { return tree_.size(); } - size_type max_size() const { return tree_.max_size(); } - bool empty() const { return tree_.empty(); } - size_type height() const { return tree_.height(); } - size_type internal_nodes() const { return tree_.internal_nodes(); } - size_type leaf_nodes() const { return tree_.leaf_nodes(); } - size_type nodes() const { return tree_.nodes(); } - size_type bytes_used() const { return tree_.bytes_used(); } - static double average_bytes_per_value() { - return Tree::average_bytes_per_value(); - } - double fullness() const { return tree_.fullness(); } - double overhead() const { return tree_.overhead(); } - - bool operator==(const self_type& x) const { - if (size() != x.size()) { - return false; - } - for (const_iterator i = begin(), xi = x.begin(); i != end(); ++i, ++xi) { - if (*i != *xi) { - return false; - } - } - return true; - } - - bool operator!=(const self_type& other) const { - return !operator==(other); - } - - - protected: - Tree tree_; -}; - -template -inline std::ostream& operator<<(std::ostream &os, const btree_container &b) { - b.dump(os); - return os; -} - -// A common base class for btree_set and safe_btree_set. -template -class btree_unique_container : public btree_container { - typedef btree_unique_container self_type; - typedef btree_container super_type; - - public: - typedef typename Tree::key_type key_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::size_type size_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - - public: - // Default constructor. - btree_unique_container(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_unique_container(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_unique_container(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - insert(b, e); - } - - // Lookup routines. - iterator find(const key_type &key) { - return this->tree_.find_unique(key); - } - const_iterator find(const key_type &key) const { - return this->tree_.find_unique(key); - } - size_type count(const key_type &key) const { - return this->tree_.count_unique(key); - } - - // Insertion routines. - std::pair insert(const value_type &x) { - return this->tree_.insert_unique(x); - } - iterator insert(iterator position, const value_type &x) { - return this->tree_.insert_unique(position, x); - } - template - void insert(InputIterator b, InputIterator e) { - this->tree_.insert_unique(b, e); - } - - // Deletion routines. - int erase(const key_type &key) { - return this->tree_.erase_unique(key); - } - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(const iterator &iter) { - return this->tree_.erase(iter); - } - void erase(const iterator &first, const iterator &last) { - this->tree_.erase(first, last); - } -}; - -// A common base class for btree_map and safe_btree_map. -template -class btree_map_container : public btree_unique_container { - typedef btree_map_container self_type; - typedef btree_unique_container super_type; - - public: - typedef typename Tree::key_type key_type; - typedef typename Tree::data_type data_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::mapped_type mapped_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - - private: - // A pointer-like object which only generates its value when - // dereferenced. Used by operator[] to avoid constructing an empty data_type - // if the key already exists in the map. - struct generate_value { - generate_value(const key_type &k) - : key(k) { - } - value_type operator*() const { - return std::make_pair(key, data_type()); - } - const key_type &key; - }; - - public: - // Default constructor. - btree_map_container(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_map_container(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_map_container(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } - - // Insertion routines. - data_type& operator[](const key_type &key) { - return this->tree_.insert_unique(key, generate_value(key)).first->second; - } -}; - -// A common base class for btree_multiset and btree_multimap. -template -class btree_multi_container : public btree_container { - typedef btree_multi_container self_type; - typedef btree_container super_type; - - public: - typedef typename Tree::key_type key_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::size_type size_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - - public: - // Default constructor. - btree_multi_container(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_multi_container(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_multi_container(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - insert(b, e); - } - - // Lookup routines. - iterator find(const key_type &key) { - return this->tree_.find_multi(key); - } - const_iterator find(const key_type &key) const { - return this->tree_.find_multi(key); - } - size_type count(const key_type &key) const { - return this->tree_.count_multi(key); - } - - // Insertion routines. - iterator insert(const value_type &x) { - return this->tree_.insert_multi(x); - } - iterator insert(iterator position, const value_type &x) { - return this->tree_.insert_multi(position, x); - } - template - void insert(InputIterator b, InputIterator e) { - this->tree_.insert_multi(b, e); - } - - // Deletion routines. - int erase(const key_type &key) { - return this->tree_.erase_multi(key); - } - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(const iterator &iter) { - return this->tree_.erase(iter); - } - void erase(const iterator &first, const iterator &last) { - this->tree_.erase(first, last); - } -}; - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_CONTAINER_H__ diff --git a/c_src/gb_lru/btree_map.h b/c_src/gb_lru/btree_map.h deleted file mode 100644 index b83489f..0000000 --- a/c_src/gb_lru/btree_map.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A btree_map<> implements the STL unique sorted associative container -// interface and the pair associative container interface (a.k.a map<>) using a -// btree. A btree_multimap<> implements the STL multiple sorted associative -// container interface and the pair associtive container interface (a.k.a -// multimap<>) using a btree. See btree.h for details of the btree -// implementation and caveats. - -#ifndef UTIL_BTREE_BTREE_MAP_H__ -#define UTIL_BTREE_BTREE_MAP_H__ - -#include -#include -#include -#include -#include - -#include "btree.h" -#include "btree_container.h" - -namespace btree { - -// The btree_map class is needed mainly for its constructors. -template , - typename Alloc = std::allocator >, - int TargetNodeSize = 256> -class btree_map : public btree_map_container< - btree > > { - - typedef btree_map self_type; - typedef btree_map_params< - Key, Value, Compare, Alloc, TargetNodeSize> params_type; - typedef btree btree_type; - typedef btree_map_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - - public: - // Default constructor. - btree_map(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_map(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_map(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(btree_map &x, - btree_map &y) { - x.swap(y); -} - -// The btree_multimap class is needed mainly for its constructors. -template , - typename Alloc = std::allocator >, - int TargetNodeSize = 256> -class btree_multimap : public btree_multi_container< - btree > > { - - typedef btree_multimap self_type; - typedef btree_map_params< - Key, Value, Compare, Alloc, TargetNodeSize> params_type; - typedef btree btree_type; - typedef btree_multi_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - typedef typename btree_type::data_type data_type; - typedef typename btree_type::mapped_type mapped_type; - - public: - // Default constructor. - btree_multimap(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_multimap(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_multimap(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(btree_multimap &x, - btree_multimap &y) { - x.swap(y); -} - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_MAP_H__ diff --git a/c_src/gb_lru/btreelru_nif.cpp b/c_src/gb_lru/btreelru_nif.cpp deleted file mode 100644 index ce0712d..0000000 --- a/c_src/gb_lru/btreelru_nif.cpp +++ /dev/null @@ -1,619 +0,0 @@ -#include -#include -#include -#include "erl_nif.h" -#include "erlterm.h" -#include "lru.h" - - -using namespace std; - -namespace { /* anonymous namespace starts */ - -typedef struct _obj_resource { - bool allocated; - void *object; - ErlNifMutex *emtx; -} object_resource; - -ErlNifResourceFlags resource_flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); - -ErlNifResourceType* lruResource; -ErlNifResourceType* iteratorResource; - -/* atoms */ -ERL_NIF_TERM atom_ok; -ERL_NIF_TERM atom_key; -ERL_NIF_TERM atom_error; -ERL_NIF_TERM atom_invalid; -ERL_NIF_TERM atom_value; -ERL_NIF_TERM atom_max_size; -ERL_NIF_TERM atom_tab; -ERL_NIF_TERM atom_lru_old; - -void lru_dtor(ErlNifEnv* env, void *lru); -void iterator_dtor(ErlNifEnv* env, void *it); - -int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info){ - lruResource = enif_open_resource_type(env, - "btreelru_nif", - "lru", - lru_dtor, - resource_flags, - NULL); - - iteratorResource = enif_open_resource_type(env, - "btreelru_nif", - "iterator", - iterator_dtor, - resource_flags, - NULL); - - atom_ok = enif_make_atom(env, "ok"); - atom_key = enif_make_atom(env, "key"); - atom_error = enif_make_atom(env, "error"); - atom_invalid = enif_make_atom(env, "invalid"); - atom_value = enif_make_atom(env, "value"); - atom_max_size = enif_make_atom(env, "max_size"); - atom_tab = enif_make_atom(env, "tab"); - atom_lru_old = enif_make_atom(env, "lru_old"); - - return 0; -} - -int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info){ - return 0; -} - -int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,ERL_NIF_TERM load_info){ - return 0; -} - -void lru_dtor(ErlNifEnv* env, void* _lru_btree) { - object_resource *lru_btree = (object_resource*) _lru_btree; - if (lru_btree->allocated) - delete (LRUBtree*) lru_btree->object; -} - -void iterator_dtor(ErlNifEnv* env, void* _lru_iterator) { - object_resource *lru_iterator = (object_resource*) _lru_iterator; - if (lru_iterator->allocated) - delete (LRUBtree::iterator*) lru_iterator->object; -} - -void node_free(LRUBtree *bt_lru, LRUNode *node) { - enif_free_env((ErlNifEnv*)node->kvenv); - return; -} - -void node_kickout(LRUBtree *bt_lru, LRUNode *node, void *currenv) { - ErlNifEnv *env = (ErlNifEnv *) currenv; - - if (bt_lru->pid_set) { - enif_send(env, &bt_lru->pid, NULL, enif_make_tuple3(env, atom_lru_old, node->key.t, node->data.t)); - } - - return; -} - -ERL_NIF_TERM next(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ErlTerm key; - ErlTerm value; - - if (argc != 2) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - key.t = argv[1]; - node = bt_lru->get(key); - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - node = node->next; - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - key.t = enif_make_copy(env, node->key.t); - value.t = enif_make_copy(env, node->data.t); - - return enif_make_tuple2(env, key.t, value.t); -} - -ERL_NIF_TERM prev(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ErlTerm key; - ErlTerm value; - - if (argc != 2) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - key.t = argv[1]; - node = bt_lru->get(key); - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - node = node->prev; - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - key.t = enif_make_copy(env, node->key.t); - value.t = enif_make_copy(env, node->data.t); - - return enif_make_tuple2(env, key.t, value.t); -} - - -ERL_NIF_TERM create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - unsigned long max_size; - object_resource *lru; - LRUBtree *bt_lru; - ERL_NIF_TERM lru_term; - - /* get max_size */ - if (enif_get_ulong(env, argv[0], &max_size) < 1){ - return enif_make_tuple2(env, atom_error, atom_max_size); - } - - if (!(bt_lru = new LRUBtree(max_size, node_free, node_kickout))) { - return enif_make_tuple2(env, atom_error, enif_make_atom(env, "alloction")); - } - - lru = (object_resource *) enif_alloc_resource(lruResource, sizeof(object_resource)); - lru->object = bt_lru; - lru->allocated = true; - - lru_term = enif_make_resource(env, lru); - enif_release_resource(lru); - - return enif_make_tuple2(env, atom_ok, lru_term); - -} - -ERL_NIF_TERM seek(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - object_resource *it; - LRUBtree *bt_lru; - LRUBtree::iterator *bt_it_; - LRUBtree::iterator bt_it; - ErlTerm key; - ERL_NIF_TERM it_term; - ERL_NIF_TERM kv; - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - key.t = argv[1]; - - bt_lru = (LRUBtree *)lru->object; - - bt_it = bt_lru->bmap.lower_bound(key); - if ( bt_it == bt_lru->bmap.end() ) { - return enif_make_tuple2(env, atom_error, atom_invalid); - } - - - bt_it_ = new LRUBtree::iterator; - *bt_it_ = bt_it; - it = (object_resource *) enif_alloc_resource(iteratorResource, sizeof(object_resource)); - it->object = bt_it_; - it->allocated = true; - - it_term = enif_make_resource(env, it); - enif_release_resource(it); - kv = enif_make_tuple2(env, bt_it->second->key.t, bt_it->second->data.t); - return enif_make_tuple2(env, kv, it_term); - -} - -ERL_NIF_TERM iterate_next(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - object_resource *it; - LRUBtree::iterator *bt_it_; - LRUBtree *bt_lru; - ERL_NIF_TERM kv; - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[1], iteratorResource, (void **) &it)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *)lru->object; - bt_it_ = (LRUBtree::iterator *) it->object; - - if (bt_it_ == NULL) - return enif_make_tuple2(env, atom_error, atom_invalid); - - (*bt_it_)++; - - if ( *bt_it_ == bt_lru->bmap.end() ) { - it->allocated = false; - delete bt_it_; - it->object = NULL; - return enif_make_tuple2(env, atom_error, atom_invalid); - } - - kv = enif_make_tuple2(env, (*bt_it_)->second->key.t, (*bt_it_)->second->data.t); - return enif_make_tuple2(env, atom_ok, kv); -} - -ERL_NIF_TERM close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *)lru->object; - lru->allocated = false; - delete bt_lru; - - - return atom_ok; -} - -ERL_NIF_TERM read(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ErlTerm key; - ERL_NIF_TERM kv; - - if (argc != 2) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - key.t = argv[1]; - node = bt_lru->get(key); - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - kv = enif_make_tuple2(env, enif_make_copy(env, node->key.t), enif_make_copy(env, node->data.t)); - - return enif_make_tuple2(env, atom_ok, kv); -} - -ERL_NIF_TERM remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - ErlTerm key; - - if (argc != 2) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - key.t = argv[1]; - bt_lru->erase(key); - - return atom_ok; -} - -ERL_NIF_TERM oldest(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ERL_NIF_TERM key; - ERL_NIF_TERM value; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - node = bt_lru->getOldest(); - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - key = enif_make_copy(env, node->key.t); - value = enif_make_copy(env, node->data.t); - - return enif_make_tuple2(env, key, value); -} - -ERL_NIF_TERM latest(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ERL_NIF_TERM key; - ERL_NIF_TERM value; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - // last is "last in" in the lru - node = bt_lru->getLatest(); - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - key = enif_make_copy(env, node->key.t); - value = enif_make_copy(env, node->data.t); - - return enif_make_tuple2(env, key, value); -} - -ERL_NIF_TERM last(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ERL_NIF_TERM key; - ERL_NIF_TERM value; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - node = bt_lru->bmap.rbegin()->second; - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - key = enif_make_copy(env, node->key.t); - value = enif_make_copy(env, node->data.t); - - return enif_make_tuple2(env, key, value); -} - -ERL_NIF_TERM first(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - LRUNode *node; - ERL_NIF_TERM key; - ERL_NIF_TERM value; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - node = bt_lru->bmap.begin()->second; - - if (!node) - return enif_make_tuple2(env, atom_error, atom_invalid); - - key = enif_make_copy(env, node->key.t); - value = enif_make_copy(env, node->data.t); - - return enif_make_tuple2(env, key, value); -} - -ERL_NIF_TERM write(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - ErlTerm key; - ErlTerm value; - ErlNifEnv *kv_env; - size_t size; - - if (argc != 3) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - - bt_lru = (LRUBtree *) lru->object; - - kv_env = enif_alloc_env(); - key.t = enif_make_copy(kv_env, argv[1]); - value.t = enif_make_copy(kv_env, argv[2]); - - /* do not use the size of term - size = enif_size_term(key.t); - size += enif_size_term(value.t); - */ - - /* size based on entries */ - size = 1; - - bt_lru->put(key, value, kv_env, env, size); - - return atom_ok; -} - -ERL_NIF_TERM register_pid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - - if (argc != 2) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - bt_lru = (LRUBtree *) lru->object; - - if (!enif_get_local_pid(env, argv[1], &(bt_lru->pid))) { - return enif_make_badarg(env); - } - bt_lru->pid_set = true; - - return atom_ok; -} - -ERL_NIF_TERM unregister_pid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - bt_lru = (LRUBtree *) lru->object; - - bt_lru->pid_set = false; - - return atom_ok; -} - -ERL_NIF_TERM get_registered_pid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - bt_lru = (LRUBtree *) lru->object; - - if (!bt_lru->pid_set) { - return enif_make_tuple2(env, atom_error, atom_invalid); - } - - return enif_make_pid(env, &(bt_lru->pid)); -} - -ERL_NIF_TERM get_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - bt_lru = (LRUBtree *) lru->object; - - return enif_make_ulong(env, bt_lru->getSize()); -} - -ERL_NIF_TERM get_max_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - LRUBtree *bt_lru; - - if (argc != 1) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - bt_lru = (LRUBtree *) lru->object; - - return enif_make_ulong(env, bt_lru->getMaxSize()); -} - -ERL_NIF_TERM set_max_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - object_resource *lru; - unsigned long max_size; - LRUBtree *bt_lru; - - if (argc != 2) { - return enif_make_badarg(env); - } - - if (!enif_get_resource(env, argv[0], lruResource, (void **) &lru)) { - return enif_make_badarg(env); - } - /* get max_size */ - if (enif_get_ulong(env, argv[1], &max_size) < 1){ - return enif_make_tuple2(env, atom_error, atom_max_size); - } - - bt_lru = (LRUBtree *) lru->object; - - bt_lru->setMaxSize(max_size); - - return atom_ok; -} - -ErlNifFunc nif_funcs[] = { - {"create", 1, create}, - {"close", 1, close, ERL_NIF_DIRTY_JOB_IO_BOUND}, - {"register_pid", 2, register_pid}, - {"unregister_pid", 1, unregister_pid}, - {"get_registered_pid", 1, get_registered_pid}, - {"get_size", 1, get_size}, - {"get_max_size", 1, get_max_size}, - {"set_max_size", 2, set_max_size}, - {"oldest", 1, oldest}, - {"latest", 1, latest}, - {"last", 1, last}, - {"first", 1, first}, - {"read", 2, read}, - {"next", 2, next}, - {"prev", 2, prev}, - {"seek", 2, seek}, - {"iterate_next", 2, iterate_next}, - {"remove", 2, remove}, - {"write", 3, write} -}; -} /* anonymouse namespace ends */ - - -ERL_NIF_INIT(btree_lru, nif_funcs, load, reload, upgrade, NULL) diff --git a/c_src/gb_lru/erlterm.h b/c_src/gb_lru/erlterm.h deleted file mode 100644 index 42627c8..0000000 --- a/c_src/gb_lru/erlterm.h +++ /dev/null @@ -1,71 +0,0 @@ -#include "erl_nif.h" - -class ErlTerm { - public: - ERL_NIF_TERM t; - - static void *operator new(size_t size) { - return enif_alloc(size); - } - - static void operator delete(void *block) { - enif_free(block); - } - - bool operator< (const ErlTerm &term) { - if (enif_compare(t, term.t) < 0) - return true; - return false; - } - - bool operator< (ErlTerm &term) { - if (enif_compare(t, term.t) < 0) - return true; - return false; - } - - bool operator> (const ErlTerm &term) { - if (enif_compare(t, term.t) > 0) - return true; - return false; - } - - bool operator> (ErlTerm &term) { - if (enif_compare(t, term.t) > 0) - return true; - return false; - } - - bool operator== (const ErlTerm &term) { - if (enif_compare(t, term.t) == 0) - return true; - return false; - } - - bool operator== (ErlTerm &term) { - if (enif_compare(t, term.t) == 0) - return true; - return false; - } -}; - -inline bool operator < (const ErlTerm &a, const ErlTerm &b) { - if (enif_compare(a.t, b.t) < 0) - return true; - return false; -} - - -#if 0 -// extend std::hash to understand ErlTerm used by hashmap not btree -namespace std { - template <> - struct hash - { - size_t operator()(const ErlTerm& term) const - { - return (size_t) enif_hash_term(term.t); - } - }; -} -#endif diff --git a/c_src/gb_lru/lru.h b/c_src/gb_lru/lru.h deleted file mode 100644 index 47567e4..0000000 --- a/c_src/gb_lru/lru.h +++ /dev/null @@ -1,266 +0,0 @@ -#include "btree_map.h" -#include -#include -#include "murmurhash2.h" -#include "binary.h" -#include "erl_nif.h" - -// extend std::hash to understand Binary type -namespace std { - template <> - struct hash - { - size_t operator()(const Binary& b) const - { - return MurmurHash2(b.bin, b.size, 4242); - } - }; -} - -template -struct LRUNode -{ - K key; - V data; - void *kvenv; - LRUNode *prev; - LRUNode *next; - size_t size; - LRUNode(void *kvenv = NULL, size_t size=0) : kvenv(kvenv), prev(NULL), next(NULL), size(size) { } - -/* - static void *LRUNode::operator new(size_t size) { - return enif_alloc(size); - } - - static void operator delete(void *block) { - enif_free(block); - } -*/ - - void printChain() { - LRUNode* node; - int i=11; - std::cout << "("; - for(node = this; node && i; node = node->next, i--) { - std::cout << node->key << " -> "; - } - if (node) { - std::cout << " loop detection end "; - } else { - std::cout << " end "; - } - std::cout << ")" << std::endl; - } - - void printNextPrevKey() { - std::cout << "("; - printNextKey(); - printPrevKey(); - std::cout << ")"; - } - - void printNextKey() { - if (next) { - std::cout << "next key " << next->key << " "; - } - } - - void printPrevKey() { - if (prev) { - std::cout << "prev key " << prev->key << " "; - } - } -}; - -template -class LRUBtree { - private: - LRUNode *oldest; - LRUNode *latest; - unsigned long size; - unsigned long max_size; - void (*node_free)(LRUBtree *lru, LRUNode *node); - void (*node_kickout)(LRUBtree *lru, LRUNode *node, void *call_env); - typedef btree::btree_map*> LRUBtree_map; - - public: - LRUBtree_map bmap; - bool pid_set = false; - ErlNifPid pid; - typedef typename LRUBtree_map::iterator iterator; - typedef typename LRUBtree_map::reverse_iterator reverse_iterator; - - void printLatest() { - if (latest) { - std::cout << " latest " << latest->key; - } else { - std::cout << " no data in lru "; - } - } - - private: - LRUNode* erase(LRUNode *node) { - if (node->next) { - node->next->prev = node->prev; - } - if (node->prev) { - node->prev->next = node->next; - } - - if (node == oldest) { - oldest = node->prev; - } - - if (node == latest) { - latest = node->next; - } - - if (node_free) { - node_free(this, node); - } - - node->next = NULL; - node->prev = NULL; - return node; - } - - void printOldest() { - if(oldest) { - std::cout << " oldest " << oldest->key; - } else { - std::cout << " no data in lru "; - } - } - - void check_size(void *call_env) { - if (size > max_size) { - if (oldest) { // remove check if oldest exist and rely on max_size always being positive - if (node_kickout) - node_kickout(this, oldest, call_env); - erase(oldest->key); - } - } - } - -#define SIZE_100MB 100*1024*1024 - public: - LRUBtree(unsigned long max_size = SIZE_100MB, - void (*node_free)(LRUBtree *lru, LRUNode *node) = NULL, - void (*node_kickout)(LRUBtree *lru, LRUNode *node, void *call_env) = NULL) - : oldest(NULL), latest(NULL), size(0), max_size(max_size), node_free(node_free), - node_kickout(node_kickout) { } - - ~LRUBtree() { - LRUNode *node; - LRUNode *next; - node = latest; - while(node) { - if (node_free) { - node_free(this, node); - } - next = node->next; - delete node; - node = next; - } - } - - void printSize() { - std::cout << "size " << size << std::endl; - } - - unsigned long getSize() { - return size; - } - - unsigned long getMaxSize() { - return max_size; - } - - void setMaxSize(unsigned long max_size) { - this->max_size = max_size; - } - - void erase(K key) { - LRUNode *node; - if ((node = bmap[key])) { - erase(node); - bmap.erase(key); - size -= node->size; - delete node; - } - } - - inline void put(K key, V data, - void *kvenv = NULL, void *call_env = NULL, - size_t size = 1) { - LRUNode *node; - - this->size += size; - check_size(call_env); - - // overwrite already existing key - if ((node = bmap[key])) { - this->size -= node->size; - erase(node); - node->kvenv = kvenv; - node->next = latest; - node->size = size; - if (node->next) { - node->next->prev = node; - } - if (!oldest) { - oldest = node; - } - latest = node; - node->key = key; - node->data = data; - } - - else if (!oldest) { - node = new LRUNode; - node->key = key; - node->data = data; - node->kvenv = kvenv; - node->size = size; - oldest = node; - latest = node; - bmap[node->key] = node; - } - - else { - node = new LRUNode; - node->key = key; - node->data = data; - node->kvenv = kvenv; - node->size = size; - latest->prev = node; - node->next = latest; - latest = node; - bmap[node->key] = node; - } - } - - LRUNode* get(K key) { - return bmap[key]; - } - - LRUNode* getOldest() { - return oldest; - } - - LRUNode* getLatest() { - return latest; - } - - LRUNode* getNext(LRUNode *node) { - return node->next; - } - - LRUNode* getPrev(LRUNode *node) { - return node->prev; - - } -}; - - diff --git a/c_src/gb_lru/murmurhash2.h b/c_src/gb_lru/murmurhash2.h deleted file mode 100644 index 12542a5..0000000 --- a/c_src/gb_lru/murmurhash2.h +++ /dev/null @@ -1,73 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash2, by Austin Appleby - -// Note - This code makes a few assumptions about how your machine behaves - - -// 1. We can read a 4-byte value from any address without crashing -// 2. sizeof(int) == 4 - -// And it has a few limitations - - -// 1. It will not wo -// -// rk incrementally. -// 2. It will not produce the same results on little-endian and big-endian -// machines. - -unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed ) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - - const unsigned int m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - - unsigned int h = seed ^ len; - - // Mix 4 bytes at a time into the hash - - const unsigned char * data = (const unsigned char *)key; - - while(len >= 4) - { - - unsigned int k = *(unsigned int *)data; - - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - - switch(len) - { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - }; - - // Do a few final mixes of t - // - // - // - // he hash to ensure the last few - // bytes are well-incorporated. - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - diff --git a/c_src/gb_lru/rebar.config b/c_src/gb_lru/rebar.config deleted file mode 100644 index 0ffcccf..0000000 --- a/c_src/gb_lru/rebar.config +++ /dev/null @@ -1,7 +0,0 @@ -{port_specs, [ - {"../../priv/btreelru_nif.so", ["btreelru_nif.cpp"]} -]}. - - - - diff --git a/c_src/native_array/native_array_nif.c b/c_src/native_array/native_array_nif.c deleted file mode 100644 index 6ef3b5b..0000000 --- a/c_src/native_array/native_array_nif.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "erl_nif.h" - -#define A_OK(env) enif_make_atom(env, "ok") -#define assert_badarg(S, Env) if (! S) { return enif_make_badarg(env); } - -static ErlNifResourceType* array_handle = NULL; - -static void array_handle_cleanup(ErlNifEnv* env, void* arg) {} - -static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) -{ - ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; - array_handle = enif_open_resource_type(env, "native_array_nif", "array_handle", - &array_handle_cleanup, flags, 0); - // 用于存储指针的数组, 最多1000个array - *priv = enif_alloc(1000 * sizeof(void*)); - return 0; -} - -static void unload(ErlNifEnv* env, void* priv) -{ - enif_free(priv); -} - -static ERL_NIF_TERM new_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - // 取参数 - int refindex; - assert_badarg(enif_get_int(env, argv[0], &refindex), env); - // 取参数length - unsigned long length; - assert_badarg(enif_get_ulong(env, argv[1], &length), env); - // 分配内存 - // unsigned char* ref = enif_alloc_resource(array_handle, length); - unsigned char* ref = enif_alloc(length); - // 保存指针 - *((unsigned char**)enif_priv_data(env) + refindex) = ref; - return A_OK(env); -} - -static ERL_NIF_TERM get_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - // 取参数ref - int refindex; - assert_badarg(enif_get_int(env, argv[0], &refindex), env); - unsigned char* ref = *((unsigned char**)enif_priv_data(env) + refindex); - assert_badarg(ref, env); - // 取参数offset - unsigned long offset; - assert_badarg(enif_get_ulong(env, argv[1], &offset), env); - return enif_make_int(env, (int)(*(ref + offset - 1))); -} - -static ERL_NIF_TERM put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - // 取参数ref - int refindex; - assert_badarg(enif_get_int(env, argv[0], &refindex), env); - unsigned char* ref = *((unsigned char**)enif_priv_data(env) + refindex); - // 取参数offset - unsigned long offset; - assert_badarg(enif_get_ulong(env, argv[1], &offset), env); - // 取参数newval - unsigned int newval; - assert_badarg(enif_get_uint(env, argv[2], &newval), env); - // 赋值 - *(ref + offset - 1) = (unsigned char)newval; - return A_OK(env); -} - -static ERL_NIF_TERM delete_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - // 取参数ref - int refindex; - assert_badarg(enif_get_int(env, argv[0], &refindex), env); - unsigned char* ref = *((unsigned char**)enif_priv_data(env) + refindex); - //enif_release_resource(ref); - enif_free(ref); - return A_OK(env); -} - -static ErlNifFunc nif_funcs[] = { - {"new", 2, new_nif}, - {"get", 2, get_nif}, - {"put", 3, put_nif}, - {"delete", 1, delete_nif}, -}; - -ERL_NIF_INIT(native_array, nif_funcs, &load, NULL, NULL, &unload) - diff --git a/c_src/native_array/rebar.config b/c_src/native_array/rebar.config deleted file mode 100644 index c6f912b..0000000 --- a/c_src/native_array/rebar.config +++ /dev/null @@ -1,7 +0,0 @@ -{port_specs, [ - {"../../priv/native_array_nif.so", ["*.c"]} -]}. - - - - diff --git a/c_src/neural/NeuralTable.cpp b/c_src/neural/NeuralTable.cpp deleted file mode 100644 index b8df3ce..0000000 --- a/c_src/neural/NeuralTable.cpp +++ /dev/null @@ -1,905 +0,0 @@ -#include "NeuralTable.h" - -/* !!!! A NOTE ON KEYS !!!! - * Keys should be integer values passed from the erlang emulator, - * and should be generated by a hashing function. There is no easy - * way to hash an erlang term from a NIF, but ERTS is more than - * capable of doing so. - * - * Additionally, this workaround means that traditional collision - * handling mechanisms for hash tables will not work without - * special consideration. For instance, to compare keys as you - * would by storing linked lists, you must retrieve the stored - * tuple and call enif_compare or enif_is_identical on the key - * elements of each tuple. - */ - -table_set NeuralTable::tables; -atomic NeuralTable::running(true); -ErlNifMutex *NeuralTable::table_mutex; - -NeuralTable::NeuralTable(unsigned int kp) { - for (int i = 0; i < BUCKET_COUNT; ++i) { - ErlNifEnv *env = enif_alloc_env(); - env_buckets[i] = env; - locks[i] = enif_rwlock_create("neural_table"); - garbage_cans[i] = 0; - reclaimable[i] = enif_make_list(env, 0); - } - - start_gc(); - start_batch(); - - key_pos = kp; -} - -NeuralTable::~NeuralTable() { - stop_batch(); - stop_gc(); - for (int i = 0; i < BUCKET_COUNT; ++i) { - enif_rwlock_destroy(locks[i]); - enif_free_env(env_buckets[i]); - } -} - -/* ================================================================ - * MakeTable - * Allocates a new table, assuming a unique atom identifier. This - * table is stored in a static container. All interactions with - * the table must be performed through the static class API. - */ -ERL_NIF_TERM NeuralTable::MakeTable(ErlNifEnv *env, ERL_NIF_TERM name, ERL_NIF_TERM key_pos) { - char *atom; - string key; - unsigned int len = 0, - pos = 0; - ERL_NIF_TERM ret; - - // Allocate space for the name of the table - enif_get_atom_length(env, name, &len, ERL_NIF_LATIN1); - atom = (char*)enif_alloc(len + 1); - - // Fetch the value of the atom and store it in a string (because I can, that's why) - enif_get_atom(env, name, atom, len + 1, ERL_NIF_LATIN1); - key = atom; - - // Deallocate that space - enif_free(atom); - - // Get the key position value - enif_get_uint(env, key_pos, &pos); - - enif_mutex_lock(table_mutex); - if (NeuralTable::tables.find(key) != NeuralTable::tables.end()) { - // Table already exists? Bad monkey! - ret = enif_make_badarg(env); - } else { - // All good. Make the table - NeuralTable::tables[key] = new NeuralTable(pos); - ret = enif_make_atom(env, "ok"); - } - enif_mutex_unlock(table_mutex); - - return ret; -} - -/* ================================================================ - * GetTable - * Retrieves a handle to the table referenced by name, assuming - * such a table exists. If not, throw badarg. - */ -NeuralTable* NeuralTable::GetTable(ErlNifEnv *env, ERL_NIF_TERM name) { - char *atom = NULL; - string key; - unsigned len = 0; - NeuralTable *ret = NULL; - table_set::const_iterator it; - - // Allocate space for the table name - enif_get_atom_length(env, name, &len, ERL_NIF_LATIN1); - atom = (char*)enif_alloc(len + 1); - - // Copy the table name into a string - enif_get_atom(env, name, atom, len + 1, ERL_NIF_LATIN1); - key = atom; - - // Deallocate that space - enif_free(atom); - - // Look for the table and return its pointer if found - it = NeuralTable::tables.find(key); - if (it != NeuralTable::tables.end()) { - ret = it->second; - } - - return ret; -} - -/* ================================================================ - * Insert - * Inserts a tuple into the table with key. - */ -ERL_NIF_TERM NeuralTable::Insert(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM object) { - NeuralTable *tb; - ERL_NIF_TERM ret, old; - unsigned long int entry_key = 0; - - // Grab table or bail. - tb = GetTable(env, table); - if (tb == NULL) { - return enif_make_badarg(env); - } - - // Get key value. - enif_get_ulong(env, key, &entry_key); - - // Lock the key. - tb->rwlock(entry_key); - - // Attempt to lookup the value. If nonempty, increment - // discarded term counter and return a copy of the - // old value - if (tb->find(entry_key, old)) { - tb->reclaim(entry_key, old); - ret = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_copy(env, old)); - } else { - ret = enif_make_atom(env, "ok"); - } - - // Write that shit out - tb->put(entry_key, object); - - // Oh, and unlock the key if you would. - tb->rwunlock(entry_key); - - return ret; -} - -/* ================================================================ - * InsertNew - * Inserts a tuple into the table with key, assuming there is not - * a value with key already. Returns true if there was no value - * for key, or false if there was. - */ -ERL_NIF_TERM NeuralTable::InsertNew(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM object) { - NeuralTable *tb; - ERL_NIF_TERM ret, old; - unsigned long int entry_key = 0; - - // Get the table or bail - tb = GetTable(env, table); - if (tb == NULL) { - return enif_make_badarg(env); - } - - // Get the key value - enif_get_ulong(env, key, &entry_key); - - // Get write lock for the key - tb->rwlock(entry_key); - - if (tb->find(entry_key, old)) { - // Key was found. Return false and do not insert - ret = enif_make_atom(env, "false"); - } else { - // Key was not found. Return true and insert - tb->put(entry_key, object); - ret = enif_make_atom(env, "true"); - } - - // Release write lock for the key - tb->rwunlock(entry_key); - - return ret; -} - -/* ================================================================ - * Increment - * Processes a list of update operations. Each operation specifies - * a position in the stored tuple to update and an integer to add - * to it. - */ -ERL_NIF_TERM NeuralTable::Increment(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops) { - NeuralTable *tb; - ERL_NIF_TERM ret, old; - ERL_NIF_TERM it; - unsigned long int entry_key = 0; - - // Get table handle or bail - tb = GetTable(env, table); - if (tb == NULL) { - return enif_make_badarg(env); - } - - // Get key value - enif_get_ulong(env, key, &entry_key); - - // Acquire read/write lock for key - tb->rwlock(entry_key); - - // Try to read the value as it is - if (tb->find(entry_key, old)) { - // Value exists - ERL_NIF_TERM op_cell; - const ERL_NIF_TERM *tb_tpl; - const ERL_NIF_TERM *op_tpl; - ERL_NIF_TERM *new_tpl; - ErlNifEnv *bucket_env = tb->get_env(entry_key); - unsigned long int pos = 0; - long int incr = 0; - unsigned int ops_length = 0; - int op_arity = 0, - tb_arity = 0; - - // Expand tuple to work on elements - enif_get_tuple(bucket_env, old, &tb_arity, &tb_tpl); - - // Allocate space for a copy the contents of the table - // tuple and copy it in. All changes are to be made to - // the copy of the tuple. - new_tpl = (ERL_NIF_TERM*)enif_alloc(sizeof(ERL_NIF_TERM) * tb_arity); - memcpy(new_tpl, tb_tpl, sizeof(ERL_NIF_TERM) * tb_arity); - - // Create empty list cell for return value. - ret = enif_make_list(env, 0); - - // Set iterator to first cell of ops - it = ops; - while(!enif_is_empty_list(env, it)) { - long int value = 0; - enif_get_list_cell(env, it, &op_cell, &it); // op_cell = hd(it), it = tl(it) - enif_get_tuple(env, op_cell, &op_arity, &op_tpl); // op_arity = tuple_size(op_cell), op_tpl = [TplPos1, TplPos2] - enif_get_ulong(env, op_tpl[0], &pos); // pos = (uint64)op_tpl[0] - enif_get_long(env, op_tpl[1], &incr); // incr = (uint64)op_tpl[1] - - // Is the operation trying to modify a nonexistant - // position? - if (pos <= 0 || pos > tb_arity) { - ret = enif_make_badarg(env); - goto bailout; - } - - // Is the operation trying to add to a value that's - // not a number? - if (!enif_is_number(bucket_env, new_tpl[pos - 1])) { - ret = enif_make_badarg(env); - goto bailout; - } - - // Update the value stored in the tuple. - enif_get_long(env, new_tpl[pos - 1], &value); - tb->reclaim(entry_key, new_tpl[pos - 1]); - new_tpl[pos - 1] = enif_make_long(bucket_env, value + incr); - - // Copy the new value to the head of the return list - ret = enif_make_list_cell(env, enif_make_copy(env, new_tpl[pos - 1]), ret); - } - - tb->put(entry_key, enif_make_tuple_from_array(bucket_env, new_tpl, tb_arity)); - - // Bailout allows cancelling the update opertion - // in case something goes wrong. It must always - // come after tb->put and before enif_free and - // rwunlock -bailout: - enif_free(new_tpl); - } else { - ret = enif_make_badarg(env); - } - // Release the rwlock for entry_key - tb->rwunlock(entry_key); - - return ret; -} - -/* ================================================================ - * Unshift - * Processes a list of update operations. Each update operation is - * a tuple specifying the position of a list in the stored value to - * update and a list of values to append. Elements are shifted from - * the input list to the stored list, so: - * - * unshift([a,b,c,d]) results in [d,c,b,a] - */ -ERL_NIF_TERM NeuralTable::Unshift(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops) { - NeuralTable *tb; - ERL_NIF_TERM ret, old, it; - unsigned long int entry_key; - ErlNifEnv *bucket_env; - - tb = GetTable(env, table); - if (tb == NULL) { - return enif_make_badarg(env); - } - - enif_get_ulong(env, key, &entry_key); - - tb->rwlock(entry_key); - bucket_env = tb->get_env(entry_key); - if (tb->find(entry_key, old)) { - const ERL_NIF_TERM *old_tpl, - *op_tpl; - ERL_NIF_TERM *new_tpl; - int tb_arity = 0, - op_arity = 0; - unsigned long pos = 0; - unsigned int new_length = 0; - ERL_NIF_TERM op, - unshift, - copy_it, - copy_val; - - enif_get_tuple(bucket_env, old, &tb_arity, &old_tpl); - new_tpl = (ERL_NIF_TERM*)enif_alloc(sizeof(ERL_NIF_TERM) * tb_arity); - memcpy(new_tpl, old_tpl, sizeof(ERL_NIF_TERM) * tb_arity); - - it = ops; - ret = enif_make_list(env, 0); - - while (!enif_is_empty_list(env, it)) { - // Examine the operation. - enif_get_list_cell(env, it, &op, &it); // op = hd(it), it = tl(it) - enif_get_tuple(env, op, &op_arity, &op_tpl); // op_arity = tuple_size(op), op_tpl = [TplPos1, TplPos2] - enif_get_ulong(env, op_tpl[0], &pos); // Tuple position to modify - unshift = op_tpl[1]; // Values to unshfit - - // Argument 1 of the operation tuple is position; - // make sure it's within the bounds of the tuple - // in the table. - if (pos <= 0 || pos > tb_arity) { - ret = enif_make_badarg(env); - goto bailout; - } - - // Make sure we were passed a list of things to push - // onto the posth element of the entry - if (!enif_is_list(env, unshift)) { - ret = enif_make_badarg(env); - } - - // Now iterate over unshift, moving its values to - // the head of new_tpl[pos - 1] one by one - copy_it = unshift; - while (!enif_is_empty_list(env, copy_it)) { - enif_get_list_cell(env, copy_it, ©_val, ©_it); - new_tpl[pos - 1] = enif_make_list_cell(bucket_env, enif_make_copy(bucket_env, copy_val), new_tpl[pos - 1]); - } - enif_get_list_length(bucket_env, new_tpl[pos - 1], &new_length); - ret = enif_make_list_cell(env, enif_make_uint(env, new_length), ret); - } - - tb->put(entry_key, enif_make_tuple_from_array(bucket_env, new_tpl, tb_arity)); - -bailout: - enif_free(new_tpl); - } else { - ret = enif_make_badarg(env); - } - tb->rwunlock(entry_key); - - return ret; -} - -ERL_NIF_TERM NeuralTable::Shift(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops) { - NeuralTable *tb; - ERL_NIF_TERM ret, old, it; - unsigned long int entry_key; - ErlNifEnv *bucket_env; - - tb = GetTable(env, table); - if (tb == NULL) { - return enif_make_badarg(env); - } - - enif_get_ulong(env, key, &entry_key); - - tb->rwlock(entry_key); - bucket_env = tb->get_env(entry_key); - if (tb->find(entry_key, old)) { - const ERL_NIF_TERM *old_tpl; - const ERL_NIF_TERM *op_tpl; - ERL_NIF_TERM *new_tpl; - int tb_arity = 0, - op_arity = 0; - unsigned long pos = 0, - count = 0; - ERL_NIF_TERM op, list, shifted, reclaim; - - enif_get_tuple(bucket_env, old, &tb_arity, &old_tpl); - new_tpl = (ERL_NIF_TERM*)enif_alloc(tb_arity * sizeof(ERL_NIF_TERM)); - memcpy(new_tpl, old_tpl, sizeof(ERL_NIF_TERM) * tb_arity); - - it = ops; - ret = enif_make_list(env, 0); - reclaim = enif_make_list(bucket_env, 0); - - while(!enif_is_empty_list(env, it)) { - enif_get_list_cell(env, it, &op, &it); - enif_get_tuple(env, op, &op_arity, &op_tpl); - enif_get_ulong(env, op_tpl[0], &pos); - enif_get_ulong(env, op_tpl[1], &count); - - if (pos <= 0 || pos > tb_arity) { - ret = enif_make_badarg(env); - goto bailout; - } - - if (!enif_is_list(env, new_tpl[pos -1])) { - ret = enif_make_badarg(env); - goto bailout; - } - - shifted = enif_make_list(env, 0); - if (count > 0) { - ERL_NIF_TERM copy_it = new_tpl[pos - 1], - val; - int i = 0; - while (i < count && !enif_is_empty_list(bucket_env, copy_it)) { - enif_get_list_cell(bucket_env, copy_it, &val, ©_it); - ++i; - shifted = enif_make_list_cell(env, enif_make_copy(env, val), shifted); - reclaim = enif_make_list_cell(env, val, reclaim); - } - new_tpl[pos - 1] = copy_it; - } else if (count < 0) { - ERL_NIF_TERM copy_it = new_tpl[pos - 1], - val; - while (!enif_is_empty_list(bucket_env, copy_it)) { - enif_get_list_cell(bucket_env, copy_it, &val, ©_it); - shifted = enif_make_list_cell(env, enif_make_copy(env, val), shifted); - reclaim = enif_make_list_cell(env, val, reclaim); - } - new_tpl[pos - 1] = copy_it; - } - ret = enif_make_list_cell(env, shifted, ret); - } - - tb->put(entry_key, enif_make_tuple_from_array(bucket_env, new_tpl, tb_arity)); - tb->reclaim(entry_key, reclaim); -bailout: - enif_free(new_tpl); - } else { - ret = enif_make_badarg(env); - } - tb->rwunlock(entry_key); - - return ret; -} - -ERL_NIF_TERM NeuralTable::Swap(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops) { - NeuralTable *tb; - ERL_NIF_TERM ret, old, it; - unsigned long int entry_key; - ErlNifEnv *bucket_env; - - tb = GetTable(env, table); - if (tb == NULL) { - return enif_make_badarg(env); - } - - enif_get_ulong(env, key, &entry_key); - - tb->rwlock(entry_key); - bucket_env = tb->get_env(entry_key); - if (tb->find(entry_key, old)) { - const ERL_NIF_TERM *old_tpl; - const ERL_NIF_TERM *op_tpl; - ERL_NIF_TERM *new_tpl; - int tb_arity = 0, - op_arity = 0; - unsigned long pos = 0; - ERL_NIF_TERM op, list, shifted, reclaim; - - enif_get_tuple(bucket_env, old, &tb_arity, &old_tpl); - new_tpl = (ERL_NIF_TERM*)enif_alloc(tb_arity * sizeof(ERL_NIF_TERM)); - memcpy(new_tpl, old_tpl, sizeof(ERL_NIF_TERM) * tb_arity); - - it = ops; - ret = enif_make_list(env, 0); - reclaim = enif_make_list(bucket_env, 0); - - while (!enif_is_empty_list(env, it)) { - enif_get_list_cell(env, it, &op, &it); - enif_get_tuple(env, op, &op_arity, &op_tpl); - enif_get_ulong(env, op_tpl[0], &pos); - - if (pos <= 0 || pos > tb_arity) { - ret = enif_make_badarg(env); - goto bailout; - } - - reclaim = enif_make_list_cell(bucket_env, new_tpl[pos - 1], reclaim); - ret = enif_make_list_cell(env, enif_make_copy(env, new_tpl[pos -1]), ret); - new_tpl[pos - 1] = enif_make_copy(bucket_env, op_tpl[1]); - } - - tb->put(entry_key, enif_make_tuple_from_array(bucket_env, new_tpl, tb_arity)); - tb->reclaim(entry_key, reclaim); -bailout: - enif_free(new_tpl); - } else { - ret = enif_make_badarg(env); - } - tb->rwunlock(entry_key); - - return ret; -} - -ERL_NIF_TERM NeuralTable::Delete(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key) { - NeuralTable *tb; - ERL_NIF_TERM val, ret; - unsigned long int entry_key; - - tb = GetTable(env, table); - if (tb == NULL) { return enif_make_badarg(env); } - - enif_get_ulong(env, key, &entry_key); - - tb->rwlock(entry_key); - - if (tb->erase(entry_key, val)) { - tb->reclaim(entry_key, val); - ret = enif_make_copy(env, val); - } else { - ret = enif_make_atom(env, "undefined"); - } - - tb->rwunlock(entry_key); - - return ret; -} - -ERL_NIF_TERM NeuralTable::Empty(ErlNifEnv *env, ERL_NIF_TERM table) { - NeuralTable *tb; - int n = 0; - - tb = GetTable(env, table); - if (tb == NULL) { return enif_make_badarg(env); } - - // First, lock EVERY bucket. We want this to be an isolated operation. - for (n = 0; n < BUCKET_COUNT; ++n) { - enif_rwlock_rwlock(tb->locks[n]); - } - - // Now clear the table - for (n = 0; n < BUCKET_COUNT; ++n) { - tb->hash_buckets[n].clear(); - enif_clear_env(tb->env_buckets[n]); - tb->garbage_cans[n] = 0; - tb->reclaimable[n] = enif_make_list(tb->env_buckets[n], 0); - } - - // Now unlock every bucket. - for (n = 0; n < BUCKET_COUNT; ++n) { - enif_rwlock_rwunlock(tb->locks[n]); - } - - return enif_make_atom(env, "ok"); -} - -ERL_NIF_TERM NeuralTable::Get(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key) { - NeuralTable *tb; - ERL_NIF_TERM ret, val; - unsigned long int entry_key; - - // Acquire table handle, or quit if the table doesn't exist. - tb = GetTable(env, table); - if (tb == NULL) { return enif_make_badarg(env); } - - // Get key value - enif_get_ulong(env, key, &entry_key); - - // Lock the key - tb->rlock(entry_key); - - // Read current value - if (!tb->find(entry_key, val)) { - ret = enif_make_atom(env, "undefined"); - } else { - ret = enif_make_copy(env, val); - } - - tb->runlock(entry_key); - - return ret; -} - -ERL_NIF_TERM NeuralTable::Dump(ErlNifEnv *env, ERL_NIF_TERM table) { - NeuralTable *tb = GetTable(env, table); - ErlNifPid self; - ERL_NIF_TERM ret; - - if (tb == NULL) { return enif_make_badarg(env); } - - enif_self(env, &self); - - tb->add_batch_job(self, &NeuralTable::batch_dump); - - return enif_make_atom(env, "$neural_batch_wait"); -} - -ERL_NIF_TERM NeuralTable::Drain(ErlNifEnv *env, ERL_NIF_TERM table) { - NeuralTable *tb = GetTable(env, table); - ErlNifPid self; - int ret; - - if (tb == NULL) { return enif_make_badarg(env); } - - enif_self(env, &self); - - tb->add_batch_job(self, &NeuralTable::batch_drain); - - return enif_make_atom(env, "$neural_batch_wait"); -} - -ERL_NIF_TERM NeuralTable::GetKeyPosition(ErlNifEnv *env, ERL_NIF_TERM table) { - NeuralTable *tb = GetTable(env, table); - - if (tb == NULL) { return enif_make_badarg(env); } - return enif_make_uint(env, tb->key_pos); -} - -ERL_NIF_TERM NeuralTable::GarbageCollect(ErlNifEnv *env, ERL_NIF_TERM table) { - NeuralTable *tb = GetTable(env, table); - if (tb == NULL) { return enif_make_badarg(env); } - - enif_cond_signal(tb->gc_cond); - - return enif_make_atom(env, "ok"); -} - -ERL_NIF_TERM NeuralTable::GarbageSize(ErlNifEnv *env, ERL_NIF_TERM table) { - NeuralTable *tb = GetTable(env, table); - unsigned long int size = 0; - - if (tb == NULL) { return enif_make_badarg(env); } - - size = tb->garbage_size(); - - return enif_make_ulong(env, size); -} - -void* NeuralTable::DoGarbageCollection(void *table) { - NeuralTable *tb = (NeuralTable*)table; - - enif_mutex_lock(tb->gc_mutex); - - while (running.load(memory_order_acquire)) { - while (running.load(memory_order_acquire) && tb->garbage_size() < RECLAIM_THRESHOLD) { - enif_cond_wait(tb->gc_cond, tb->gc_mutex); - } - tb->gc(); - } - - enif_mutex_unlock(tb->gc_mutex); - - return NULL; -} - -void* NeuralTable::DoReclamation(void *table) { - const int max_eat = 5; - NeuralTable *tb = (NeuralTable*)table; - int i = 0, c = 0, t = 0;; - ERL_NIF_TERM tl, hd; - ErlNifEnv *env; - - while (running.load(memory_order_acquire)) { - for (i = 0; i < BUCKET_COUNT; ++i) { - c = 0; - t = 0; - tb->rwlock(i); - env = tb->get_env(i); - tl = tb->reclaimable[i]; - while (c++ < max_eat && !enif_is_empty_list(env, tl)) { - enif_get_list_cell(env, tl, &hd, &tl); - tb->garbage_cans[i] += estimate_size(env, hd); - t += tb->garbage_cans[i]; - } - tb->rwunlock(i); - - if (t >= RECLAIM_THRESHOLD) { - enif_cond_signal(tb->gc_cond); - } - } -#ifdef _WIN32 - Sleep(50); -#else - usleep(50000); -#endif - - } - - return NULL; -} - -void* NeuralTable::DoBatchOperations(void *table) { - NeuralTable *tb = (NeuralTable*)table; - - enif_mutex_lock(tb->batch_mutex); - - while (running.load(memory_order_acquire)) { - while (running.load(memory_order_acquire) && tb->batch_jobs.empty()) { - enif_cond_wait(tb->batch_cond, tb->batch_mutex); - } - BatchJob job = tb->batch_jobs.front(); - (tb->*job.fun)(job.pid); - tb->batch_jobs.pop(); - } - - enif_mutex_unlock(tb->batch_mutex); - - return NULL; -} - -void NeuralTable::start_gc() { - int ret; - - gc_mutex = enif_mutex_create("neural_table_gc"); - gc_cond = enif_cond_create("neural_table_gc"); - - ret = enif_thread_create("neural_garbage_collector", &gc_tid, NeuralTable::DoGarbageCollection, (void*)this, NULL); - if (ret != 0) { - printf("[neural_gc] Can't create GC thread. Error Code: %d\r\n", ret); - } - - // Start the reclaimer after the garbage collector. - ret = enif_thread_create("neural_reclaimer", &rc_tid, NeuralTable::DoReclamation, (void*)this, NULL); - if (ret != 0) { - printf("[neural_gc] Can't create reclamation thread. Error Code: %d\r\n", ret); - } -} - -void NeuralTable::stop_gc() { - enif_cond_signal(gc_cond); - // Join the reclaimer before the garbage collector. - enif_thread_join(rc_tid, NULL); - enif_thread_join(gc_tid, NULL); -} - -void NeuralTable::start_batch() { - int ret; - - batch_mutex = enif_mutex_create("neural_table_batch"); - batch_cond = enif_cond_create("neural_table_batch"); - - ret = enif_thread_create("neural_batcher", &batch_tid, NeuralTable::DoBatchOperations, (void*)this, NULL); - if (ret != 0) { - printf("[neural_batch] Can't create batch thread. Error Code: %d\r\n", ret); - } -} - -void NeuralTable::stop_batch() { - enif_cond_signal(batch_cond); - enif_thread_join(batch_tid, NULL); -} - -void NeuralTable::put(unsigned long int key, ERL_NIF_TERM tuple) { - ErlNifEnv *env = get_env(key); - hash_buckets[GET_BUCKET(key)][key] = enif_make_copy(env, tuple); -} - -ErlNifEnv* NeuralTable::get_env(unsigned long int key) { - return env_buckets[GET_BUCKET(key)]; -} - -bool NeuralTable::find(unsigned long int key, ERL_NIF_TERM &ret) { - hash_table *bucket = &hash_buckets[GET_BUCKET(key)]; - hash_table::iterator it = bucket->find(key); - if (bucket->end() == it) { - return false; - } else { - ret = it->second; - return true; - } -} - -bool NeuralTable::erase(unsigned long int key, ERL_NIF_TERM &val) { - hash_table *bucket = &hash_buckets[GET_BUCKET(key)]; - hash_table::iterator it = bucket->find(key); - bool ret = false; - if (it != bucket->end()) { - ret = true; - val = it->second; - bucket->erase(it); - } - return ret; -} - -void NeuralTable::add_batch_job(ErlNifPid pid, BatchFunction fun) { - BatchJob job; - job.pid = pid; - job.fun = fun; - - enif_mutex_lock(batch_mutex); - batch_jobs.push(job); - enif_mutex_unlock(batch_mutex); - - enif_cond_signal(batch_cond); -} - -void NeuralTable::batch_drain(ErlNifPid pid) { - ErlNifEnv *env = enif_alloc_env(); - ERL_NIF_TERM msg, value; - - value = enif_make_list(env, 0); - for (int i = 0; i < BUCKET_COUNT; ++i) { - enif_rwlock_rwlock(locks[i]); - - for (hash_table::iterator it = hash_buckets[i].begin(); it != hash_buckets[i].end(); ++it) { - value = enif_make_list_cell(env, enif_make_copy(env, it->second), value); - } - enif_clear_env(env_buckets[i]); - hash_buckets[i].clear(); - garbage_cans[i] = 0; - reclaimable[i] = enif_make_list(env_buckets[i], 0); - - enif_rwlock_rwunlock(locks[i]); - } - - msg = enif_make_tuple2(env, enif_make_atom(env, "$neural_batch_response"), value); - - enif_send(NULL, &pid, env, msg); - - enif_free_env(env); -} - -void NeuralTable::batch_dump(ErlNifPid pid) { - ErlNifEnv *env = enif_alloc_env(); - ERL_NIF_TERM msg, value; - - value = enif_make_list(env, 0); - for (int i = 0; i < BUCKET_COUNT; ++i) { - enif_rwlock_rlock(locks[i]); - for (hash_table::iterator it = hash_buckets[i].begin(); it != hash_buckets[i].end(); ++it) { - value = enif_make_list_cell(env, enif_make_copy(env, it->second), value); - } - enif_rwlock_runlock(locks[i]); - } - - msg = enif_make_tuple2(env, enif_make_atom(env, "$neural_batch_response"), value); - - enif_send(NULL, &pid, env, msg); - - enif_free_env(env); -} - -void NeuralTable::reclaim(unsigned long int key, ERL_NIF_TERM term) { - int bucket = GET_BUCKET(key); - ErlNifEnv *env = get_env(key); - reclaimable[bucket] = enif_make_list_cell(env, term, reclaimable[bucket]); -} - -void NeuralTable::gc() { - ErlNifEnv *fresh = NULL, - *old = NULL; - hash_table *bucket = NULL; - hash_table::iterator it; - unsigned int gc_curr = 0; - - for (; gc_curr < BUCKET_COUNT; ++gc_curr) { - bucket = &hash_buckets[gc_curr]; - old = env_buckets[gc_curr]; - fresh = enif_alloc_env(); - - enif_rwlock_rwlock(locks[gc_curr]); - for (it = bucket->begin(); it != bucket->end(); ++it) { - it->second = enif_make_copy(fresh, it->second); - } - - garbage_cans[gc_curr] = 0; - env_buckets[gc_curr] = fresh; - reclaimable[gc_curr] = enif_make_list(fresh, 0); - enif_free_env(old); - enif_rwlock_rwunlock(locks[gc_curr]); - } -} - -unsigned long int NeuralTable::garbage_size() { - unsigned long int size = 0; - for (int i = 0; i < BUCKET_COUNT; ++i) { - enif_rwlock_rlock(locks[i]); - size += garbage_cans[i]; - enif_rwlock_runlock(locks[i]); - } - return size; -} diff --git a/c_src/neural/NeuralTable.h b/c_src/neural/NeuralTable.h deleted file mode 100644 index 527c820..0000000 --- a/c_src/neural/NeuralTable.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef NEURALTABLE_H -#define NEURALTABLE_H - -#include "erl_nif.h" -#include "neural_utils.h" -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include -#include -#include -#else -#include -#endif - -#define BUCKET_COUNT 64 -#define BUCKET_MASK (BUCKET_COUNT - 1) -#define GET_BUCKET(key) key & BUCKET_MASK -#define GET_LOCK(key) key & BUCKET_MASK -#define RECLAIM_THRESHOLD 1048576 - -using namespace std; - -class NeuralTable; - -typedef unordered_map table_set; -typedef unordered_map hash_table; -typedef void (NeuralTable::*BatchFunction)(ErlNifPid pid); - -class NeuralTable { - public: - static ERL_NIF_TERM MakeTable(ErlNifEnv *env, ERL_NIF_TERM name, ERL_NIF_TERM keypos); - static ERL_NIF_TERM Insert(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM object); - static ERL_NIF_TERM InsertNew(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM object); - static ERL_NIF_TERM Delete(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key); - static ERL_NIF_TERM Empty(ErlNifEnv *env, ERL_NIF_TERM table); - static ERL_NIF_TERM Get(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key); - static ERL_NIF_TERM Increment(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops); - static ERL_NIF_TERM Shift(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops); - static ERL_NIF_TERM Unshift(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops); - static ERL_NIF_TERM Swap(ErlNifEnv *env, ERL_NIF_TERM table, ERL_NIF_TERM key, ERL_NIF_TERM ops); - static ERL_NIF_TERM Dump(ErlNifEnv *env, ERL_NIF_TERM table); - static ERL_NIF_TERM Drain(ErlNifEnv *env, ERL_NIF_TERM table); - static ERL_NIF_TERM GetKeyPosition(ErlNifEnv *env, ERL_NIF_TERM table); - static ERL_NIF_TERM GarbageCollect(ErlNifEnv *env, ERL_NIF_TERM table); - static ERL_NIF_TERM GarbageSize(ErlNifEnv *env, ERL_NIF_TERM table); - static NeuralTable* GetTable(ErlNifEnv *env, ERL_NIF_TERM name); - static void* DoGarbageCollection(void *table); - static void* DoBatchOperations(void *table); - static void* DoReclamation(void *table); - static void Initialize() { - table_mutex = enif_mutex_create("neural_table_maker"); - } - static void Shutdown() { - running = false; - table_set::iterator it(tables.begin()); - - while (it != tables.end()) { - delete it->second; - tables.erase(it); - it = tables.begin(); - } - - enif_mutex_destroy(table_mutex); - } - - void rlock(unsigned long int key) { enif_rwlock_rlock(locks[GET_LOCK(key)]); } - void runlock(unsigned long int key) { enif_rwlock_runlock(locks[GET_LOCK(key)]); } - void rwlock(unsigned long int key) { enif_rwlock_rwlock(locks[GET_LOCK(key)]); } - void rwunlock(unsigned long int key) { enif_rwlock_rwunlock(locks[GET_LOCK(key)]); } - - ErlNifEnv *get_env(unsigned long int key); - bool erase(unsigned long int key, ERL_NIF_TERM &ret); - bool find(unsigned long int key, ERL_NIF_TERM &ret); - void put(unsigned long int key, ERL_NIF_TERM tuple); - void batch_dump(ErlNifPid pid); - void batch_drain(ErlNifPid pid); - void start_gc(); - void stop_gc(); - void start_batch(); - void stop_batch(); - void gc(); - void reclaim(unsigned long int key, ERL_NIF_TERM reclaim); - unsigned long int garbage_size(); - void add_batch_job(ErlNifPid pid, BatchFunction fun); - - protected: - static table_set tables; - static atomic running; - static ErlNifMutex *table_mutex; - - struct BatchJob { - ErlNifPid pid; - BatchFunction fun; - }; - - NeuralTable(unsigned int kp); - ~NeuralTable(); - - unsigned int garbage_cans[BUCKET_COUNT]; - hash_table hash_buckets[BUCKET_COUNT]; - ErlNifEnv *env_buckets[BUCKET_COUNT]; - ERL_NIF_TERM reclaimable[BUCKET_COUNT]; - ErlNifRWLock *locks[BUCKET_COUNT]; - ErlNifCond *gc_cond; - ErlNifMutex *gc_mutex; - ErlNifTid gc_tid; - ErlNifTid rc_tid; - ErlNifCond *batch_cond; - ErlNifMutex *batch_mutex; - queue batch_jobs; - ErlNifTid batch_tid; - - unsigned int key_pos; -}; - -#endif diff --git a/c_src/neural/neural.cpp b/c_src/neural/neural.cpp deleted file mode 100644 index 0273324..0000000 --- a/c_src/neural/neural.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "erl_nif.h" -#include "NeuralTable.h" -#include - -// Prototypes -static ERL_NIF_TERM neural_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_put(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_put_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_increment(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_unshift(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_shift(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_swap(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_delete(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_garbage(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_garbage_size(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_empty(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_drain(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_dump(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM neural_key_pos(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); - -static ErlNifFunc nif_funcs[] = -{ - {"make_table", 2, neural_new}, - {"do_fetch", 2, neural_get}, - {"do_delete", 2, neural_delete}, - {"do_dump", 1, neural_dump}, - {"do_drain", 1, neural_drain}, - {"empty", 1, neural_empty}, - {"insert", 3, neural_put}, - {"insert_new", 3, neural_put_new}, - {"do_increment", 3, neural_increment}, - {"do_unshift", 3, neural_unshift}, - {"do_shift", 3, neural_shift}, - {"do_swap", 3, neural_swap}, - {"garbage", 1, neural_garbage}, - {"garbage_size", 1, neural_garbage_size}, - {"key_pos", 1, neural_key_pos} -}; - -static ERL_NIF_TERM neural_key_pos(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - // This function is directly exposed, so no strict guards or patterns protecting us. - if (argc != 1 || !enif_is_atom(env, argv[0])) { return enif_make_badarg(env); } - - return NeuralTable::GetKeyPosition(env, argv[0]); -} - -static ERL_NIF_TERM neural_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::MakeTable(env, argv[0], argv[1]); -} - -static ERL_NIF_TERM neural_put(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::Insert(env, argv[0], argv[1], argv[2]); -} - -static ERL_NIF_TERM neural_put_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::InsertNew(env, argv[0], argv[1], argv[2]); -} - -static ERL_NIF_TERM neural_increment(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_atom(env, argv[0]) || !enif_is_number(env, argv[1]) || !enif_is_list(env, argv[2])) { - return enif_make_badarg(env); - } - - return NeuralTable::Increment(env, argv[0], argv[1], argv[2]); -} - -static ERL_NIF_TERM neural_shift(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::Shift(env, argv[0], argv[1], argv[2]); -} - -static ERL_NIF_TERM neural_unshift(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::Unshift(env, argv[0], argv[1], argv[2]); -} - -static ERL_NIF_TERM neural_swap(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]){ - return NeuralTable::Swap(env, argv[0], argv[1], argv[2]); -} - -static ERL_NIF_TERM neural_get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::Get(env, argv[0], argv[1]); -} - -static ERL_NIF_TERM neural_delete(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - return NeuralTable::Delete(env, argv[0], argv[1]); -} - -static ERL_NIF_TERM neural_empty(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_atom(env, argv[0])) { return enif_make_badarg(env); } - - return NeuralTable::Empty(env, argv[0]); -} - -static ERL_NIF_TERM neural_dump(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_atom(env, argv[0])) { return enif_make_badarg(env); } - - return NeuralTable::Dump(env, argv[0]); -} - -static ERL_NIF_TERM neural_drain(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_atom(env, argv[0])) { return enif_make_badarg(env); } - - return NeuralTable::Drain(env, argv[0]); -} - -static ERL_NIF_TERM neural_garbage(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_atom(env, argv[0])) { return enif_make_badarg(env); } - - return NeuralTable::GarbageCollect(env, argv[0]); -} - -static ERL_NIF_TERM neural_garbage_size(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if (!enif_is_atom(env, argv[0])) { return enif_make_badarg(env); } - - return NeuralTable::GarbageSize(env, argv[0]); -} - -static void neural_resource_cleanup(ErlNifEnv* env, void* arg) -{ - /* Delete any dynamically allocated memory stored in neural_handle */ - /* neural_handle* handle = (neural_handle*)arg; */ -} - -static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -{ - NeuralTable::Initialize(); - return 0; -} - -static void on_unload(ErlNifEnv *env, void *priv_data) { - NeuralTable::Shutdown(); -} - -ERL_NIF_INIT(neural, nif_funcs, &on_load, NULL, NULL, &on_unload); diff --git a/c_src/neural/neural_utils.cpp b/c_src/neural/neural_utils.cpp deleted file mode 100644 index a579ef5..0000000 --- a/c_src/neural/neural_utils.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "neural_utils.h" - -unsigned long int estimate_size(ErlNifEnv *env, ERL_NIF_TERM term) { - if (enif_is_atom(env, term)) { - return WORD_SIZE; - } - - // Treating all numbers like longs. - if (enif_is_number(env, term)) { - return 2 * WORD_SIZE; - } - - if (enif_is_binary(env, term)) { - ErlNifBinary bin; - enif_inspect_binary(env, term, &bin); - return bin.size + (6 * WORD_SIZE); - } - - if (enif_is_list(env, term)) { - unsigned long int size = 0; - ERL_NIF_TERM it, curr; - it = term; - size += WORD_SIZE; - while (!enif_is_empty_list(env, it)) { - enif_get_list_cell(env, it, &curr, &it); - size += estimate_size(env, curr) + WORD_SIZE; - } - return size; - } - - if (enif_is_tuple(env, term)) { - unsigned long int size = 0; - const ERL_NIF_TERM *tpl; - int arity; - enif_get_tuple(env, term, &arity, &tpl); - for (int i = 0; i < arity; ++i) { - size += estimate_size(env, tpl[i]); - } - return size; - } - - // Return 1 word by default - return WORD_SIZE; -} - - diff --git a/c_src/neural/neural_utils.h b/c_src/neural/neural_utils.h deleted file mode 100644 index 6111d0b..0000000 --- a/c_src/neural/neural_utils.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef NEURAL_UTILS_H -#define NEURAL_UTILS_H - -#include "erl_nif.h" -#define WORD_SIZE sizeof(int) - -unsigned long int estimate_size(ErlNifEnv *env, ERL_NIF_TERM term); - -#endif diff --git a/c_src/neural/rebar.config b/c_src/neural/rebar.config deleted file mode 100644 index 8d8e6be..0000000 --- a/c_src/neural/rebar.config +++ /dev/null @@ -1,14 +0,0 @@ -{port_specs, [ - {"../../priv/neural.so", ["*.cpp"]} -]}. - -{port_env, [ - {".*", "CXXFLAGS", "$CXXFLAGS -std=c++11 -O3"}, - {".*", "LDFLAGS", "$LDFLAGS -lstdc++ -shared"} -]}. - - - - - - diff --git a/c_src/nif_array/binary_tools.c b/c_src/nif_array/binary_tools.c deleted file mode 100644 index 64d336c..0000000 --- a/c_src/nif_array/binary_tools.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "erl_nif.h" -#include -#include - -static ErlNifResourceType* test_RESOURCE = NULL; - -// Prototypes -static ERL_NIF_TERM get_bin_address(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM new_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM size_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM put_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM get_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]); - - -static ErlNifFunc nif_funcs[] = -{ - {"get_bin_address", 1, get_bin_address}, - {"new_array", 1, new_array}, - {"size_array", 1, size_array}, - {"put_array", 3, put_array}, - {"get_array", 2, get_array} -}; - -static ERL_NIF_TERM get_bin_address(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - enif_inspect_binary(env, argv[0], &bin); - char buf[256]; - sprintf(buf, "bin: size=%zu, ptr=%p", bin.size, bin.data); - return enif_make_string(env, buf, ERL_NIF_LATIN1); -} - -static ERL_NIF_TERM new_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - unsigned long size; - // unsigned char* data; - enif_get_ulong(env, argv[0], &size); - // enif_inspect_binary(env, argv[1], &bin); - enif_alloc_binary(size * sizeof(long), &bin); - - return enif_make_binary(env, &bin); -} - -static ERL_NIF_TERM size_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - enif_inspect_binary(env, argv[0], &bin); - return enif_make_int64(env, bin.size); -} - -static ERL_NIF_TERM put_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - unsigned long* array; - unsigned long pos, value; - enif_get_ulong(env, argv[0], &pos); - enif_get_ulong(env, argv[1], &value); - enif_inspect_binary(env, argv[2], &bin); - array = (unsigned long*)bin.data; - array[pos] = value; - return enif_make_atom(env, "ok"); -} - -static ERL_NIF_TERM get_array(ErlNifEnv* env, int argc, - const ERL_NIF_TERM argv[]) -{ - ErlNifBinary bin; - unsigned long* array; - unsigned long pos; - enif_get_ulong(env, argv[0], &pos); - enif_inspect_binary(env, argv[1], &bin); - array = (unsigned long*)bin.data; - return enif_make_int64(env, *(array + pos)); -} - -static void test_resource_cleanup(ErlNifEnv* env, void* arg) -{ - /* Delete any dynamically allocated memory stored in test_handle */ - /* test_handle* handle = (test_handle*)arg; */ -} - -static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -{ - ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; - ErlNifResourceType* rt = enif_open_resource_type(env, NULL, - "test_resource", - &test_resource_cleanup, - flags, NULL); - if (rt == NULL) - return -1; - - test_RESOURCE = rt; - - return 0; -} - -ERL_NIF_INIT(binary_tools, nif_funcs, &on_load, NULL, NULL, NULL); diff --git a/c_src/nif_array/rebar.config b/c_src/nif_array/rebar.config deleted file mode 100644 index e1e93bf..0000000 --- a/c_src/nif_array/rebar.config +++ /dev/null @@ -1,13 +0,0 @@ -{port_specs, [ - {"../../priv/binary_tools.so", ["*.c"]} -]}. - -{port_env, [ - {"linux", "CFLAGS", "$CFLAGS -Wall -O2 -Wpointer-sign"} -]}. - - - - - - diff --git a/priv/binary_tools.dll b/priv/binary_tools.dll deleted file mode 100644 index ce0fce177bacd866f47cbdbd0b050c7110184e8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114176 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 G$o2rW+E%{+ diff --git a/priv/binary_tools.so b/priv/binary_tools.so deleted file mode 100644 index 57345daa683d216a48c3a6b984a223292c49d300..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21104 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

Z74C&u67KZ!((Gu%9iT$8EtGPfC^fB) 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%0D-RzTdDcqwr=;$_4=h?f)hBwk6}n|KZJ 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 diff --git a/priv/bitmap_filter.exp b/priv/bitmap_filter.exp deleted file mode 100644 index daad71886efc695627e77e12e6e765c7ceb7f077..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 702 zcmY*X%}OId5UwQa-y&*m9`-P>IYg9Z5<`fh5Y``d*)@t$PYq$5>4e74^w87M-~;#) zKEU365}!oy33k7pWTGkR_nYdjo~nwTiu4b(SS0#N6JjC?Y%mRA&L9!wWeP3Aw!thC zx1&L&JB4YpK=hUeQ`@GEAI^0(@=r*kTykvQ-bys2qeyg$D>zv(X>mMMIQ=wzP{q&-qP7F-y0ZKXeP;#4emtA_>Q@Xv`($E8^Elo?NARBCL{b4T2s`Wig47td#|o(?RM`uIzYhTslvAa&-G2E@dEX4lQt=~ zp+o4{q`Ml{-{eIO{yOkTN=zbnLF<3OGz5+kI7nq-=LaIX+E(Z;%@y5FIqjDfPtBiwSTG+q)xgzO}D<5y!fJ JE&aW({{an9TyOvY diff --git a/priv/bitmap_filter.so b/priv/bitmap_filter.so deleted file mode 100644 index 8d26c84ffccd0462a8383a90530322b79342a43f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12528 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 diff --git a/priv/bsn_ext.dll b/priv/bsn_ext.dll deleted file mode 100644 index c1179f5efd7f6fa713e7ba676e1286fdb3359969..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95744 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 diff --git a/priv/bsn_int.dll b/priv/bsn_int.dll deleted file mode 100644 index b1b7515915b56ded85f878077a11f956e1daef06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93184 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%0A+4#kG#EDS^M#DT;qtG8{x7sR3$>%h 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 ncKNTf#r@oP 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 diff --git a/priv/btreelru_nif.dll b/priv/btreelru_nif.dll deleted file mode 100644 index 7049a76ad6b7b581e0e714cd5fb31b4c61e848d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164352 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 Cb*Y;G diff --git a/priv/btreelru_nif.exp b/priv/btreelru_nif.exp deleted file mode 100644 index bf278d4d5fc43b7c50d604e935702b219c739555..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 692 zcmY*X!A=`75S=7YD5^?w>w&`x< zEE0oBTU?s+5QQ)Py`9 z^l>Q?Q$dHj{Fl+O2}a(rR3i0i)7H)wf8F>>rjtNPx%-g@<7MJL4*NYjG-gSc!3N#SSa!fqFK|rnxIWx<0-N>2Sp9bH`Uoh3K R52;gU!7Ppn#z`^j{eS1*wWI)m>9Ak8XHO>rFb$K+HR3eN=r&Pd*kHE zEB}FigyGbqF(w|=n+GNOCwRd3c4xNR@=;1;HZ$*I-pu=+z4N5htZd9glV6p8@#TN*-?zRvh1N)Ha_+XF|2O1D3q)gtlhdigC zL5D$KgtVU5o0IH4-OFT;JAE_qNabiP8V+B=snrlos>Dg5ez#evOpsq^H-h-PukD)@ zpJ9p8OHbw|B zNS~5pz72C}GU8aps-ZpRzW7w;YEV`9uW4p25GVd22n7M3zLGE{f zt3Y8?V7(Pb_z;^0T-rIm@`G(?ne)`&^OonCX z5CR#i!(skiUgY50ff-3L(D91a^VevfS{$%?(v}sf9vy#wxnDkbR8cWzT;%Y|E4Z@ zJ?f~qM??fv42vWxYE;yyI0gr`V;CnoF41w!|9z+G-mdN>fX*Z8`(Nwj_W5c%b?Tf` zr|Q=2T<)HDMr1^UWq&$YBP~)jD;1KY6Jp19)ZZ*C$x5&~5I9;J(udtTK# zJ^bER=dLMWBR&^HWU>lMMqs9T^88~ zd`FM6I!8ny(_-uDqM8oTE|HJ5GZNxL<>gki$K{FwT0~hKkIVj?fG;t)`e~rQz7N3t zBwVqY<}J?nZhnmiF&Nh{TnR?PaO1oA^;Clxf$vG0K3(5O;XVe}SX|?9aUdt*;^!=N zv(DCc`!)?<&egxzNRn0{V%vuaHZl(!Zx`aq(sT~) zx%&G>xaZ@Vhid_@g}5%kbt$ezxGu+Kf3ConCAhA{b(IE};qJ#(g6kSwWw@@#bt5i* zZc?|9e{ThKn}OVc?{{kYE(7P+dvM*WvHSF$xBGEDfa^hA594|i*Auv&#Pt*|y1{2~ z@$;;@S!;1$r@udk`+8iJxHjOjKU?snS`#nZAnsdny@u-zTs641;o|2VT<_wl#q}Ys z?YKV1wFB2Dxc-idpHJ0I{{38kugCoh{k;MAf8hEOS0k>kaQzF{H@LpX#m|rGCjb7V zzc=GEz+K7Q+G69YqlU9xo9yxzS(j~PCH%J*?=OTX&&LC?EJtiR@`!V_PaGx~sK7k_rr`CY!< ze9EWG5B#E2@~RDQCUzZl_60X~yyb_vAMJ|SczI}MgX#W!h$G-E)m<>zbde(oa z_kwLd9@y{fOXpnnQ0F^;T6^u;y$?SB=DBArSo1{lStDjl`QXXFeDUg=8OJYK@b`c9 z?);CGVFwo6Qux^qZ(Vj|m--_wdisC?jr+Yk^wnhynXG^ zMQgK5{rL;ODtuw%os)dSUQ0+BHRSR&U!3^F_sgEXu{qkx7;@p*_s1VG`hr(3-EiBu zYdSYJMO|36f8U)sTVI>~(x_`58Pc)A{Z`BYJ^nPV{F$ddyDxM8&};j>9F={?wc9SZ zXVT>N=DN>%GA$$KmE`j(YQH$|mA8I)aOhiq`O7;G&);w4txr8S;G|t|7XJ9*{;T}Q z)}_`@T5)36m~o$6_2C(-=bssW-LzUjiE ztEPN&(NB*)JpS!&*=sM^dVkGNrycUz`h!rmQ~E$ee%z9x_mn< zaP$lNpS5_`86O_9>7m}2b^FIhu8%srFmS`s-$(D?De2SY#W(&VCHp%6Gv^FD{@cHA z?D5cwK3%?@@%+g9K6$J1)bi+61N{Ga_ul-w{&jr+iu&vh`M#?cHvMqboQnsndnMy~ z*Q80k58f7a%MYI)zWSpsgrdY8;E*eUb3S4ydi5}sJUy^$hgG?1=Y3R?f3q@ zPd_*!y5HFy7c2|JFSz-Kx+@24>*71`-5(A=a?{rf)@G+2_>YwOt}p(u=E1SI_3Aof zeEy#ncRzU5dmkQjLt64{!=FCo>?L1Reevqpp)sF+d-%#7=gd3e&dkqFf9u`-j!U^R ztM;PlM~=SEzhU*-H$Ta}FXNxbZa8t|e|99#h^m=CwcxTdj~{r=Nj;|@cgnDTZSB7K zxxe3V=}F`7dhO2GOV{RnG<5Cy<9}Q}_M4`tTYB7*y>?>E5ikGz=d;INwr0@Op5w}w z_M7wMKYy6F=Jrvm=kNdFf2JPcANB0y^0{Z;bKdO(>yP`#%BX8+U3l~*pN;)$@+D%OQxa@e!WbFNPNa7*(EU-_=|oqpwIlNMB_RxDrI@btifQ+Ea$-u`E|QGMOss^^cS zDYp6PbEst<)Jp2v5tbF*3J%zEwSu3G$)vS zD|~Se#m}-1s@%ZC6+WH86Fvv3n>Dhl!uNY!0Y_;3?LRAg;Ss8yWR3qgN#RM|75=iW z|ApZS|BitaJ~p4T2PphvEl<1M506#&ogb+Dqt(sYq4_M*eA@iHoT&02sP&o-8nlH0Ji0G)(I;AYJ3D7G4X2|z~W=e;mRQj&(-#Y;__M2Q{i8}uV{2%K1U2w zcy^0$DJ`(%a3 z=<%iB=Vvg!u%55A{lYxu^MKC(h30dl#`~PB@_(l7uDizH?x65XYZPG1EmP}T`hkki z50Q_bJG7o;hv`){7IEa~Vm&UYD!X;GmVdauMI5E_Z_)KY4dk;_=f7Q#m#v3B&Ht{$ z6hF3JEq1ATwtuY3U!-oM*T85U+e(omBpS=HK>j z{h?p1XWbhrUvHg1Nw*trk29eF%wPGf%6M>Rb$_Ri!bj~;v|az~{S^N5n+k_H%O@Hc zSkJZEpJO=W^E&)0@zr{~>~Y)_tMI!6DxWR43uY-ivbPcd)J#6*QxtyA&x%HO}9> z2M=&5e2dmMyI*%>qG3JZ^Tp+ei->Q3OVxW3#*d!|Co4Qt&ujL$MY(4z6i^9X>!};A69v-LFk5IU5rlIZ^wH-Zh zw!#n4^2GEfpBwNm_2j5kiq_(@Zl9|1_ik3W&HqPJ6rLXDpU=^93!m?PJXYnuZ-=Vq z1$DD_=s;BHNRRPpSKBwz($<^ax_v=Xv9OeJx=PI9F&t35f zAHPfCFKhW^Bj92FA)hMzIE|+sqVVvzF7ic+y zZTXDpqwv03ZnnL=qsJvy=O3%{_aCM5hsSmKNN2zDwSO3?^S?Vr;hVL-&5h6r$0$5J ze!fNPd88hf<8=00;aFIIczixTRpAfndGi7-&)x$R9v(OTEl%M-exWK(*8CiOvck7( zd$Gsy-02FB)^TQr=I5kC6&@ZpW@2I`pW$)!6-f#Ym*>q|4w)aTivO(Z@2u_mtT4Oo zsmC$gUq$&;{WEm_OLRR4oUib{N2-n%YP>fb0{IM&!>41Qi4WRe<%heF&!3VM{)Dzi zjyFHPfeH`zV~fvF_|N;PdXCcLa?4*79v)X5uI(s1K0gV?$xr#~syz&=e7=T!qOD#n z=C9|_4)br<4iaiCpX0SYhp*EQJWKPV=hJaI|2Xa6Hfl!&cOjoQwS11$^0EEu$m#@k z`M4%1e1XPCYaC)j{Lt09ppRA%jo*>1^7qi|6nk7|bWwPCylQ#9v$Jxtz4-;Hc?Djt z<(-w0ond*$O`YUT_vQI!XXF?7@}^E2otcyEo0>W^)2Gr}Pw}QLNM!}7nHd)&<9XTh zvy$gozU+)y-mKIMecpn?Tvl*iX2IkMC%f}9r~2};ro?$wj&w>c<@>VJjml=Grl;o3R={jufzxQ)4Gcy$_mo)O7wK(U zPVPd87m`i8(3>{rLhr29jLcx2g%Ig%R0tupUG3;NcUJNl^O7N@o-fmvn(qr{^iIp3pOKyJo$Sjm%)$_O zXD?Xb&GqHw=Wr|w7JBE!lEFZuoBWX;rs%BHTrWm7 zH7nm44x=A~y;BBz-BSvNWcu>+$I189aUgS3GxEkw06k?Gx;Zf;e&{eaKa;17@+RYQ zLo6Iln<2%QH!UZ-AT=Y~Cmbnk-|^y(i_6TJ@5}Sf%qh%H2WzLpcz#=s21AKM---gfDek#7oO5DZdq4zwrS zHbR8BSQMI+xLpQz zF%#=>>bLE1NDL4kuL6$3?2L;FeeE0ygv$Q6WhF!^af!h&tbL=SP?7wGe1ym&D2~>` z(Ks){>|i*`@1_wT8E)4OM?*XKKUuNnxb>MZwAtTIt{DrQkhgV4N5}8&$_0+ol(W5T zGu8=uy%QGp$0sP|zRO^~Y>NG&{FFqZsHV+qY(=+?$Qwz@yu z>MEU!4DWeZ2gZ3Z_oQQ|Ek9>ga2Ly-evCm@aR&D9reZ_nWJ6*1v22}1*lyM~X>)wo zt<2}z30t?w=iEP65ctf+3A_sOidk^=1vZuM%BvM4z?tZO(-6JZ=ANL7~546L({NV zoo9v|zg^H9H)OUC+soL9^2!XzxG!YicVBD8;Ee2iAD+{wRjw@WgJG<(=$$gg>kelY zJ3&(tyi#UQleK6+dzOsL?VgEVCVHnDj%rVVZp+YH2aS6f^zPYnI8O;|w`BNN70n>t z|7DHZYTdTG+M7o0T9hgTN9Vs@aH2QAe37BJ`0XraZ<$U|E2Ghdj_qSx8j_cj^LyJ) z=d_BrUbfWr!;yU~h4|QvY%G@Xln>jg&eN=4<0RZ}Ml*@tDHGdo4CCS*?)m78llU)m zZNW%;k0n|AUTxL?`lp(-@Q@YEJ|6wVyo}j%3bJzShcEU6xprAYrs28i@67-cGqTh2 zd|AFw#`f_j#Kp^VGCirwTuZh513X!RznU`6F!$eq4P|AiP|;T9J^6!uZ`FE%sGpVW zV_izf^vyC1y7k3dyuEl+%vSm(Bn!Vs(a1dSTH%allYn7@MoCU)twixP=&(z}*5Y?Pkd^czJM0Z78-+hvSAi zTFK|h1?tq&p1eia!bq*Crg}5PX}4?SgO#8ix25J6{Mvv`u{@<9Q4Xxhu`BzTZc9=8 z24@#$x?u(ATtVE)UYtivOI_&2t__aVGv}i492snkojp}TUosQUYG zZ;T|^rwT&c^{+eT{7XY4^I$P&_C4*k^5mb}ox$Cg$rIEztv%(n$4VZ@EK+B+znF%m z&B@424~ZGuWq86jP1F?rJ1`P1wDj(UHy^usAw#l{0}W#z5$}Jbn-3^j29lv?U|Yo0 z?Z1zxA9Qms;1_cHN7<-}|J`lWBqE$Qx7ym%V-4erM(w)C7e6!|@7&`bDvvjI2W?){;fA;hH0*3H((DOwa@NAi-$w|dSHgxds2>mI0q^A6g{W86Lel5 zPFzkfw5ffjLmqyYjU1k$7~{o>J#OmE%J5~T=ObzxqaJ4|0S;ETpcSaS7jJGR-rKSd zaVDf^&t*AvY&KqaY(ky^J!Y;N)33dGk~A(#QR-7WD43AR+xYBP(Yf zUpt(IS4*?gaQFuASPkKeoP5#B;D6@s%3zzvDT8X&+k)-ZV=rDzY~^hm{bC}{N{o&h zhi7;*C(evRP>!yj1>Inn(4G^V>@)E(vLVyo*DlZQ26)2net;+c<|B+9+XZl#`Vp!( zHDAsJ$s5KT^}$&g+2fRRf&;|=)IjBY+htZ5l0QE+SG<3);e9-Z#wFzWW~OGQ!moI9 z^1S)EcyU=B?E4ilz2bFWbL>2clJl1BJ#+K)glLUE&28`Zh3*YIFX|gJtbQNE@x6Po zKYwzIz1jrq)s7OqwCmYTQ*^3mb%HYUd|+|HWqDZ@Y>4>-Piq zv>ny*@SmK_A7c2R!GqPSJ2UZH5qS%}`8eU3HYYR+G=smra9N>0kg(@Bxk7(fU@tb= zxG~zwJO3~mw|C0ecAw!-&d6{1{CZE0j*;%ZJ;H?J7jk+{+2US|Tbsu>cNf18#aKAM z3K178{>v*fPDa>*qkTAt$3_MN+ZM0I?CS|6wt&_%Cod!WLbXg(4esM+%pXw5 z%dwv=?~dUF298d=p=R!aIe9tr@m55-dVNEyuh!=Lm4o1E z^;QpM*dc};1Gn^)|HG#n_Pk7tZ+)4lJ!i{zTl35raWCg!zRwI-IWrbhSNzPJoJ@O< zg$GTC@`p|^Q-=I-&0b9pd!Am67o}w_ik1)ovNfd8IDVG{()zMak4Ssiu|1b@PfaFL zuH;e|{FW=ZxWQ_AwLMI`Pw#{6tx@B7pB|EvRTRmY-#f>bi65*Q*J3YwpG_KW_q@qd zy&=c7<62>8j?UNaOYr!?&cy7MbKZP8p2tJOH)Y!;Ml!h}6J$oX3g>2@$1Xf~=uA4T zK80x)fh6KrWbmtS@(7zjYcOKi#}bG;**2)K)Bmd7ecpPA{P*y*r+rwG7gzV@;ge#g zUDRW!-oFZce$p;Y$Nk3JQhGh7=0F4vA@NCj%)-hvWpB0JXS;a11?RC&_!hpiL$P5 zWH{s$VS7IKia&XGTZ)JU_q1>_&eCOM*r(}~toKy3<-Vu9&u8v?wvYdIBIXwRQG2)h z_luTy_LH9A@=gac?X!U}#M#QQm-g*4L&*CQ;qMRaV{Z``r?%V}GvQJs+}q0Yar@*%#$LbCvD?%A_U^DVFQjGCL&BX8{snW6WLFaw_qCjYvG7HgapUD{Hjawn7iHpx zdcD~8mtWELW@cRI<0~-uy}I}~pt1pt-}u8mU}0K82HtNQhgV>b8kbE3FUCUXIN#Jc^CoNgQqx zJJ*MsWWi`0ZVE2jLX2^*cO8BqLwi`fwGR(?_Y6|8*UJ zi%z_`yJ83%+bU)|hE~G9IGo@yEFW&8<-_EC?8^7{sc}f)C%;0(2jRA#ZKaP&onSub zRVv*>P(Sx^mwxGQD@p7-cYchu4vEr^tTCDhDzXIAICA4RT;ZmPyMdX*=6vY9rx=q^ofw*K|9+%>;Vs|0L|WB zsTv+Y&;6W1$4zeU$^R^PI{t)s*vUwku3F5VrOX4JQvA(v@41B9Yu+n4?9LS`hl5r9 z{|<*EtFds0Bc6DQ8S~g`v+qJ59A9WF^x?^dmSNdYFC3*2mIXE1W>&Ux_F1fk<$PNV z%dwaiab$wmcIv;gZQjn05lP|wbIAk0G zEc?CLa9q6!D}QPSKU=T>zdWCrF*Cm)J?+F32j%As8b)$(@!5Y&Koyqu<>*e`S-(Z!HLw|2UmLIt>4Kv&!!Z!_Ut?u*BW?V1FtjiYYaSK;0p}A(ZD+zc$0x|HE`>qpxz=da6VB6 zP8+j7(FTq`(dm4888~gg{0m47`Xj6#+jUC;M(;_DUX5M zzK;pX2HszCs(+>%`1b~$V&D%M_#6X2!N7A3-2NM+OkQB%CmQ*S41B(UFEj86!|=)s z{97acasyv!;N=E>kAbf+aQkmBGr7XR#~S(982J7MUTNTy47}RFcNut%f#XlYI-gnt z|J*?>tIojnpPH4_fPvp+`{9ps`W#G3PxXZvVH1HS$$Dhb{KCuSwbx_MnFz^%uPcrcF z2JSKN?+iTIz|S)9=?31zz*7wTZUdiV;0XqvYv4~B_yPmpVBkdtzRT z11~c0PYryTfgfhzWd{C$fiE}k_YAz;!2fOFD-8T<1Ftaf{}}ih11~l3N&~;oz^e`X zBLlB7@I(WzHSnPZUT5Gh8F;|JV-38~z;870CIf%Nz^%fd{vT)HQ3igvfkzwo+Xmjt zz&|u_mx2Gyz+(*j76Xqp@TUzt!N9LH@FWADW8fYG|EGZ`8~Eu4KHb2NFz^%uUv1!X z4E#s~&o%H&17Bd^uNruffiE-gWd{DWftMM0g@G?O@D~ld+`un0@D&Drlz~?m_*?^D zW8fDVc%^~=Y~a-fUT5Gn2HwxWYYqG=1FtjiG6N47_#gvsH1Kl`yve}tFmOxn8+QhF zjDbfPc)5W`8~6tX-pjyC4BTblE(4D-@G}fN*1(lzs>B2XKgYxko9Cse+2vk{L zBlKj_k)$hy9#6Uh=?bAok?u&kTF^^PnzmipDT19(ov*Sgg%_~ z{-l$IK8W-Iq?3d`fb@Z+V}*_+-G#JE=pSDO&8263w9sFZ?nc@Y`U}$CNjLrs;J8mn zN0Y7-`UBEjI@Q+*{T691f$A%TeuebGq$`BpNSYyZeYw!XBJ}N~83NQN3w;A=PD%AiLSIdqF1tQf=*6VzlIvYUUrd@Vu0C4m zJkm##wuGKbnx5-%@q34o5jdbI`W&BAek**W^Jkksm>T86a zOnMaQN}s>1d(9CY?>% z68a0$Iiwq#Wc*3zlCBf_1JVrn>T864i*z38N}*pNolm+#=#8WcNS6z}j&vdEGND(J zo=3V!=toG;C!H(weWVwVP7(Tc(hEr^3w;CWi%BO5eKqMzNXH7jnDnKjT|!?>dJ*Yp zq4P*zM%og3F6qljH-0bUPr8V7ozUlzzJhd((343oCS57?c+$nBD})|JdI{-rp@)-R zO1ezwc+yvrE)u#w>8nWR3f+hFGSVqRA5Pj&I$7w0NMB7lN$3MemynJXI+FA?q+LS) z_!8(+($PYHP5N5Wme602zK(R`cQXE@%ShJ={Q>FgN!JMd7U>&ER|@?K=^IH`2)&W? zO{B|(UPt<7(q%%gBz+6%BB384eJkl)q3AOi=LeC|A59!8lW&BB(ldco`Jks})t`T}N>HA1m3O%0m z{iG{|9!2`Eq|1dKPWl1TWkSc3evovL(EUk2L^@aKKBQNWP7(TW(hrkP7WyF4kC09h z`T)|8l8zNRlJsMwT|)o38T8|%qlNyO^b@2lp}!#gBqsXFeKqOlNXH7jnDq0cT|!?>`UTR_Lg$fQPudcCF6kFZH~ve;pL8YZI-$=a zy@7O%(344TBwZ==c+#6lR|q|d^k&lKLJueX66rFb<4JEJT_kjW(p98$h3-STnskcL zhm(Gpbh6L~k$#1AlF$c`ewB2r(2=CKl6DFG<0jCrk&YJnYtpZiwuJtI^c$obzn1YQ zT|>G~==c|0)-3u1`FKi4JOu|c%~RUxVcb25O&dWKP})XnS^Uvqo>5(v_2BWJpL-GrWKfq*zx5}t(Ngms`Hc-;tG}> z=Wkr!!7Sc0Slqv;)?c{IKdq+9z3b-sH_xwfH}*o-68E;j?i#-v?YnC|es@#xqHP^6 z8%s_~EWi7k68AS5mCUccZwT~?3kL&sh5$0hp{8PYQ^(@0Z5vVsOr?l&g2uQoM=a?+shMvxwzbfz%T(-yGX(V*Kq^GBqVg;($(W=-1 zy4ZuNSa%eY9+eK=Pp9nqN-F#hltrifGgCG7mP%;|ro69H)~J;CgDEfRl)F_*RWRjg zow7uwJQGa0N2knHDfb6cuGJ~YD&_iM$^xA-Or>0o6j4X5iH7D1tmSRM9w?`gGzAJD z1BHiz71p5tHBdB;c>v?Pq_Ut7zj{ihM|qZhSkTks|2QxZE_1yHgG8dHvAMYn+bJ>U zhYV~0mDQnS9kmwsCRJHae)W_jMXisP@>PK{mUoOO+A+KmHw-QbbX8ykLiAvkSY%PP z`=`~SI4E;?`C8Aq@ zf;%h=HkORWs{MtV;Rn{FdrCSU7bE%HO>vdQi#A6V{0Zf&$>Um-tM5j=E9v>b25D}M zrzF->I*B5cEL#w1F0Jf-Qze*m%3Z;7@3K+~9)i)rUjrjV55N;TSRx%or{85$Psq4K zJjII|t-@o_;04lP*GZ~fH1<04aV6Mr8S14VU8u+?(O;D~BDBQM5Ol*tRRVG&+tT8l z|Deps2deZGf86J%hW5+zL7cA`xvkgT2?ny#RBNEnH7! ztHRAd7~4X)eOd083HNNMW2mT_Uu1(_8|CJ7?rkia9O3o~E%BNvu|BlKy)4m+a8KLS zrf~bJq}GHx1+|3>_W|gV67K0!Y1EYO8h_y`DDMhWc>{5k7^W4Tk| z&cK%Iq8dwS{~&3zbm;2Y(0gcVrt}jw+4btsrn1mf!yHdZr^&$Ab4FzL5%51+9cddi z9>1t$?G`ISn+iH4ikpuPAtwY#H&GQxqqdfG?IEpDVV$mbKueyI838EkkxE(nG#oL` zzuEC~frjShiT*m)(iJ@pBtW;|_L9#({Q~9qrg*N{wL&<2VGbe*|1;_M*WqbdW-*ci5Y5*}5li1y5)P!Pm-_`_9Ap(v>H|z7&)Dv$M6>VT~3P5s>X{~rO zdR;$6ic_YdFzB8erQ~}B47b86q(As(os8_Ka$}V;tkoFD`hT%s)UJ|A6?!t2#4AL_ zA-B>Hr8=C38LCtdeIXEPpmE3mt_p}!jV3o(EHTB28>S;oxU5QwLfc5}IZd&5Mkssz zz+S_-s@AGNDqFF~U0SLNOmZ-cJG*5M`j>F181yyJ<{fL<^=bI(^t*P&I^)i@~X=#={(b zh9t#{YE=+)t~A)yquNEiqJJksk#5N^VXbodUh+Oo}&84~E4_5F=N49{;pz)959Z+!iTXP)jjp z)r04`(1Hu3DX50XkS$93A}E=u1p+3p&`@MSHqnAce6J1)FaQDKLI9`wzg+C6)Be&# zXplAo3bmI(T0}6CeHIngb4vq^-@jT0e~n_CLn-RL8MT>DI`LU|I3(n2V6S*|Ui%uu>-2<3dKy6Z&Mg58ai z(2drF^4g9zg|bp5wI-AubZucm84RU!gpv#WUg0nNEA)GL3;q79(v%g{@6ylExgAPV z7OoPZELWNWg@Mr{%k5A>Qz!yXV>xbuA{TOsrc~lPLKB%>5S+=NcLi6Ni$GQddefR! zRv5#gYNbRcN~>LuRE?u@8TMwVr^u^XDG_j~@2)zLSB+?hQX)HPu}Xrza)~BuJ-fE2 z=t#rl)d3^-%jGrW)1bV@J>9Cj=0ZDL$m=+k+Y5QUfPA*RPN%`Okk`LgvemA;DOU4 z%i=;Yuxc6_4iz!wCFQpf{v~FDSH>a;mMUt7Crc!G? z*v~_RWlfX?^dcy)YzsmRvg1-W$-iNozlpq`O@%7yl=l<`9_p62V3-_X>}fKgdzE>? z#ZLK^FuF*saC0JzfSqL^w9*y^qY5b(fO~A1Dm_(_XvFtMSXvV9dR~cAnME0vLFg?i zxu2DEXTdE=30RzUy1k-hzrIAtz3i6?m4rVJt4_vTbFx&~qEpl1L0j;1Fbf?4e$aOr zUg;EggRndO{A5esCjTnl9)rp>%m;6GNR^5=%C!{3jzNANYTbgj`7AUloHuT26*op; zX>|DU^d>cbczN!r!HXtqpP*D)i1!C5V~Z}V zVxj)wU0{#TW5DUh3ZKd)PIU#%DvZ^g7@l9w>PVZ_fvvN8W^1hO-zKZad=Sp+e~>qv z)x^DLRRzeRv^T>2)!(a(@$$z_!PY6!R0Yagv!nA-t|3!~K~{C#xY6b-8gL-|3`RpX zZm1{cOWj>hRZZA>atsEc;ds=7#%%2mi3wMsQ!s>YA8Tm{<1k!$Xi7UxM23*v-X@Rp ztVEud(5!$7Z_TFuMf(^1_s}%z+ao2o5OavVFwO#(U>z7b7CFQ!WVZ{MF*#1U^fJg zfpK+S!`d1;t--(6%bUvdGaXf2`cf76?jvEK)wJ>GBzBEkO>S+%+WR;_1a+4pA}*a)!Jq=l5`?Q8@tAAv^Ak>toRuN^ao$999m9yRqSMDPCL| z%ao^8mqL2Wkz{5SQ#E@0*u{G<;O9(M0?{xpHy0DYYAuGn#)IXI$3Lmk4EfX!Bc?ry zlkNjw?Pa=r=LV3%)&3njh1rlbCA;+ZN_>xz@1@hR3U+@^jw7~HMckk9`_T9JecI=W zmd(bM?@F>{J)W@!s-=4`!xt`d)dqe+?}o#<_S1F#M`mesrYaDRu?ox}UA#HLUb<0* zC=sVOP3R2tuDNW6GvW|u@Tr6;3PdOH9d&G9f<$E$w?ps>3U$_>0Czl~(gr+7X;GhS zU1H!xi=Plr%IZVwx+PdwD@NdtM*EE_nr*^Po70zIUi4Zr@i9c=#zH*b#bZZVLp8#G z012_0pe&-r=qNE1ESc6+GC4|d+5;ov^v;o(f7c?#khN_X0Upy3lgO{|elZL2wkH*XPk}a6f39 zkz+4g`wPuX)HhVWhB>X#A9%w3!9;AFP4rKyK+#$)t_n|t4gn}C*-V4gi4ymt;WDQh ztQzfh4&sa;Pr(8htbiJVtwxC)MIj4E%_Vo) zbryfa+QidxgcTuPHo2G732v5F79Oa+)B7?T>f={kr}#8oT3lLG{YcC&!Q$PQUWXpM zNCzFbhpxcCNj6QnXtB5<-e15O>;*19;s~iu!**|&r2?(T1UYGqDQuQ)jMai&Tgz6p z;zrir_yH zUZ$6Gz8CL`c03krDO+fwE#2~VFrRG!YcL~Ln8qtJ;fyshK&6PHl}3KZ zo~hmPYBhF$V~wj|odV8hsSPRvl4M>Ky zpuevJ=Q^WQ9wY;k=rIm#3S6>?50X!{A0)4Zzc78P8s|oj%vk6M^}m(bjpNh{%)?d- z>yV|6O=rD@Hgseqzy$=5a1$EwZ))hot!Y}XB%UUT+#e1&4+=^Y^YBrddl^xKzmO+% zJj*eT5uOco9hJc~c_L~Yj~}9W-$2Nyw<*krJAG4b36B6}r3b&ooy*DSkGLj$PwK^f z*cp213@&6qW;Gutx*H2+MGg;$3;;z|7;o4q6Gd`JR3(&4W2zH;DnbRHC~X9vRY@)_ zsYb+mz3lJyIN7!oVw2+iA4SK5xneOuwHTlp(~uZPaZgX2p%hvzMkJvSatk!&H4$KK zcqmjN)1DojOa0+>a|2KI45gyOm4V8Dzpk}mm`qWjb6|XMKGpMi*vQ&r2W^@MYxMZ3 zI8CNOoVC>+belh=6ONQrSRsR(xN+VDdmu5;r>XCl`#>D&5E9A6tHGay5qd+`1{gLD zEpGAD?d7I#XleV6&>59IIFN{dodHHQGU3Iv<$I=6T3C}xbak{ej8;bx^>f+*Un1!m zW#AyI0^2BQ6=mB;aBR+mar*FRG^o}f!H22Pl_LE#)#ESCMPzP{u3C&BLsj5L$Qr@k zv1niDG#Oex1S(q31t9aBL?iTfu(Om2NjFw$j@oUnAeTH%pH%57kq5c(WK!vuuj(W` zr-siMfqIh)&)DKFw;;3SC{&@l{tT<;gZ5I+-Xe{!sUv*ESc=EYrKnnY!B^p5>LbA+ z>Vr&|%2Q-j8L~*pIjXV>l(eA-RAm*^j=`#3>@UznI0E{V0RD?KFji^$^!l zTili4UAO|X2QWJrZ-lB&Y{m*U@veK zRQ0zSoqiXbHbk?|J6RC-n|No&u&1t%v7;eRT{)XMnrVLD^Gx?5Dl@fDG%qA-hh_Sw z?E)h-f=2B>OQ!8IR#BX1rh|z!P)&#nqVP{DZ>f~!jBPQXOYWoc;VHMLq!(@|Q5E={ ze!)*uL19_kz63cP2Qi-}Xt{FDQU$UvfQl@sEbOalL1oj*OD0Dt-MF*bt_s5v-Ed18 zQ$_Ps`T)inqRfu&WT+VpwM*~`?{BQB$^t4lHymk(#N2F z%2t5q2ta6~p<+;QZh8xTKkx=lpF-WNbKM1vuxr+}Vq3NY&d2JtTrjE-RxMT?P_71U z*d4&sTAGaQZ+AeAS%8O5f>{TG^TbVy!pyFaTp>=uc_)e?g7Ghd3wxGGw$-bpJlC+) zdo&76H<;8+$5r}%9CR0%m@T@Q7%8cq^lLcLIu??icUj^xpwl1V5g zj^p*J=4RPG^Mu7{Jt19NZf(!~Pa5`S=h6f44SB`)uy}~6P#AM(VVw+q$Uc^?qlU_e zu|I?rQ7$ivgQ^NV3!`v0{B%i|qo+F9>jPgdPOVuF%YKeF!E>npLA&oN^ZyMKykkkFBw3?(Ot?mBfS&Kd5z$ykhNyeNQ_ewfY> zrXb-Cv_inYzHG$sS~J7q58dgDIl6E(H&r^al)m~4cYRVYV;;7jxoidBeG zRe>n7BMa-$z`qh=RDB3qogK?7FVdT%pezg;Zn3ggQ7Ca!=zbOKo{zWeED7kIj8~nq zQ5!GY!ftaQ>y)Rr6FQ^UsN+gWQ9(=nJWhh4V^o8ILzeKR0iY&5R3QV$PQ^G{=$vq_ z3N&pb9}HE%M^&I7T35j&2JZuHiRrLJ-4>5y+n&OO`k?xSjTjNQO=Sklq+ifQfsiDF z922mm3qGQy9P@`++nvxRtw(#u{3)T#|HR-)B4LC1TbV}*%05d@VI=HMom6O7tw*^K z-B%IJQK(3?=pZGLYR3attI@TKtc0zJP;KBVs9oYFvB>UAjz+{KH7PB6ht8m>o0xm| zCHh=33UXS3wpRtxsk4cj@Tjh|YhaXRt-Bvb)9@tY9>jon#!-%2Aa{dw({Aqy6p5(x zyLVQ>574!g!n}gk@iq99u)RiE{0$;GY9qQAt$1^q|5OqsRJi&@X+^kt-6;c7{6?no ztCE^R35k9ymLeN2fKK4M?+ zd~86p77fWd^n1)92IO)$>53rbHt39YNM)K#cVTN&g|*ix%YmhMZqziR6m zEb|o%hw`2_Yn~Ti`C==V;hSLri5r#uP6WS86u%!Q3co$cZv}JH{1gijtVZlH)Uo_o z8ng|yeqeKJT^a)6InL%8hhrn+mW|n#viLV9tHe#0Qm#ql^BTqH$%@ara0^5keA3t^ zN5N*5{+B)0vctK3nS)IEs;i@%*fK zR|@U3t)-A!1C&#sTng2T9Ate03l&dr4b@!&&q;_6xUS@)sw%JqOkpoft+0E+PU^r4 z+L1!%qEI~_6d(uJ%zS%UvILK|4^j2P0XXZPiI9d8su!ClUyS~mLuq-b*Puw!pyrGj0NMK>83zU#|P`&;(pyi z4PZ+K)eB9OLG8ZeeGvg1ADEU_HP>Cl2AvWEQ36XzYS-ck(5?xcDY>EV4-+exeloP{ z5yj;Sj>4H@KYW^s@u~`(CM1Jg2*r*}4u_@-6H@B{);dwuTC8e4jI~a+Yb~+lRU!;Q zN#Tl;c~PwQPzV5Bi%=C~e_5EuRP=>26^T-OEOBa8U^ZN*Qf5k7lKO@ev1J5>f^{gR zSuFk^8W!|M-DUowa5&LQJ(scxZMX#qHabgjFFE?-JKf?r>Dkbd5W7O%k-x z82Efz14}CxgjtrXlQ9)n1>RW;lZt4ucN6w}6~Ruo{c zunwmJl)Sn8a0lcCAKa26;Lo@FIk5(oB}Yn9QV06G0L%s~D@}1YNdhOesILY_QoZNZ zXdlW!9z%z&Mt|h=*UD-%9=O9d8O3|9NqAA(+!=}i3(_TtH(?UU21fBdlGvrl_z@)w za9S1UhH>n^IvTmq!)R)fy&kgHWNklJV);RiEU5);wmx4pKw2MQv|fhMZliTQ?&u+2 z4@VD0xe02;mW2vlOi$dnXg_e@*wB#zF=EN-ki-h)Ze=GFFrI(HfMqpSPU=BW32HI* z93roE=c*uQGM<{lDtgDkDvkxSXhRBr4wvh9Zw4u)Ye6QuH+R32%64eUG$yQ24)v&~ z;nm!mBO2beGe0V2tL>`pl(N-MStgXLvO89?Y_(A~N6J>(WmANFUP6je_w(9^uR~w> z#y*yLTjBbl!&^^r?_6|b$)v4N+wzh^+*CJWQNGx{HKKv)FCFV9HPXLI$C^=9qPxan zP7)MzJ)lszFljqt4plZeL+K7gYRqpMq#O7zP@UvZQp!F#clh;)S zo??}>bM$&tRIz(|L_;+e$-20R;u~<7h;_4iVO8zB=++;0ilZ+qUWejRS!pt#?X18v zS^V6DOt7UG(=uSj0kKYj>8RVBtJEvCxzONc(BPtU<7IH?0TZTdK3L)rO?hjKv_(~( zfXYO%ZQyE*l-?-#I03r7Sp`G$20}KaBQ6*O-5$#xFyK&CL{ZiGTt%Q>xPgzVz*iVH zh9Z2-)_uuDP8`89=@oJ;lu}ZUv+MI9tu&b}Xo1fMfCi|@m8|(zjGyo<)ca@$ z&Cr^?Mj=F!@m&;rAKlWkk*F?li?fb z-Ix3W4V0jMkxZ1zE#Z0xcmjo5?D99X7IJS3=|(itZ|j6T&Oanof~3u$hLouJf0u%m?2mTK`R^-k3(+A#^aMJ={IrU%n( z@+#Df{?eEV21;+t)C{CTQ!uDhsNkTYN^?+0kv(%z8BcRiuZ3wK{Cf+r-nt*upl0+F z^?Im326g!3!jX*O&rn*aL#QNG0bi)>zGQ6Zh^C_-A+2qqZpbsC8fR(t#-qSg{Ju;( z#K$o3_5W7V0ag_#fiCiXFgzvO;V+M&o09g1q`Av&+AFj;Zf8+@K7EVM|4-E6ICh$@ z)bg-&kPI&_4RMqMi>@@9J3vf{ zLJDA(f6x$=t#E!S^Qk924^)npdqy?+$?HQ&=nR zr5@QK@2amc11Ki zP%@3dnXasa>QeSk;IDMY9qv zp7iS3+4)7RQ`s`ssbtApL}%&Ds!7hLwJP5d-1oVUjB_(VLc`E~{O_1%Y!f^ zdOR6blu26HPT}1SH_rNxk9qN|0l)jCf@G~$V?_H9w(n#;s)<;^L}PnG|Lx_c$WmD| z#VOXTE%|2*yf%|o^S=0=#^W?EsU49l4;n5VMa6sEiJ@6NFKw`=4%M@ghMb@h!=6wOW zNI|=TcCGY~o=dFs06KV75Qz$~nJp3hQuT9FDnmVu2^lVef?=-09-_iGpa73QQ0&j( zxxl(YHHUWTEn?pU=?6agt8_vdx>xFMW&$dT0vz@Vsnv4WCVT6tO6 zMsLBzNRr||q0*4JwgCWx8UMx&UmO!r^JbG;$4cfmkS2TBn-S&W;!;tx=oU1{)kURd zf-5jSc2J_`n}u==s=jxq)VkkmsE8Gs_OEhEl-jK&+6S2-(K;Y?l;0>agN{?VZ4nOw zhK?UjNImkjQ+gn!<-sr~{FWLUVL>6X_!vMdvXIQ1kk+a!u0~5D3lRcY!$3NZo1edj z_6F{fqp;F;bX3Hnk5EB!3>KZAkvWWp9r`me39!g&bAAhblKd=ODc2S!D3S8W1~&WY zw~jgve(Pbi`#1GS@mq%;4Zn39qz!d5A05x64}tWh@Iv!h@fsMb%u=E6da}hbW#`pe zy&ii>7QEATU#wHa1V zf|dSXNnH0u);l&k1EKm#R0C8wF3ar1A`Fv~#Tg;x8`c|{&POKO4iHl!C1fS>6K4qX z1P4REDr{TxU0yS43j`0`$?Vht_42@-y{Y>XYgWDK1!peD>)tk>fl%jQ(XDU)EB%Af zvwz~D^Tk~CK*-kk+0bK=QKfRq$ZgS(_lG*^W)xs@yv< zh89}kc@qR%laL?|JrW%fIP6K;2b@}r*tkVlD#ueAQM5_d6a@=5Vo)SF&p@If1fKqA z<6RUy=21V$wv+=3PZY%tMu9UsDCY2}FuY0j_#4~U0n|9F-ELgxP-;xza0pI>x>EuF_uoQu#}aqW8Q`u;W=6gKVOw6pa@4X4YJR)jK7 z)ZAJB&1NNAn0JhShpYhcdyBLuJp^Ns9btNj`*|#tZEzzRkwTUJ_u-2s`achxgfe(c zfLVY~`c82_zbGDeqs-56`{Z_e>5xdcf0!R;Ta?EBEmGt`!4|ZlW?dP8t-urAb;@n@ z#=LeSlDEiP*-~6GJu8LrnBZlYt7?5GTECnzie$JF8BTQX^wT|5Doei~ycQ$3JV_Fv zd~HRk;IHd3Y&yB&kFWTk695q3`yYBrtyhsz)@2tfgJhFY4_F%1Imr$rZb? zZLtQw4$bOpj=sVCg066N#77|Amn>sj)37b^ys;~OG<*rRNU2NtoFcX<-8i79=sN%E zPT<4knEs`lXnuNnDHRb9&d`*;>v9s)AZ7aZOc!zMjE2C})954%hRhat&wI zTCRgsFHE@}fn+X-l7f3pZQJ&jP)j988K#d(YU{k8{oSGZdw37&@A5;?--cVIc^R?O z@ZGTOSdhB3kEK6RrMDj}r7vdb$Boh#VZ6n*rR_9O+O}g@`0uLlJ*sdD3+JM+W7{2= zX{X9`vC6a(!xETaw9uV(YTJGr%e8f%s{G?s{=TgA2;|p~*0jygwuPk{42@WeIkHFG zLXE-`HQKghdzd=LlOA>arL_fk7FS0tq9$Ty!%;<@?|SCTf|b+)5&1RWtR)~+swg#w z8}DJUkh0A4s35w)NMup#bt!QYl1_v@I*#NCRw$0d@d-y5#haml%uh4auIfmLD8y(N z{TavD4VUq%U50SD*I184o45OnjHbS>W4CGO_t-h3=%dxsQ9jZHEOJam1%7v+81Jet zm>6R2hoW1UYJu5;Wr?qqi4TWNLUP|mE!Gyr9m^+R`pPL`$MN~gGi^4$0vjR?Y$2Kq zKYSBg!#M1cZBW~M-%@rbf?wT6(W8x~xj}M`-PTgO00HFy^Vp2;`Ya>|4PE?siw%d^#MseHJCHw+ zBsA&^?pX14O>FjH%UDH%%7Z4O!8KsK!awb?mW#0pcJwZ=ptp<`9|MQeO|2>3?-suVoI2CLp~iaAiwgb$3M<|Zn5x?TkRsE0-$3fpN(rS_Vb!?q zNY$GIRc{VtZ?+?Q@U*zd;5L*>bn7D^UiH*LGxE2xVC8va1XflJuTp~Nhw?&|>d0&) z**r|Jvx$jnCTF3ia+IXR&ej*%lniy2gN=T47K?U{S$a^~E_W$7vUlD$QZYUbJPc#B zlV>1V#cd+t^*16(q$lK)P$6-*Yl3mxqck)vzw4OJanL+HY4j^Uw5o5e1OMFg#0G+2 zGm@e_gfz7c)~bExI~aU@7Di>S3LKAy65U&MD6@*%p;_o6Ea1#cM2*_7(yVModswi@ z!MR#2b=M72V|!8;(bN$zlz?T7)@Dq59IfzrE@eGP;9(r7o2<-Muxq;gYuYIH4f|$? zJzFE{;}Q0hp-{+Tw>t_WlnMS&IK31yHF-KMgGYB(X(8UL^3mhGEQIVsS!%r}1J|@HxkHOJnVk}$Q|vv{YOb8%K|@}rkd?BEv~_E1MXWQb@ymvF z7%2=De#)WL{jpA7NA2W!Y=LAYJysy*s0v&HMQ9V$KuspM&*R0XYuqPmRU>+qX~OVJH!npEC z?jx5D{aPLnj5r216xei)bQkKH6vbAHm7ypmVGW^kbCWkFTyp3yJvS9rBCN&P$rNLmFzPMr6wv32{=%~?Ve563pit%Vz97I+Xh;dN#4yQ=bcJTMlIt3Pr ze%Q`}se8@BdMG;HEL!HqTS-cEj|n0iEF%MWdkN>q9I$R#%l3c37&_MGbQ%LqV}$@@ zOqY~U3m`N?O!AjWaHX65mOd!0MinTdGsV}YaM^cak(QmqCY&^7MpFe;M%~_)mbHmH zAUE8mavndcACiI^8o!|yvRy?LtqRm$E&AD(2BAjglWxDJrXF;eA_W?4D|Se|A)96U z`-AG=cCO;kIYMeLDN@sraArK=g%VP?)_)Ro@C!?Z-&arW@aJWw7x=Cx=9T zR8dD_8AwxJOTW3f0KJe+9KYSb7Fej-W-kox>xh7k6o%BMPQi5buv=I2Cfk?bO?vn> z22_~br8bQK>P@2nL=jSQEFFR!tYBGpgU!bitV14KO5^vC?9qX#O5WG1F4s5#`yxXI z7eVh#ewK>F5r8J3xzK0YtvKS~S@?Q%S-)>^H_cjW&SXeUtRR4&qhWQYxcshJt+7XB^tSy`zIy!jay`>cZ0nXJK^lbLR%W@hH3 z6=dXOTWNW|)B>NCmYI|9v+{hiGxAZVHy6bVvqQ+)z5=fe_|hc@A*q^QoObd}0*(3d z3#?4EAvgRpDG)Jl!l%13AbB!3`%FD?qi0NnL=HzFvudb0JTud4` zGA*Ytn<@IEz}(b4A5kRF$;eEP(WovQ)2|<^Bp9sl)Uf(E9@6^2k(s{KS#}G(({i#{ zyBA}WA5)N(>y?b4V)_km1;6*RSiP&a!?~)2?Jh8DRGjHf*jx=9$q@+YnaXFtu7RNp zvur_*R|E}Ngw|_!&M2?t)jz8c%9QEz=4H&DQ;?OD52>*#B|%$$vHh$J*Qq01HcLvX z8Lksuag?zd0v3z4s6?uYSVOoYTm$2RBnA(OdW={sKQC?INE;B&?14sAXe5}&8A40S z*iEBGn>Q^4YDk=A6X4yerdmCQfvOIdEh~zE|6d9^ug2yqt`5mpGo`!{v5ra%x^` zR{qH0!_$44z5<^&BO98Zoto*T3dOXZl}X-$g}J`|{aokI&nTGVQnk27gwzd3lIw*e zQU9s#yv(V-ysQDPiIbNQe)!e=pIj%EV-!}d8el3 zWak&S_M+aAE>sggblAvKLbwa|-04m@|4ca56v6MUN3M}GLdVx+HrPS>B}%W2{yRgS zk)01e+{U2Wl?;HXQSve;i549Hzd{S_{)H=njfX12A3!yksRqN61di z@@a1{e0Xk-s1_vpYs|KPD>df%X8AC;ry(f#RdpWUmJbMO^RKK~8Db0nD)E7ME_%HI zuE}->2Dp-ybj0-{cFRJ@;?2*@nU6U|IaQVj*Tz49-klQcb8MNrX-L>Jv^L#T?GgDz&-kdyder{&Qo+g4n zmNeQjeA~T;8=8&(pWEL}$Tsqi;`VJ@Xrcch_H7!0EQq{VKxF!6g<3*L5k#$+<7|)p zhX{jhSvXG7__Fu%p;#y$k<)Hm=Ko*vY3VorcWKL>^h2#6yb?{tz%c%IEfzw?a`^E7 zp_tJyKdYsDoq1?C@nc5z|HY33?5zOIN&7B-{8j5~EWY#G+Dfs9_YXCL{9i1!yM3&Y z|I3%q|6TmR9qQox@P|@+bi9Rs4z8aH@)mmY=j3H%U+660|9IhGi~fa8d0D9!;#q+_ zjQJz&{5mST$Nu^9W{0}!5CQzLmb~b7#1S1%*^I~d?;*&jnAO}|hwnA{&COAr@wC67 zxj6~oG{HxI!5;LDqvlW~u_;{UPt=HWG6Z`kl&=j@#9 z6C{Tqk%SyXOc4sjMo(_VWgCubD!6X0TC0G_Vy2ObAr0e(9@ zC+8{fcRaxk^Fw-YvLF%I3eSQwfth%IJP{a&=jzLW=Yacx3-B2B3Xo3WJO%y)3@e~% zm+%-p5qKLI6^wM?0e2dXS!Du;0fz&f$mcZR6yOnHCiJfYCjy@UmjjFA3HW|sEbt0& zD^SDPtSdk#@Dx2Q2mS)=2Ta1jo>{=T!1ci0z>~noz&pTFI7ns`(zNlw3P684Yyxxw z`vGHtvw)L;>w&9)CxO2J?*O~tIAcMaogD^@0!{@s2d)4P0&WD(1)czI0{#X(2kiHD zPR;}1C}2?>%*+DD0XG5Df!_j00SDlC=|bQl;7*{1V}log5x~d52Ebta`^X|VHJ$)W z0JaA%295>Z11<(W2ks^w2VpM)Hvu0L4-Cek-&7n(P5^cTwg*na8S^Q?IlxuGMZm+r zW58>`3qY+1>;j!YKfJD!44eq;1-t;94*UhU2G|9!2Au@@<8`k)z&N0RS2o%JD*(p= z8w0lk`vHf&hjIr_0pdVc=Ha zZQwcJ1K>Si@>-NT-Yprh9{C1d0k#MFZ9usJgMo{I6M?&dZvZa>Hv=C7j{$@6u8H3! zv}<6;57AzM5nEAiz$w60z&C-1f!V-oK)-FcAMuXSBwz&aTVP}0FF<jATXQ-NE7Ek4W1`3g85_y9NySQKZOKLf@A z9{}m4XUl2aAHed!g}@fTok02@6kh?;&Y_-wAzz{YDT($3Oa$%)W&*DOCjukBM!ty$ z?gzdJyaEilgnZ*PRy5EFoCr(;E&_G~ZUs&OHux6p8@La64EPjy6X?8*dd69>Ilu_u zFTiBrge$l|fU|+qf!_kx1NHAv&%j9F9bg>LaBA8HUksKW8f-aKVUX+7O=<-^h?05z;nRCzEXN!+&CY8%>OLh|q$*2EC(#*S$3qwRW;A$_jm==I==L4{jPz;3Z>$lIXMqK(@HvbIJp|>Y=zF;mvV9r!R@j`Wx7k23F&+Vo${D7HTJXv9cw*lhsxzK zbefIJ$;pP>WyiA3Rc^snO}h@hn5PY^!&Pnx;4h5NpKlNTvkAD*yuw#@g&zz4^2Gf4 z#o&K_IVY!(S9rq}emD4wlQ4dHrT@Gu{TIP!O-4WH#lP+1AA>JAB`4>o7k|*j2jisb z7VsZ<@oQat0{GgmP!rlW)v4-+PO}cbS&=y+!r68+^xq z;V**E_!s^$_%`|QPS}5n_R$jjB)Ii`805`%ZIK-er6Cb-sji1k6x=SpugpUPyztRx zZ72BO;MOO2*r4`$)1BApurnTW^U|JnGTrcx1z!9cE`B=r zgWxd*b(dpvxBWHXw}Q`a961L5UGVwlAN(2cWjyV7cH7snHaVA1cqhU{fPV^pINF4( zo~b|SEBh|0gT~OI6W#fxu^;%vf8l3=j|QLDSVH#KgRcZ0%OiIhQr&qt3BGtf;hhL_ z8f~Bm`0{Y;&oS9H+j9?TgLDdFU?v;m;CAuURz1e#80ZZ8mvp3oZ=X*(C?CVXr~eC| z1wIXYv}YRnyYsmfd>8QZJbB9;Rv`Q5z@Gu1S6g?2zX$$X@B=)d9L=y29@S zAB?qmdoRANi~kCI8h9*4-RW!MPTvFY^}yHiG6UExQ8x4o8=gQbK!eJ{%JROVw*M;iE2xZm^)^a^HMrMW<1_dQ2@ z6OZQRe^1b!#f^XAX)ogp`25;h7;d&J;PdO362Vh=x~E)e^thig!CwGh&~pr@e#64U zIFEBvpmXqb^evt?EQ4HSwF;YfXA9h}d{{QR;)noWYTm!}|KKCQ=XXE!0$=7|!cPZZ0eo%G_(#h;Q2N$@ zZvg&)CvVy9O5ZW?*TCo3-fx0G1K#NwzLVR&j!6jd?L2vloaj^h5#ViESd)A4vH+d% zO9tNt{2ou9%6N-Aj(*Vj5jy#$aTfTm^WiCu_27?!Ps<&L#~5%LI`^QH;HfjxorYh) z8}sw8$AU#$r%BCcY7P`s?UYs zCxVami@4haIJ-E%_!#%^x?w9Y` zIK}68&!vN3gYc=j%OKHR24kUf;w{vtXWw9L#vOwb@h%4c2>Xhk=C*OjZDT)lmM_VF z-gO21MezB}NuGi~1-`6jKGoWi(h!Ed#53T%=AG28o4VUn5_FzJC$F)BY;*(PW@)Z8 zk`0e_^mORVflhw)u?GA^@cGsEG4KPx*F$`+_T;e-a1;D2@XbAWi+g^oBjTOl^SZzJ z{-=a3%fF9J2LA}*TYB23JbJA2`$1uRg+te%-M~o*wP?ob3e(S+IkcNMY z<1}_7zO?W_->wUU)tc!7qYP!Iu`8b=2z#t!QXp7fBqu)tKbu1%azWT z+=5GvI&gsta{il+I-6&=)@IdFohex%#qR;G1vA$$1m&VOL!s9_uVCN92U}Ea>dr zl#^33ciRhe_jjA1vvYI){lsbTYr*F=FQqts0e=eoXK;I_!}77a%pK^|hi}RMK8*oC z34DI-G7bD7@Ohwp};prP@8Q}A4+q=QH2cO@)cM<#)@cH%mkHK%s$G#Ks2V>KBGx%Bmo5zV}9uuKc zWFMYoK;M-Ixh=zedM0=qc&|R&A}1=8$BE$Mz~{9NB7Qme=zMr5`2FB3fS(VyD}K6v zUv<~_HRvpYj@O#rA{ToUhlb}!zk`3l)5b)1cqe$r{`}X%N#Kiu@9!Dj(u)m{{chm9 zfbZ_b%grs~r+{Atex4UU#}$4R_<#d>-)~NYISl?8!l!wLr!w%E7vF?VCD^FwsUwrf z^`w_TJ%G=ztwn&3&nFF(hh*>(;75Dfw+wUTp%?gZ{}O&W_`cvrc!hV*hu47L13trx zcdw_9fxibnzjk;NJUuI{GgG)1F$-{rx!skCMc{_qHx|5Ex%#N$%_!3UtUty%ec!jA7GZgkwI7Z=2g^LufRhX^tgu)97Z!3JH(6(O1 zuP{KXw!%jWZ6B)m6-FwISD30WLt!6KX zw!%jWZJSm63L_Q9D@;|Gp|Fp_F$!lYT%>TV!fb^n6kbqxTj3*xwk;}tg^>#56{af8 zP}oP|7=<$xE>gHwVYb2(3NI+Ut?-dT+g267!bpYj3R4wkDD0zfjKY};7b#q;Fk9gX zg%=dwR`^JvZJUZ;VWh%%g{cZN6!uX#M&V3_ixjR^n62=H!V3y-D}1ETwq3=qFj8T> z!c>JB3i~J=qj09eMGDs{%vQK+j2yqsaa6|UzFtxD4>fJj2JdxqH~~!`^&{T>OrYy!kGBfi_iJko1dt7vwhY7#G9vl1>iST>8a_Xc38E`fL&6*U$(@_3Z;a19#lB9 zeVFsbCCcB_Yt*n|ZD)lSd*I`(qn$OQ}lV4g9t%*_@)E$tB;-CELzOL#BQiB~McJS1CD4$)}b4 zo09jQm5$O%zM|xGCC8kTj^;|9sN~8@Ua#c(O1`7yNG1D!Asv}Y9-!m}N`9{7!b*00 zDII3{lu@!7{}3gYQTkcA_$x~0W986k`L(ybZn@+UN;b>y4JD%*`EO}1K3mD<6@OL9 zmpoI+W_j&Yve|x4DA}x^-;~V9g<<;1RcU{?1Z~jw($PW5CzM=F z$ybzY>bJZmi7kqMu4J>lM_iYLS^f=_Z1Vk-Tw8^ISIGyI-0g;RnB_f0$xg+;q2zW- zzOCdzN^bLmbnH=bft%7{+AEPuF0bS?r9W252}<6oWHbF2a)rN{D}3B78IX^v!rAdh zDUVS0PAl20zb8sI(`Vd~ianM7ASIjiJ4wmmieI5*v%YpJnU9G==gD2E$VWoqZ2F6o z`3NYSMSqvF**^_aauMmRO;vJPC9hSo**`u}vf2KJ-1D|~O37yWuPE8f?_DL=Q}!$T zAsuFaH%iH7{a#bDncqiBZmGg2-cT;i`mEMg?Hp}<4lFj-mT2zKN+jBQ1oBC6g%tyi~ zzF?_uZUL53ve|wIDcKx9X6BMtDS5t1Uu+3!Z=8}pQ1S~({zb_VN_K=u{r*ZGujKAZ zzM|xLN*)+0_09Skr{pxnpHgx~B|lKIng8PSJ~aN!@DWNLt@M}V;@2s8gyKt-l;O?# z8Kz{j{H80pmeN0{WV5~fqU3PJ7c3?1nf-sVlFjxrAeWq_WV1h6t7JYl1e=!9(q0Q? zzm<~B{B~CI1jS#>#ot!4SsqHw1wKdcstrrV98tIw1ZNSUEqVo;)h-?BcunB5&RS*d zBXs3G=#93(XKezX#e)o6ydCdj7fIO#ch7zpK#DZFg!=OPAqmdLU&5JU!%DMA(`XEC zouKqGe$()?T%~GFsk{Yxt;&^@ivN30z?O40!<#qpD59rFxtqku1(=2a-u|5CO*hY_v!uPE9g_5xq;$1CRWwL+U2 z%R5YSK)(X53kxj>6uk}4w9qRd^jz@^2Ad)U3jA0aUjg#wH2nbVg~h&rWcWAl3$hQn z{hNP`Afg*7`;iUQ7~EriSO~5asR=FYeiQ}Tvkp=+SXa|bqe=cd4|*V7(UNrI5VE%>|%y<*?{`R4RMLlE*>!Y ziK21jA1o+6^^1oJDxCVoO9?8L`o&8NDvM=k3@S4 zDkgqJh3j9Vdn=}GhevTi?OneOkG`i$!y`=eOheQ5LC847Eo$SUHSaxK3v!CsW_TYK z>M_I;q1ZvAw4eylySrptdb8y#Qan*&++j_lxJD42M1lE2Y17INeF&Y}B567fLBnIN zsiT$uU^19wVfDv60)e)g3};HlU%vlJ=(G`wHsDiA(4l`SF38~-!#A6t(@`uy_KA9& zmkt*|4>4zf)S>^*D(z631QnWAp0;s<28ixRDS5;b(;No1^3S$`878#9@WCT6X~b~p zmA91vGfG5Z!a&70)C?m7zSMhYA1!FC`0G^}u-`ZzbUG8o3olApYH5BIdtrR}k-ET$ zb05KW&~z~rouRDQnS%N_eKSnUihWhkt*sw|hpgDw#b-V7sS1j_4V6uMN;FsOeDP$T zOqm@sawZf@75kQ0h4ntAySz6mD|U&P1aGR?W<+-((|qx$*!AL5lp1;THg#0R?ht2x z#d%)@nr$+&VmWoPVvmUAA?W|1^C9Vw!!ri1*i#}NlPIFT$V-PS_DfOln6&fIrNb3V zg{CU@Tk#V{H1Y^Q(TV{us4DiV81;dsQE^rzMy8J|_J&Br1eFS?u^C1Nlofkh6uBh> z%8I=!ngvQ(YH8Nsn7$49RsEQc@Wjea+DL8bgzx8Ja#~X#O%oi&+dU7cjJ1!O*%4 zn{HDO(C$sN*%EQK0yv<*5Kf~+tH1p)Ru90`vPA3YXo)!NOhk#c{lCLYfOZ_v#?R6m zVFlB%3Fvl8y!ad1+AJ_{n5-6id;^G##pg9`H9Xh3h?Me+L9{Ug)sbNGRq=!%5MK6xT8f3 zs5&mX3xy7(c|nOWn`@(?QP$dm?&4D62&BsRWgV2t7|^e{RM<+$pG#rol_srV;Vn?B zOj=G(LY83VR$_X}_n>!zI%Lv<;?%Ol2SMPo-bR5^s}vW(UnLzbnXzX#gI#jz2UG(k z@xF`E^m-@@Yl$X>D5usEwMd*VMx29w%Gyj#qY@D0srjvsL}q_Zg@OK|SZ?dlpu)D$ zeI+tF!QF@59d<5+Pda4FFiM)HC%~O*N8Uwe9amEIdt(J`-Pwqw#&tmB5CsPnD5z-- zpO-}d5pwuCUTT=OI|uI7&|XKz){-&F%!mvJvk$%}Oh#;D`ogCO_!|CeoF={nb<3n> zA-*Z@w*)_%_6h}kt;7Fkq&}snvMxR!0YcNqgRzFx_y`uQA@MhB64ZQ75IXst1cE7_ zGt7*xMbhl%40HQ1ygq^9jVy+F?=#Fl%dqe+!y*lB#TvRKlwoN+!?Fy9cg8TRdYj?B zjSTN+Gps$yujv7Q>Tu4F8;9 zcuF6+vWE3oLes#op5LKKTf=&%)+OlEg`w|AhJLdd`mbadu#;inDTYBmGYl?JkHQTv z$1tKP!>IlYqh~XW*~l>VFvGYD43lm!OnbmE-Cm!p%qYw7Y7K^2%^6-B&hYv~hI#V= zSj6}*zrfcwyITgM1jkQIg}aA^ZmCM75#HGnhOa(}ng~-DKg$usti2`L z^a0n!vVJs-v@-o=qeC8?ZO&6Voo!Dtr1J#anR%0?-p?8O1t*b8|0;lp`)L27NTzKa zOchJO(z-}z8>K-@|7tQmH2`1#V;I3XJq<&lcT8tbE`3YY3XU` zy#6t{T;XEcV5$Lfwo`YQlMU^B&J$gXj4R1U=Y$8jamZx@-|PKilfxe`o+rMM0trVm-%McGTe*aJ+% z^SDkYi?3!vOrDA>i&gODufi!6a3wM6Dk(Rm&sDW8Q`SNi+qUc#oiMCkBa~!B^b@G{ zA$LS{yT|bP6S8GgeycQWO*6!7`8Z6e-B1?!{2QleqF+qzA5m%4m^=B4q zX_2Ag$O+_`#cDBHWSA%vj{i9QWh>Y;Qwc7Js0}nI~l=u8hd4j}N0t8L2BP>ZfShds0U7BE)6X{fAOU`pSuyTi}a| zEXN{!kzy;te=KFBue^vv_|K$_^i>e0U&K^f%1B>D(W@Prwv>^+O5%PieCbxoNMB{q zvkmq&q>S`MiR;jRCS|0rig+C@#}`vD)Cjv;w_0xz>IRS2DR?$E;ItNv?GCFnW7( z%_Pe`$Tb2@P#;9DWe?#RL#`$x;F>}%3$}LkIpk`Fr%n1oaxHPdwSrvDi$QNax#nRD zP~T3j%A~iSTzyFI1i6M{eyX1%*H4%w>)(>=Eo`dkH_0`i99+MVYpZ}gdf~p*5g)iL zD1=gn47duC>l*GMy*OOf@-H4JPmpmCca61tJ1Mo7(19SL=W*O0j+X^}MmG6%T(ltC z(sH~aDj`eMe+_}>B>XJXhe7pSSmKMuJ3f;e(%sk)bXd?dvNUEF-2P=yfJFIMFdZKv zI}q{Me%i`xbSAX3*r>}E7wzmJ+ZG|f)z8CZXcf zZw)S@3^W6iVY14=JWBojdzF>S5YXdDh4LnzAzCf4ItER@r>)DWIOc<m~5?ZoQ|Sey>8OlxT=&gqHs@q^DR;O4sc^TeC|(#1EB1$fXa_FG&kJu#?_QJf>Usik|A;f7E}66_OrcfRb3Po|gnSR|Ki(RgYp>o|(`oRIsE4VMC^Fut zg5=4rXM$0ZY(~^Wn4^?XZ5VG(_=l$9{~Dk+{D-W>M>|Hq@v>cx=~~>S8HmvT zU~5#lD#BRuxd=;F@w059WT_$?fT+cN_&zfkuOi&_JpvYnp)mY7px8yBRS{Z$!WR%A zR|i|qTZ4M9C_Wnd8B5>yN9m7>xhHvZY)(`T#?p z*a*d~_*w3H=HrQ+%!C@(SqcM$#*%KKKAhWIQo0rn_y&aI%&;K{8D}m|iFlEn?+i z$@KDIWIRrE#gytv9t@|g|9F&j73UBkU$m+(VSQJq$DT)6 zeUC#gVXC<`8qerXM!pOZ+Rr9Sk&E@T*rJP8wQM7(^td|;yAR{xDy`yjYc$TnX_i9! zO1y{1ekMzK6LnF=e9@}@8caq;6h=zWe%hF7ZjHuG(1#YQU9_qR zc(PWg#~X8zlF6F(wyEaUC_fzq<6qFSz5zM|G^3MJrJ5!iAx0CbC*f{?RXb1#HR+64 zsXg8`O|n|IDHQ4T<9O0@`O#4{UT739qq5H$!Ro&);8IGz zD%4~V1}ieM-qhe({B+j%D+#GTx)pnZl+3fHMy}MCfA$@xt$Q^TEk;_cEGNmM+-MY* z<<6-U;l4Wh19a~4CADHqH$3^%J@M( z%D5??Vce3>Fn*K|Htx`)jOC(N|1nzV*S^@Tr;xuRYLN|7e9^)t1yPE6Rl@|KYzxFv zv9CKB12`Ek#KLzV`x;0=#X+tVeMTt?TXK&rjQl^baEvXS#u-DTVb}kOg#&D1VgpJ; z&A-TkELS>&rOK6^`IjplD$v4G`?G~woQxQJw%m@ZEg$f%-;e{i?w$|RqS^9>eLO?s&)f*;@?ezfBda~U^%|qHMC1j zC;KbJ00^~ekdAJFO5ATPp;DlB8wAbc&gTs|b?2`f0Zn!1Q}1Km`Lt_e-ud+t;ZCoh zX&v#id;rG0^DWE341w=BlM&fW%R7JBanP@WT5Qr@cYYe~F$Wm26_SVi9X3xbuENAR>E$DBf;L-M-wTc3eP2>41a9gdt7%{zbe4wN7T zYyh3cq(>=|cRuaoVN)4ptVQX*X%8V#)kFznek$BFm58$V4o43XW%xEDni?bAsc=UL z+X84sS$uMEkU*y+n2+K~~ zHLCW^eSw+9oi~k_`QwJFRCboX<=0HAA*AT270T7UZwCsI4005(^ZNPKVj!k zI0K2+jOYb$2XI$u^tl6f(G~F5wJLOv(l3!xXQjejN>xB!e#79ZQtVA~j({`T_9vV= z9TN2W@&}C0J|!w6M~gnkhpJ;9(t}~6H#P=i9?57vLB}5R4-JimvHld?Ps!~tcCCi{ z8M%XvDI4K_PHv}B2gMzeL+%RUCD9wlXo7^8@LeZx6)Yr#S3t4GSXf93e`x>&orT8X z##<14SV#*$yAXnnh4$g2zlUImsi>xI;h!Z#@DsBj^b3b&Ev6vvMGXrtgP}3T!NREU zidb;R6cInfY1-KESJCWZii=?=qAB6?i$Mqyb0AC&zwQemSiAvi)52E~6DC$bm=#`y z43rbU!9Z5HKo1sEg%7nX4mU`MW?@x$00~uDSRY=TgcugKhLc|=9ov^VCLwUsTcPR zW$q#;ht8hLlni+FB7Cp1ucHBGZn4$5NNGSx}MC_VbolylcpT4DTN=h5}n|6r%>^mwt?@$ z{0tY*sc?K>8q@}+R^_6KqF0Np!i7%V2H*O!I91(Jm*)HygJ5iTYTDOGiQ708j^=Fr z7MKCl-s#M+J5ieRF7D*mX~fVmVo!#Sj>NVjBXqLZ)7mSzUt{-Cz0hG}k1&oCo53`+ z5;fQr$W?)Y{LqectjpdNqXugU)qTlr+{O4)-7b69E9l&+`;l8Wu6_Y`0dgCL^Ag+z z$z9O+jQk6c+hH`>4!1wKiy9p_z|8|euu%hTxVnSj!yNWG=5ev zMz(@~F>0$Znm!VVb5?7N3|yrY)5&Shn&#~DH#~b&NzgfJcS&%4Xihr47dx1&)A?$5 zaVYwQP|%qadO{wd9a&vLw`1K^#B=?!i_1;bFIA&cghBNXc^~$QNORPz4rZL7ne}BC zMJ|*!#q4NG3X=p)-syz5Oe8Bfm!K1my+m4}v0k}j+Odl0O@a!n4)K%{!I4moHAl}B z@Y@89u$}VYo#9lXuTrAv_;~KZa|{>^+R0r_7V&iZYL0@`-Yx9A)n$}pCPSKI0rg@w zHg+yAt6|jh6kz^dUi_ks-~*Va6YDZn)a}bUbF8JVpc&tabd;SljSf1CyhpJ&9c@?M zRd2iu@6~jrB+_YjSxRJ3t2(te*yD61mJY#7mL#p}6Fk0+{eZ3{)7f~>u%F{@jXg(K zV(G}dXV@lZ!2V5FVril84y!UU;}}$?hy$n{I$$q(S+Ng_gWAI@LOOHrrqoDPVKn%| zVj6<-xp<1hqvGwx*E-D_uj6V{SLt(~{pm=t-Pnt(O85zZ)!182Q3S#q2{4}#9JR(gv zHQMjNzr3hPy{BeiXwec9$ZZ(0n6D(%Be%n7+7a$#at9k9o!l=P!Q0{PMD7e@A*OW+ndEL~lqNe}$=%*aCp+EA z-N6uKrzg2P8o^|zH@Q0*Maj+}a%UP@WM?qBJ8RC5N<$b=iKf%Me3SDWQJZ)c=Y#)4AU&q!bR3*G=G>qIPSq41iBUC-ls$zM zg)O9zxvdwR!5pdIORr!kags(0BeBl>-q^x`W#a%;5);W=Yc-LwmBCucT(oZJqN2m)P1$#Y$(cWnEJgn&T|i5s5EZsD`!LF4G}!CbqRwxze$09_{&}IaZ7W z^|qBl$D0Av;1abC)Vn;}%OHy85S&)UfAc5!r5toj9(>vx=w=h^Sn04gFAH<$W38w{ zWjV+~=fLHtG@v<}dg5^w(wdR`6G7*~1$C5~bLu|$jHVL4L7u#>As1?ga_l@n{8lPg zIzH~^rE%wF^k0cPsq2f(gLWKP2D%XEht8X;YPm5RzVCA>(D8FQf#bna&Q+e41HXbp z(e|x-?yfmXAZdy1tkgVdOV?wI*7-I1*2H1dLTQ&*M&&F)4eum*(w^`CB)<>3<;2fv zz^1+6|Dk8^L+A-K1*APVB>7cJLqfr~;he?JGYC;{HaidCa|QK7C}CCTXmGi@co>CV zFa#N>A6f!Z!hX^yMH=+q?X~*hU~1m^b&!j990Uz z^Dpj1=$!)P8;%xH?{D^{R}7@Da~IBh);moG=!FBN`uY;6e#b4B-ak;jUADmYG8?#M z`U-L(F_e2px#v)b;|_pcRFKV)Oslwt{0;sNC7)hkkZpnPBhB#x?x1=zE!3*%rH0(M zEa%u+q6U!YP!cr9(eJ=tV}6Z`Cl```hmwD?0~EGd z=oY58CzRPYs4T8>S(#sW`DEu4MSoma7w02Adl;^yS z@O+az>BS3o^0l;wm`&Bow$O_h(tKJ0@+l+eoeY@|?o=J0V4_ejl4GU!G~5(>$*ytY zp6*auNx64^^)Z|uQdoMCgX;s?TnNv}Tn?Ma*Ihvgr+&v2goo&aMbJu$rl2;1UI6e* zJBDMDkvw<1kA;1Mn5zhEGuGjrbxy*D@(U};5WRCDw@2ue49&TtC>~PQd=>Q?oMbZe z&W7gPMNAyIs}RHOz&V8sEu)|fT%I)z+G(!4KLDj^%^HWP2LAkFk*0>jI5}H!$26SO z4gsvm<@wE_biql7iNC5;Bwtz~7r)nVs4QhvdpJw2#RIsoB^^i(RdlN$dqw3a2bY%mXI?3 z5L6sSAM7)yOd$VYtutBNfuGBeHls0L$+ln6r?4@xGn`?62uzGqCXZ_X z+fGX9D-;rabs4BkCAThyi(x=!l3kQ<&@|bx8I!@W62<7DN4i|yH-%Bgu{PYDlOI<;s`K=6s{7q|;d!KVh_WkGRzDL0~zK1VG_UEs7UZnY* zg>TA@4EV>LMUfa*E1lt)JW8|ra8zQ0*lwV~`4+-JN7)hEpey^2jUxRrr~+-!1&Nd7 z&G`+yfiBV-5SW7VkSKR?4b0E@S(d}HeAz&%6nlAsQn7;Z$4{#85t+b8WF@pU62(>` zL05CBybG5mRZ3UGD9gk&PL*r#O>_0h097z^P!6hF)I;V$A>U09t8 z&(}>ttaG!+3*t?)RbW1Fu^$ra?}MI#&Fd6xL`*M=)ggMN;-sx?-FBzA32KVS8jJuU zzE5AA{M3qV9*NWx7vZ3a)uBQO74+Ihv0hW*4ii+$#pv{hy(G7>VswDhUP{!#g0NU3 znxMUkpl4=2&3ccMucVkM@qaK6C-q^f$$Uh}or{M4p~?HWJy=2=p?nti<^p{SHdKMq z)`WL~(#{78ln5>2s1qjy?A67LM((WD7at*f{2G{cXvuq#7p+)zs-1X22Z@UPkc5OL z2zmyqe+tcF;mzPtOVAM|{af;&!z=bgF&%|jj1I5Z>xwyWHy?;>*&B);XtmT9N+MDw z6q78PU%{?5n3}|7QC;+<7f9?W;_9DRLrj1h=^I?>vCcLh!Cz7r z`T`O@FkfFAylNXfg#Doq2N5=zMiMQ&3Ef$b1^w@E2OgqQ9d8#@y<%{}AJp{i{|NCr z=~IN-kWts6ks|_7c4(S|_kF{8w^&5!CeZneb(($(9jk@XJVgvSIt`L;A$}H?FMMO{ z76wHT{t>!8`?nUduf_1~vqd~bQxtj~sK;;{+$N~`3B3jRaGTpDekPw7Zgkn=FBbSF z+arpS5WuDGXL*X7kh_?b2>U*MmT*dU0GE9s%bwq4b&G?bj=AVcSyVOtmc{pB>Jq$f z62)f@=_b?yxPl5>a{8;_&%j%on$|0Quo}UOSdODML@>O|66Fz`D?8A#{}mNH5qtwz z@Bpsy;+9|LW4eM86rDhIchdtg39*M*%ELNmZ9J$cF1peI+^_af%ZhY_d<)*oh$1^$ z09Q+C%d?-A;hmuNx3B=mVLPMwo+_YQ0k89_ZAwROp8oqruhl z;yFgG(kjek>~noqQX_5)g`VC@4UiqJ(repcX{FD{-JvuV3e&ulv`SmhrQ5gpRKs4R zSPX8xi+4A|O8OcoUGqu8984U5!smG@#p3?47qT5&2BjNNxSyBOlj%^3w-s9pCEpe} z|Bau_I5)dW+_h)e+MvT1<-x^z^TX~^5BZ_xJ*R4x?ZAu1RB`U)4vP^`+G`p5rBZr{ zlv7^I6Kdw-X=cX#aAjb9{fqmL+V>Lf!mIsY?`5;ehliYWeU2p6Y%}Q zWMutI`b$fV(%1_E_0WqJTqB`jWc_Q)u1Wxm0Y&lisQS<~r{Vvd#Wx5Oeo)n2G>t%7 zD81BXziMf<3a|8lYU51@j1ESd7;c$?B{+3IVifo{@N*48vL*6u#R`1kDNew9Kf`pY zZHSRLpEIUgO~L9HrhZu3WQyMnjWythCD8d@b$G3Pfs68t5&Sr4G8V5G*#6X z!>3prMlO4ME9Fw(Kpr$a+B;flm@7s@pS_cnhO=Tc#Mv{gG)9%OV_#5?Q}e8HocaMl zV|d<_Wj#Q*tsJ!8w8}xtf;%XN=Zg!hH+f(yNGF`uRw|va_5Kt-$Dwe}rSuQ;d#Q1LVExMh{ua1v}_MyFp^wR9v)_wO>=tiJg zxI#;Mk97lPXB@f@s6npK0e@25`>nJm(~*qN0QH(Hw4@JNH}r)~qSt`h=nh>GO~HP| zT5G%teH7HE?$Ashw;G34=o_H!xI^=-@|1N%NtJTZ2FEGzlLcImau>ilXRP007lM;t z4txyhQ09fy;hE=G)+h{STz*Z!r{}W6Iv1@AcdKF;41R1bJJVl5ZmwC|_rb6m0_*d^ zFVAI%b$+mJ#H$Tqq_YeB;aqkm2E)!D*0wt_%#+R~@Hbs{ig3PI=ay~5dX?s9;0v`i z^Um3jikEM~n6LgN1|;^4gi=Kl}@1twpa6|Aw~>^!g!Yw zx6}YT-Qg*|i&rQ^3&5{%>5w5!>P+)}gh@5`hqPV)iIWoS*3UlNp>k z)pSb(WoH=p3AyaBP7Qq`YJ;2GLhvhd*_nnGY_FsLbqx<@=$_aO{%9_pE!5$q=r|}V zb*_N_(Oaj|vEQMy#$NNpbzSMsQ!Mlt}iQth`2Rz)R>Gco`d5AjT zQ(Zc;E+67EtM(51haaHNC7rI|dz(61z%!yV_1W!lAb$ktNuZ_^&Am#%AWT#2UG-bt z)D-z0P#>B!-*@unt90ZiDE8LN-G_}MQ25-X6hTR?bYeO9?Y{P|;BSDtWAb=)hUU9N z_1T#Ga6yU;41@ToB+zRO_EGv#>{>GI1QqF~-~JQyXgv-zPvYx?N^#RQih&-h-#{UA z=q{joxoPU7?X&fTKPviVP}AJ>m}8*l>d!GSbLi!u-gVP|%mzJAuU|$bZ6BzQ-E`Pe z&~NE6MOEl;L0xmvTSW)5{+>?j3VJ#y0)9(^$7X$fgz_lR4sS!@r%Iv&)zwbDVJAgb z0u|$;gN`5``vLuL+(ea7=8eI$B$mc0O|l2|18*V>*gjwexx?1#kFbaIo6Rs80XrSc z9Cuh=?i|rm!=>eS!K}~49@lG3mX;5LIYBJ9pdelgoz%z7la{Z7x#_7K2t>t zTK)*^P5stKl06URs>vz?(KKfMq(3_-RUUyB=(c2!M?I<*`WO>J`|tV|JEl&coGzM^ z5V~nIEIicr)WK#gsM;p2g|^0ik^PCjt{++ed^?zoj5@R&HTsWzt0U^cpvJoBmXx*7 zpJ#)PHafq9*Wf`dchMBBO8pYr-l}8t#9d5x-A?Ena_LDke}4)yLyfv~@EH_PmrPm< zDn!XE>{B0m@zj(50p_X6%0WY_6!pnGAUoiq9WmL(kLyQJm#hUH!lKOnwsEAF?8j?@ zZbT|PE#nb_N5#;6)W3$>KSwvx349-yjx5DJKY^}n&k92e05y+jY6XJ&EkU=6T)hMx zpnfRPPOq?2^{j*9ewQ-qrIRvUw4gp)pq%cr2JcW^ZG|sDK=FGoWo?^iPumZ}eL~`} z%t4NV4y{3$eZ~nql%h@H!nn;#9rhcR<6Y1wHqwGHNv?q8 zMqmSeLTAV==-$E}C72^fOBMU7c^idRu z7ZHywrx2G;D+#&!`I|LDCs9A~k7ZIV9D@Y6A3w`*>TM;HW7!Si9DJ{tjK@M;>R7F@ zm>4|(pX1V*$R>VAKW?{MZ=7yz3DC@J&@ZK{l*9VhAELTJjeJ{lxV{MNw zZV}rVOiwqDZI#|KWSpWV~Bx1AmkqsK+MlYNhYNr(id1g5jsqt6FJPRZOVh8|7lm zR@$(!Y=X&PS`nLsM2b{Oso6@u@|6?CzMw~VtGHU}qG;SY2=p5G`KFGmm1b8!RU>g5 zz-)1|s+E4a9npgQ1kC4dR<+VkzQGL%%eTP%Oe|M~*-Cq0f=8?s?Zl3sVpS_GaRCz@ zSS}4F%4EG;sh<;vt)P+&x|O#Iw^Dkn!mX5=5w}umTbfSYTU?)l*k7`RRqTURV>h^M zv#7hpck-z**-8$BJ$*JVC3{VUT$;9PguaA4XjjHomIg7QZzK=eQ?W(RW{S}Fkq7P4 z*edcIx+L|nw#qz0m!yVki{dp_N!pdMRpIT2k{!t%&3g?Y2e6ZCiyeCx#=?3a>-bqZ z(kR%QE(3AVq)yGrHQ+7 z`x1p6P8%z>dWBI!9Cjs`_g!HnTYno?037xJn4_++fwZk+OZxR4Z1#}l%V4g%!bRN@G+oYF%2jc+AOg(|89!1vIO*+Tvk}6)p;}-wz41eC%LRl ze+5?BRJep&Fa){$9`xN@R#@dlIyJ(ZDVAQ?1i_CpBhwN>t7KcpR(Sp_N`op-G&KT^ zKf`S6Jh?n<*MV=U$%tu8)56r&ZTc^uJA&#>G!+uh@3atFm)d$3?f{Q*peCEN7D&rd zTfcSq-ri}dowvcf<7R19YU@Ao5QaKpcY)dGW@%As8*ple?2W{EFqhmctx0VI2V%pK z!~Oy0FE>kTPTQcr(Z?|x&>P)3eyV0^!D<`)q@7}`fT`|gX+dfma($3un}KQVW@$ZY z8(MO+V*7&`>SDL^a@00#_#(Bs4E!w#>LZ7iQZhX%U*S>@q{XOh)CqX-+BF-F{ce_K z4Yo12zfcA66`1c$R>*sjW(~Hfg>|L!7tv@(vT|6Z>wet%BVR@F76lPbBvrZEtsaYA zeD0uh>Y3s`K)-3=J>`CMx(gnfkp0%eXoS;Wz(w|3i)Fvnnmm4>94?`aWuf;V5ALRx z%5I7~wPmtXn@l8kYRhG(mPH=ik-ftmnZBAlxHntDy;+DKR!FvY(_e?}FTvfw&oZ0Z z-9ox7#YK}k>&M5#;}Lk#*VI`>bl?Y*k-Y4?tMIllu_Zu9xK!wEAf>Wr@)$gZA(ceX zjYwq^MIu#V7Qo8>J2^P$Nh)1I56Go*5=$xD$4_2UGxDjRUndn#?{Ca(;tn4Jj}*%wHdxTU{2^Ve+7Y@{N-P^#wda&b$GS zgqyg_#Uscz7_{*y6uo%YHj*AhIVgs@s3(M_{q_=v2;+8A!$|M6#A5syr46ki?z-A7TCr&toPb zPs3D=z--X|Qx7DNwn=^jE9l{%JT>!fG7Rwln6m#;AL9(EJco)uBDYk7bq_UnUg(4q zrK!n~JF4qMfm1y+Zt(<>O>Z8HiiZWCbUxza@vW-H<^4(F>1K2VQi0A_d3=Ye@k8X^ zKK&X-*@aS}7n0=hjj6^ba8uZ)KS6Qsk_!BY*!^{=onl%$nDFIOI{%yI+?m(f_}D9< z5O#^vLviDLQ5u^FK84DnYcTd;L)XWjibOMB!Wjjh06I-*7}=--AIEv9JG7+E0U!eD z%w$q*Bls7)1OKE~u(9G(oSo-y!nXt+T}*0%Zr&$^ollFy8OqLnU&9&3&f%DI`jlj6 zeT?otrPw(q7|w8ZUfBz$lbqQipf=oXE#qIp#@kwq=RS2TM0|>*=ROtd_u)S9h3})l zYVZFT&dRL%AP?2rMmZ!sl=-4q-sd(|`xqYI`uH**mlyx=C)j@|C@JZA@qZD2jrmb| z@o($~e}MUgdGYj^-DfrPJM-de?g2l8`LFWg|5^yXAM+3L;*ahC-<)-Ma8!uFS;kSmcvAE&x>A#EcvV@`is2i z2^x$~C;DDq^g@ceC(!|D#ks4#+x1UjyhA0TzQ=gaM+@Z%+iuoyQ0Yltl-4bk>Y`(c#gx2_*k8dk|Oo;;)3bMZLoS+^F8t4uLj1~J7}EN(T$ zjx6pr#ojC)HpO8qo;1Y?ES@vPnJiv3#rZ5=Q=-kFFQ@C%hfy+@;;PfB$*}92@Ib;S zM#nXIfH#7uBh?H#j$s6o+b~9=6EaFrnL7-scq4>@1{+k%Mku+R1}!m+Fgk%=A@)10 z#EgA%gU-R)_^SWL7RjRuovaK% zcf&7S`D<0kD(y?L9Do3{nW2-9&5$wDVLH3f7+0G?q3uRTcGEc&n?WZ~YzCb@v2#$H z!3T0_o6gQoo59<@v~OrPreu;fZ87V#(`h&6!^3Vg<11|h;xz!I(#}_#L0?wV>Elv% zqfJF3<&_Q~>qpp|zKCx#=md>UhjQ%39xf$5MMIA)>_+J{x{l)O8tB;!Iw_>n!w9=^ z4<2?SfJr)ZV>4)CWjA62$W5ni@I@uQ(qj#qL2DSBL5ElD#!Pn8I>p9zYzFV;&|1oF zWOt|QCwyIqtIePdAiMEC`_O)m&EO>zEe7nyYfLWZYjeJK#uYbqKVpZl&xh>8Q)`-_ z+Ku;^+{xGee4WhK@wnOyo-xw2&u*OVPuK7Gx)0a5_;Q%n7%k4C!!u}XFj`TQ(F__# zj5Kl^@-AvkFZ4JJT9zAa$Q^7@uV}Qri=a+}I!`0r2cvZbzikWPevxABIQ$r#?c0yS z?JPRJ^#DQ#nsFqE$@F@K(UIP?ND{ozGdl5VCFCUCvt5c8z^%Cv;kM#uq5fD)p+$?) z_r-g1s`&|sFHMp=Yc2Nid(fB?g2#Hd!2E2oT5NmVJjSf4t1yf+Ttc(*h4| zN1`4VoS{_GL3cD&w7?1_z&?G)DdY@!1emcdHlXrzusEgQrvlFhwcMm-O$PYgf{rel z&QdzNz#sP3$<>t(nzlD z($~;xs@{vAQPF;~-H5~>Z!_p|gWaeJx7~P#p3H90-muM}b8k9*ao%pwxA<**)#(H9 zcIK)ae*usGA#zt+MEn2P`x5xLt7`w>%r8wRGy&R_VgrEQM}J3*px?0D7uzLDCCw;_$!rL(J^Mp- z*mT$NXZ6~1AT$x$nDyuoUni+IE7X5a=(2~Q70~GoZut-OAy zZ>As2ruy-E_TxABlYjSgsQ(gEDa-19sA}49!xI21d^vLTOD^1r!U($vrLBS&zv_a$ zpc#O!1&*_m0k`=pKJVgZiK#uC)jtT5^#i=;UxXd%XX@_?R$akXF|dlg^4?hSUU^?@ zmD3=6;#;6vfP}}_8?m$b3iUJf3OwW4EpiRvzO@swnFLM6$@ql(RxbYGhX6&$Q)XK$ zRQWlGfhQI0mcKan!_N~_@GF);+USE-H60j`F9Q?&`8@qIw(dLzw_~k>2YzRjuopAw z$Z$74SyX#@kmF{08bO{!=gvADf90Z5A}!t1rx3`6qDmnDQFm(+~j`VZcU{c zBmbK6Q2%mORG@`L^|DG)^?AEBy&AkwcpY-4@W_HbfetM|^*uK2p%=hfw#OEu=-I4r z<24u&-t%us5A`$kKk-zsHjJoZtGsWoc(c53v!=_PhWFvyxAC|a7)qnJp(%Bn(QMXn z2?q2JyyxG|9_nZ6iT~;=Ucn04+(CKAkmMShYUEjiN8-NI{ZOEmTT|M59`FE2?c)75 zJma&LveFHJR9>T$e?@$#pQ+attgeDBvHivJj@6(yLH?!lfOY&lp@kj=^dSIPO%ku0 zyTxih+}Aj<14b?SOPe*5UNV0{k^I~2L;Xx02VnJqIiQE$2~#05@pX~><(4=f`Pk?K zVLB$bjXvOS^g*}LsTI?~u|S8!7bk@;;6GF%c!#7y;~fS|M|Jo{{HMMdyu)nazEwZw9=Ji8;GyPC| zo_P2)_M;n>P8raTOH8FKtNWp<0siBSN8!tnV;`P@R~fmGWG!Vi5ACsGq67!Xq&JYXQx+Ne|oryj&HbT!tY{ znsT%6>o%*L9%skB4c2G6wzyjxQ_sTV-w}f8mmsmsp0+%svI?GNn&9cjtr8Y0r-f}3 zKAfoQO}X_lmEp^!-tp2c>`eN8U5C{FhRqsZr@Q%?dYj3w2vFMukct}3a5P&}>3@2w zE_jYx@E0gF0Vp~q{->C`3BbSR%XS#~LxMm43P?1%BzOmTh5DJA!Q*=7NM99A**0N^ zRh9|UONYyvx)MTQlh^c^x94IVpvrtpRDryKV`>`6N8UIZs_(}J0?mVOW!cK-~=n|R-@hdM*I z73ybd$(4nws9^1aYG_k@Ch|^v3iUI!5syr)LN%K7K3$?vP29umjV(Leq7iP8wy$kpDPclgA)1)iE{c+2M3N zRl-!&`R}pw^ys__Nh=}S@paIJM5gB6;p+@xMnU3J<4A+;zEAI&)I#L`)Hnr+w<$=x z5~YBiYgLVNl5i5@3#_2LeuIh$Y0pY2U@dFTzw0bZbe7{$1GD@_XKA$NG~)o0FNogq zis~$X(pk1zbAG9_#C4XfI?La57I6HlAzShVE?r)1B$UVTv-|6)2ZExxgfmc1x7ge9 zp?QKT{S$YRn0nx{3azA%@~U0qCSJo=E>{H z1B%vMEk%VzWKqq_Nm2EAyET)})E6S-ACFVLJHu_6shxQIXqY=o#RCubft0)eYH%I0 zJgp0R%n(y=D^U0cW?Nd(Ur&eqlP);RZS}V(Bpf!khj6wh{DgPpVE^G;lV!ebx60{7 zeBwKyhtrj;aw}o#S$Oih{%x;y#>KdFK)1J`5b4xv35>x#Kb6Px%XP+iZVUg0j59(! z?0-)23T?OstL!<=gHA>W4*eM@=MR^_#RvH_us-7J>uxPf{XHJnUSB$_*sGNj#Qym} zgjVf>M|waY$_}U#2jG!XtDJt;ldgeJr>@zX0j93N1IL8GgL+ER5Ywx% z_;D0ERp*=Nc9f|drn(!k;W#zrGzRZO>fcM%z+Mwp1w#Ey{l3ZHi+*zeLUNx*YMH%# zt}0O#9j`uMCgnBdw(Ifxr%HKT0tQUeQx+2Ray8Hk>n*2;N%_0L#Ncu+vH*e6H)>f?r2ILDm8hK3-Y=5DsB8mO6SWoIKmaay%vt3uwShZD8Cji4&MYFF8 ztDK%QIOXS_#qR@%)beVW%kNtszB8fy5zN{6$!PlOZTEp&pZnW zC-az9!dlB26X8mHcu4pDQay-JKU2Sp#|Lt2i7#%@IBvD{;^)YL^koVzh z;-yyEO&(GdZi+>u4^LVpY@nRsB+A|cQmgx5kZ!1-smt;B`ml|CsMk=wm7BX>a|3H9 zpfU4Hqg75b{_QCIDqSr=P+p;are0~PB{)NfxKJ_F>nG;g!6YoP39)wHHtb4<3%Lck z|MU-4-eYc<`gv22l1w04OWdRfk^DnE!V2C5Rv`zz0CP3ZN7owR4Wlce9ePW9w z!EXVRXv-v@BItZUwGfC+pcokX6gfYDp(); zx>w1oN>v@aRf%`nQ))|WG2G&btIF)zH-SKyo z7n35dQ2#+yB=0&oqh=tvAo?+ShF@qF(VET+qr*>0-T9-}`Z{QtW3qxu%Nz@_ur-?v z{>Nd+$9*6;x;x94whl^ON zP@+wsMC?*Iy-VMZ50B|yW8Crz^)vNjczl=`qr4cH`w^tx_kzk-Io{-F>fP7-^4ooX z**!@p*Zt!a<@&>JL84zrE~xG2tFcWHhqedCe+Q{T+(guUaQy#4iACb4h}~DmQ^d3T zuH7@`!5dNh6v6a%-v{^X4OOEHtthQ1(bsj(Pw(m8jvo{S`_6bJue@sy_IC(CVttSW zg!=tT7x>bi?)%&V|LrSK)&3&V@iKdI%E!SIm4zcTF!0Dchb5Eyzqsg&Gf^geET6Q& zvi=uet;14@b#GX`hx(byNXjn0=XeMV7iq!&TE%s|5qU6SRcgYhU*4`In5}c(q-!<1 zWZV0c7;%Y~>hIcP&G}GXo`2DK)D5GAIcv^@=T)!fs5k4QI#1lw4$N7o^O&|C)Onh9 zZ@^?^^0GXmv$X0gtF1W?>nt(dQW-`^Uanndf#7qThOV=9mbp4hm(H>t@m~+s#1?>zX?C(G3^fqn0EaQ@&?0kJ&f!R=t@q~9Srp| z^?P_^EnVU$7heAn$&=r&5x9HqMzaf{M?X_%Z3E8T&RvPhXS2@wtHBhZf}C!3quVM| zTMAZP!B)XMRs2=n8!N^m8@0gYjI4DvzO2@@ar^KU>SyZ5@c73-{$sW)cub`c><_=8 z3d~b)DEJ}@4YYk##5f`*ut1Rn?1-!9hKympfNq|73Oe`d4`8|Q2%+Oiy5RYO^Tew< z&H3cHE76bH?7|J-#K|5i_|yNWo_X`c)b|vux`M58o=%hZ#)|p!zSf$_m~WrKm#1`X zC#gy?T&6NerGK6<92zf8{Rv1zPM1i#B~JSwC#Yb|OENQSZ6$iGoRH%e*3Dh1u;*Hd z?o%Q%Q?v%iWR|WT1V>(>{$5ni)1f=UjD3aCs3WO6%<0f12^)8ZUdHvrAa}ZlMdiGr z^TVj&*MCs;t#IpQ>OFX5pw}*B)s$m9aDyjr+yujQA#dwMgVxLy+b~tg`%_(uM{k+> zTT_ctO+ov<@%4~@KZIivoqwX+DpOyBM_M)7`f_^vuS9Bx&X38KSE!$ovm&=Or=|%3`=*@D(B^=Cx001sm_0zo1dvm@Z?Q?7p_H}q9zOU?=m;pCl=13V3)j99d zP5np@r3R&^WB}e}=bJq07=Ox(KStsXUD3Uu@Pho=nk>yY+bUrX%NbkX8hm*1*Q&}! zy^^7Rrv3!aKurcmqf{Ax)vAmS>e2mGt1_LWU*hq0L2Wg0P1 zFq44`uSe=`ogWJ#uTVczZ_mq5O0dkO1T)_b^!OcQN$bMi{$=WawY09$n-`C2?e zeRjiOp~~$xQ@?|!g7pD0R_v1Z?G^jv9jY@r zJRQcDOLSQPue?J2ObvZR_wG}@iqo~q8S&$EB-b5M8P9N`gsBNrKp~HnYL|UL*w7M? zi&7qyeIUXhAk@{!p39+3nf=r(Khk2F?2NZyXRy+#-v(dw^wX+MK&!mK(%`4yAv5=? zk^b@yna^FxN#G&#jR2!4FZ=$XuF+eRzpEP6mEKx_Vk;s*F@nPM8(?6ni+NgPrdHvR zwY*9w9B|S;to;Q@-Kq00QvC*fmQ<5p6A*GAP6@S_))}0}3wu zsC1=Ef@JH0tc(SxG6KrxU#h`>*`0$ODB_|s1u5?ojAi#VLd(NQC?7zUXMUkdc&rLj zAI9Sr_U$08)%`-AR`&~jt?rZC5bFx%-=k}Oy`^5Eex^>mQ7@yR)54iNC%5W``?m7L z>euc8UOA&))uBX(u4|fG7gNtMbr~LECgVFbBbEO9e0h+^)XPkM)iywg;d9=H)SG^; z^53K0076Xtb};`o#T1-UrVksd4(d{CbSa7;uaWx`<#4^6QJ?;ZI@)x;C2s9Z{RbYI zTDLb04>IE>D0_5%Zv!xOKAzwPV9<}vNIhNWf2~_TQyr7v5b^0tUj>M1NAeYT1o^!g zK%Aoi%5hiz&&en~n}y0|LH~vKzdxnw^SZ&*{dg)^2{B#|-tE5afF}mRdlKI^>Oyy_ zvY1JxmVeAQRtoisq`qssIvs#UTl56)7Q!xi=5hvynU7*W{h6xiGPgpeE;ALX!GQx} zoMi{8kLdg!?PF@%6u~pUm$MQ-2!F->5d+ z#BpSR?7p>_AjTAm+zczp|5D}k=^rMDsr7gUZa9zm_;DWd@netqu%=5Co)~IK;E{FZ ztSkVN{|{mE7hqHULkPFElnuQI`c=OI?`R4#hvyl1i zx`isYcT7D8PX+5^?|v=s(E7I$-S8%4X~H8j@fpm-s}|r$f`--BQWjr*J>*}! zU#=UN;5NY2>+n>t3gXu3D_P-3@$FN1+zPFw%r)a3mi1k{cMPb9sXxY3!IG?}oB^Ur zKLPuNpQy%O8&>b3ex`Qgaf3m*^Jn+{VYk{R-zru6N@}(PzN_|BUHDVg&Q*GhQ|PW(_C%?I%a88C7hxZ!e-{;ouST(cJTlN5 zh#PiTrN0v|>7{Q$`tSY<8(_RI)T4N{t~J!p)W`5tunI2Cb@IO5DrfM=KjX_&KUSH6 z(dC7+VWgIPG9O@m*M$luA-M*RbmZTVvO=88UPHf#72-qo8v2k~OIiL)|B8Tbc<tnA@k@xKtGvyr|8a-0}4PR#IvH(bVh5DKLX*{wVhkB&^3`SasT*Y31 ziNMh)cK7#@=XX!2Qj6WvOnu6fRybt475%_zNY!~g(o}hiChqjk0&&k+74sRIO91DC zz}X9t;VNAW#8qCQex_c4M>^Pv#RB&wcI(I8N`{4EM|t`JtReC+771UDdIQp7>f20d z)xnikIRhNrjMT8se}Qq&O~yEo>7ZxOFOa9^aaHWwZfU0e&6GBi zNfA5JYHaMw8Q^N#e_{r7X^)yQbsioWsi9^w83Dc#seky9YUXseHKtx_^82j!=DNa5 zIl~CQ8(H423t#FMX6lDcVKp(0)^rJHb_FXBWKp>n1%6%hZqIsbGEV=6ZSGUh#T) z2Zjv+M#G=Mw}}(eQ-fH-I;r*?;fm7TuG4**o6|8~)KK;>3d<@# z3a#19^*&(MS|m3QsE4V`@Kms*)N@e`AI`_Ot$3s!9z$A7nQH-d;#=^(P1iHQ?GID; z;i+Ir)>F<15}(AECx4(a?Q-)n^6CgAbfGQ^At#!Av^Top?JC^nl(nbq*exXoD@p&rvRg zB(9lBXT6Qcvt5^-wI4DpJVZ`9(>5Zd)1sRmhFMMU)1TlUNla`q6(8R2J^V8w?ORs?}! zT(5;_?ONTB_3k*Cy2|u}XWD}CzW{pCg47P3e}(F=h4gQ)-gNS!gDex`oR zKrnysisWQC^{brhdTGr6@|CMZ^N1MFayg z-i_SPen*v`>DI&4ubO&{Vqzv^N&XtCx9I$5yZM<~@@YM0ou7({SKEv$Nx+^iAqgiT z&rCeBy%vDBFAz#20SK3}f{o|lL%c~_8Fx9^xG=`R<{kNej_ixVg}YQ)Ed=5({CaV@?&Ir8jp;F z^K30;xfkCHCC6tlsk-LrZp}=cjHiMn1yPA>@C+nvz$5h#wpIu_cpq&xSBTg1ztJgp zg*ZLGUtEJ{vzqt8E-HujZ+uI&@H@95rhWiV1*=AfE9S`i%8G?}=dP_dM?S#hshl3V zcjMbfbuDwBu+`c^|(GvRk z8R9tWu7z3mA{3pWTi@-r&eXTzsbK9mgSjvC0zSMRDenmV8K2$}vhmrvUy`eIal-;$ zC0PJ+S4l3;wG}_(QjBGGxDUl{#v{{BZo=)YHJc4iJR@X%9q*;OPLI$q^)WmZtb%pM z8EvWbzrs)B`=9Yhl_Z|NDp}yM%VCxOIqaKns^NK6GWAS66|6$4WN@s~>%;X(JRgr# zNtV=CB@0}?11cZ9e``RMOuZaW1*?!M!!4-t)bKm;{Xg(XmD~uvDp_C`_RK?g|LlM& znfgsU6|90)PJaZ%FZ>gH`yC#skgjoFh5JIx|3^Tubr)tG&y^u&w;l*hYJ^0vBYY0h z4t_(;M}>L==g!nAUjV5`jwjCrXJAj;r8iJ%gb7psGmYSTUsnbG;FkLe%1uai^e}MG z=gwg#*G_s6oFKj`Td>uWo>gDJj97cf)opz4=S|~tf{o7!Ha;gDY<#Y6T(>sQZEY%p3GMxwYQSsf zeQrCa1lu_!*v=_=?VK8H=QUnCliOkJa!|GNpgW%bbK4moY-fD1o$-0?M1t*OBo(i_}RIr_=^4j@%u$^Cc?c~tT*V0e#i>4O>!zalLWEEVA0tD9<^NGWvOeG)iW z3c0Ou56abwG9B+jH&1v(b+bC8-b4KrUozdiQFjxke$vewbvNIqyLqGT=2g0zH|cJ| zZb|mX$E2I&`^3&^+|3eBL3Wq)`{NRs$?f+i^w+i4)Vt8LpFNb{vroD`yE3n5SLXHX z%DkSvJ+Ei)$m`ivc|E&2*t2(fJ$np2`nw%vX>~W^WU}0apPc^sJ1?;EYW>3+x0bes&q*L^4nEiK( zZrJROIu|@dKt`_o;q8uKRVL>epksU-zkgJ*xY4zv>CP z$$mXxdO1rv@}TO-Uv{f*KBT(&);+VXeH(iHFnhyZ*9s~{3#oTF=iOtv{|VK7O15MtKB2mQ^By&=pHSWZ;GS8+O?;Bf z1i?hj@-6H?wu0*Zf3o{PjkDhUO;q$Lc7ZbP)bFAPjAX*so7^6J!R^65)dNW0(t~}f z2Y2Zn>{C6sMK^h+YI3D&^6je0Z6ZpJdxsJwx2p-dO0o27X9fy3-oyPMg~uhSFQ3y910UFS!1rJ2M0*F4lF< zpwuz$VpV73jGmA1n^JABt(6UO2|G?+b}D1=ou=zN&8_phZk=0Ioo%Ylt-7DqR0imK zRwKQoZn=}O@72_7R4r_q(LWchZ&a-}&iW62+oWa!tuvtChjlF*-CEw{*0NUBvUbL! z1h}=TmX)&(@>_$dWt$aEqn16omQ&nXKI7K1T90F z3?xPwIXcems#<`m8C>uLFqC{C9h+}0F$al-6S-z@~Yiom7K1! zHbcCM>_80J8mI?S6DKZrOD=t(I!x@Mk`6x2_JI{SZ>W zEcE0_N=ta1t=>@fttcxp4sz&8n~;oJ;kykvo{+4u!u$2NZ4;8UR``HRo30L3vGUJK z!l!iQb#CR~b}RpEpz_ZJD!0YCw5Pealua1-5I;1zIFRwDQ0eZ`EsZr@Mz1l5PLtii;-wi2CLh8239o^W)L6F$wY3PVs+F`eC*HbG6*3_THgfTwxOb@o(N zce>l^IJSC*nMO{N$}uyzS|3oWb*9^{o(-0srDsexHoGbmt?W2{z)GLxFYC?9oCzUL z9xIxAI>2bf1p9a#$Av--vglRnXV_}E#bq(opsT%=<8(#6rO+L&@s~1lUaN&UVu6vaFHY;TAVt z)+jMmRuA4wZo1^>l(+elxGV_l6~e`x=7&RfC8O?s9J|YL(yOtqD#>+&L1E$JwOYcMM() zXSZ*@&<;nq2X(kW&K>&Jkbn$E^R|#zgW4%-k6rS# zw}-q5po+duQm>MCZU`_TE{Y0Jt#<+spK*L8(C;b7UwHDw(21DLYeJY1=`Ndim+GKw zC!zoWI~?MA$r|eP5AQ)CL)>5tHz1oD-nD^rMeq}P1NH@edq)U6fgQOnkgT8t`uGsG zk8~CXGa(nL12SQQL7gWISw|SGKQN!&RqB}sHc8717Sn5t)H_@gcQJ)UEI}J;x-~$JTd8ypgpY_t* z9z%2v^p}g!jF#>~s@YU5`*}b*d+-G_>9XKZahZI2mw!psrk76v63cyA%8?$h=Qz*9 zccE6G1qey1K99Go1L&iT(Yad>b`H{nFNyVi1&Kl&#u?&pFE&1o3h*~7oYDRAiS_VE z$Hiypx2jt+8V`Cu$)p5+e5l}$nvH$9;E$*l!0|UgNZ6W<9TE!t_4o?>l}H8XU}%1K zHZwofkw|wpC+8>T%$Z+rNm4r1G(VY1$2(f_mdnnIR#z>osxdiLoNUZ+|UiTML+s4v{H0r}X+`MF(P$-JHx z%oymkWpyNbEzB4u#py`3IPtEO)6(6M$X2ysZke1*X4>%uulDW?{dd0|@pcZLQ|II|aV|B! z0$$MnmQ*Wp0=WD=b6BM#Q?06zWyDA9utaa7EuN7I@UH7qxf0o2a|+jmbE0=9l3nbE z)0NZRO>m6qq?ANPz^M6{>f|T1nMye5ZmNS_!oah;vbkitlgwl~r9livdZt_JO|XY7 ziXU2foB4_JkxAv++VNdiqGw5c>&_;#f$2swx$Z92Ei-lTG^@&Dc*$n=-`8jtl9DZ0 z@@A*Ai_pUr>~2qH&~-JnNW&6hfXIi>z!=M#x>M=gyi|uMMtU(rP7c3-usBY8Q#KdR zVXjp|-dD$2)383Ke!_gEI`Od#DnU0<)&hwjSeUr(1ZpM}qiVBw$gWle?dJ#-UP5<% zimFI0lllw*Npxp2$&Lge4CBhB+W86X0bJWL=-$o@Ct3iAYpQ;9&g0%tu%#Ph-`UMw zSuSWgmF1A}8@_bt-_(N6GlRm9Dn*s#-fhnG$S#y!tAGeUVzKnb#uw4TmxYan#=a#K zx;tD7tXUF~BY$nwa+x=45UjLMP{`}_Y=_2cfH@7tHUet+MA{cRW9ne*++ zs>ljtT7J+z?(V+**DWs@XTPZ`vOHWr75^iBfq&OsTz&ZB>d10Dm3wcu!X=^7M@u8? z_8s1Pdt}{;!-uEiw|)D==bSXZI#TtU*2qcWGbyA9W9}jB#D2qi}OP?<7 z8AhJo$hO~vPpb^iI0?n<((qZ6N)LytPKtEz?}@Y?kgBe;vyo+XMI`rlUK&T~?Fu z_<=%chayif=U!6H<55THw@5aj+MH!FN04f4oa_&ULigg(AxiMOwqJHa{72IeU5NkKtveCS!PKUr~xnAR_BjRcymp{n;PFFEYuI zD!kc|Y4}@`!=KVpJ(jnJu*N*n{$eQf zJonxFNNePV$Yj+G<}e1A_Mbzcy}DYa8^cZeuSnnPEvhj9wa+%gJPx^y8L9n+P-uU6 zikTB*jcPA7nOU{5m9_833j7%ziV_*zfSw=TfAIPJckI92o_e=^>9)hbK)3JTzyJ3A z_VJNF+CAI!_rr(xN1hK00J1t`s%zis^_1zx@X*U+x_NT_P78j0OFe2M`EV^?lJ5Hc;4qbg&z zZX=$q-VsbTR`K>b-DK_vV~uauhd@DEy^b2s0Q=y0z~(dI&6Nrc*jwXiFc1ulXM_ED zloI5NTQ2~7;Rr#nWE=mKMh=B1m_Zmn3j5ZQ(Cuj71w`YJVc!Y}-M`M;amLTXe#QzN zJ{VbXFmm?6U;XHXgOOf08_|VvCb7T49XsEv*m$Mb`^Sg=cqlwmkBe|;++BncKLhAq zu7IxszZnl2yDGF-5oykZ@xih093LWpsb2^$#<$0Q2isq_|6t_y$Q!(kV%&!8Qvu7h zW;TqwlKrt0uq`i+d<`9Uv4!z}vVRLmyWLwUzw@;Y{UR88Mi(AKp7yl%#7A}0@{Bc7kb8z%#639{rE&6)n{G^&z;y4iAMe! zJw04M8B_8)Q-g7fw68sm2o$3TmEMiN;mG-=$S2s8%VgXw?fc6FJBAfh+6pR3luApn z*e>uHcTW4-5_Fh?tRQmZ6>48q8agPADI^=7!|7C5c3bI&$4(26H$^r+B?QN^uQ`Fx zwzTrP9w1JZy}0ygm5q@1cav>j2=E{nkNq}2w)UOAO~4ODujeLIfPJHH^4W?BJ&@OmiE&_GGoTd{a>0H-A^lL~4f{b&=S_~>gK=W% za;1^S!&MU_*MY-382KnLQV%HGB|s&56L8;|I1lz8fY}d`Uv?>`2_IqKn7<`W=nDHM zkKNE7r{$ByH!+^0HB0aZ<>phSf2Tl!j7*x0}vJo)q;tdmc#(wDdP~`kW z>mt`jl<*L#LW?K|q**V$+D~Y_A@W9aD;KGb?B5S*Y-i+3^k{x$@||`^BX0@=@(g$_oRANsQ9cleu$Ol1GAEOc0RU>c|MG|kpAQ-?O_w*R*b`*Hs>k43Im z920*up_J@<0@&R|WU{|o=0X_zY2rQEzW~z&2 zq|7Z4j=a85#(`xojX=2&EE=vxoCq&X;rPg{h0@ZI8@w%KA}-qZoj`eDuY!>9=>YmC z0NRHmcZ5%`44*kAq7(^2P{X;>*YArwEw z)#jchu#Dag+d2BHo}DYwLetipGp%&*#1(U^hrEpA{X0Vpz4XlT7)+KWCAPOAHfn# zH$>%Wg`;iZdCgw)<{E6W0Fi*STDS-ns8X!}t2Ke5?cbdUcKlBU_sil!OAy)rJn;Ok z$U(b3a=txfnm1|_VcWj@L~eb+mu!;-l>qg=+i%p+jjw$-qYIkF`Q`d70i%JP5_&Wa zykvM@B?axhH%2~#4dKrmdHsOgSDM@rTu+F4$zqlp=rsYy?N3yAg!eXXK1Fv)Z;n8A z_k!#m@)KPa#YO|UJZoY!vQME5B6t%C-F_=1WsglT@z(9HjuS#>Fq=X}|44bjVPd}9 zcatgl6~*~zId$2$4 zArK9_;B9`WpctP(JIHfDe`Y=q&nR+2Mj>3Pu+0CYsVAsY%2wYG5Th>TBA2#EW;OA= z7M5wge32g%A|OkIfP_lHlWI2M3RMmJ6jnk_IP%30f+qaP+d}5P5Bp~?(b!aPMXePs zCHEMP+?o!138Mx<^oxv`(-7IQ(*T=QeU3mnHb1?7JPnQO%;E`}6$#$<*4LZwLcPnHG+ zHR?0fwEVt3vOm0}5}*{hJv@K1Mygo-KFkdWsPN6es%rTc=@~oW{o&UdjAL#ovOi9a z$wfrw8YKI><1kOAveHA|PBFJE*?rtuq736Cnwy*KcL$QqeNgtj6=2ez313j@u`lMv zEBg;vlk4_^IL)cNBfiil>YLlNNH?J;0`KE)SaU;{{h2)7l)385{#``#?0+oPaslS2 z8?CsjXl@&`KU`widt~{MzZrAAnSDEm#q!8}NaKKn8dvNlr8YOG%oLNfz9!>IKI@y zu!3l;U%&anjm)>D*1`YSmRwyrna%QRY)iZWUulETu#P`3bYijfshR}~V|;C1x8+=? zam#s5Y*k&XamyMfWjWm)ICF2Vv7D9^TogKSQcK%{?vB)D-AQ&DH71bVtF}NtYMd=K zXh<4}H*9rc){YIS4)*b)c)A<*Ah}M5RtaLVV5K@zIR|Ik$xH`M@^KE`oT4Q{JiV+X zp3WxcPD4^QHx0+fOO|jX%hY#0{JGQg@Rzv>`ooH|R?G|(M)YJXMYcQ9h7V3tCZ0%U zXRffE#xy>)b*7t>nPoK#S6JO$t(kbU8f`8bRXr6ujE3IWLd;5Qt}WKMuGh-7JK1Ey zO439HOPtw-8;CJQ>(s)kzr$ZWHEZtbVFm-Mho2YtS1(#;<_x20PG)009!!mN)Ah;r zuG}uMvvAtt9nEO~AK{`gA9S~@A{+p^0BUO-{AqBkOp+EdPA1us6ibC=gD)Ufr=>TA zrNB^~E=+cp)3nRUc455)7By%Owv#q)sm|X?NEp^us&lr~`L+^2V=Fn>Mfj!OzYej@ zvWd<`T2rhp+Z*pf3Wn)8G}u@a0Ewtt7|#%i&~dbjb+o%Y){#`xZ`G&M&8Z&rs5J>A zDB7{~a6eiDR&$wbHrK9c8MnZSxzhv(Em`8Bre%Q?4>@^zaqzXBt9CR|mC24K@)|yt zO+O9Q0K=`A1_VGTS?oA4d~*^zcSdVrSLT@$0bp0xVB55GW_sh9X15o47@=NjF=(^^ zx~p~oX9DG$03C!?L1j+5vo{H2q|WY+=HNsbR3A*`E)J%Qxe3+lbM#pFSl00@te_^M z&8b#c!I6a0jLV8`UhiydSmUg2*u3>(=lo6QI;$Elbk5y$;l|YpE#xEZ`b1|t$YzpQ z8>Fe-f=O7j)83uVrL2z5TwA8I7rSBrVhRu%QVJnopyO?wnOswMi{)(XilR>%ygd9H ztyg=5*uZH^rn{1vh5@*7#K?Lq*0Xu5vt=!C5a@L-T2F`sNlO#&z<#YG*U+@SsX7*0 zAlpA?b)tVXXPic}%OE*i#3kU?iCQwGF%F=Xv)Pap7^7u7675}_1eZlJyO$f5JWo%% zw`fO;hmVq8*Hkwr(@Ef57e8>r4?;54$|=Te>`*t%*d`%umuy@F#C|RfZCyT;Q(E7e z%x#1Xa?Hn+D(uJs6)qEvcEN-+w;tpuoAWa<$HoI6<*G#M+q-k|rZo7GRBKzV9rm}X z4+h_lmDx|l;#<`0bJiWT8$CuMurC8&XyB}621DVF#4{IxECa1KfJAPkHE>I^v&90# zz7Zzg7dCX{7S?$ivM2yhQ=3UP#bJA&NIL0c3oLRw6(~R;S`C2+SR~Q4%UaAnrdk?t zi+2z~E?!}Qo6WR<*gNq|E1qOr?cN{}O2q0&bU=X;?VGnm%>w!;gjpS7Hxx1&6S5%% zjRa^TBpfG;Vfy!It!8LloF<0xNvA6fJ9|K8NG5+DXK+%S#}MZ-$2@}Pj&<4#^z0B?^l9bH|AiOD2eoopOZ zq~318))XomHn+t#k>$aR1%*s%A?j#k4;)&f8Kbd9UQ=X96nS+zTUwOdctqKS6QMY~ zu?`^Z?J=m9E{R4rh_lXGI_os9>q%V-<}&DxA>2_b6fwXkf(Wsgc^a5{aO@7q26igS z3y1qM7{6Jn8lXm_ZOYa-(HMtNB#lvZ(=ISs4i$U=o#vz#@RQKT!E+-M@5oy1@f~1= zIy-ZoP$04!5qv;|Sk~0pnVvfhUM$HKOO}Wrq11WQdN?idY_7IO!LZ@EJ9=Z_wpW4A zgU`<{%W2po?lPbi-C3(GN%jlESaT9&qnkcAWDf$gHcDaSk}bJh1k~X8jmJ1D6f>1Svc#|KBH^;lcuqmxjKw$$`Zgo5tCzq7# zOsB=mNIQT5tP4)%Fcok|NjjB^aW&O$c?boZSv&_HwPo^Qg)p0`mJQvx= zy+bq>OY}4W|3Fq$LZ7EGb2W}?_Zz;^aK=FmO~`k(Fc;gHeM`Y&iowx&81i&!N7qSh4yo4eb24AR*+;4o9fEi!{g zKoL+)Ceo_sgQG2;LK5pilyL`WW6~$J*^3DzY2EdU*_9P0TuP{S5-Z2UVE;)Be`4-VNwZvjb{>V%i$J3Z(-fEd9BmtwKO)Y!e6Lr6FcU0 zWjb@o1hljB;4<9V(FzLJ)tR+=JGG}cXHpYd^;TpewWy^#oyM`M96&lMfPk=ZEznaH znv+^c;==Xnqhv0k@6<$TNsG3oi}Un;Y7R#lkg!vHgj&vs7b_Z1Z5>!S6{ny9DyUer zE_hsr165FbDKn&&gU7F%q1tG~!}CeP>{j1~3Ft&dO0BV2PWaQwcnh`Ns~v13e1?HR zGTofjYBqNZ7o|thY9+9+W3d$A?;M8$fsT0KZ0RK<0)3t^&6d;IbSaLjvz^@;oICgQ zW)txaoc+jdulDvo^s@rV?WqpLKS*9iQC3Sak`)|$Ak0FvHt=4l>u7z33F?HF=Vfe48+;Vt=gd)MO#|d399FJx0y_q+19V>wR9>R$IwXu~>b)xf$@AMSb;-wNM)Yu->9zy8wm2to9<} zM-m1z%~nr4ED$m&oJd;cJjvtr4Ogh)W~}RYy4J_iVErI+iM&<#uuGJenE0a1d(A?l zPImb+SHJ>y)d9NAXenZ|l+U<;NP~0Xfe4Sp^b$nDdcXin?0&|U3R;w>z`p1EX`7m4 zA2kK`P-#Luhh+zJw0Oj|09qqG!L*KbxP_8IpXnMlG9L*K;NeFO3%dr1I2nh=)X81m z5pNeRQZ3w&=Q<#BH(JpuDxo*not%0vJ%=7pw{ zFFOU0e?$QTvLjht&{Fx>Y~-M3gzkF9OjCg;k9cb_Q|n|0!J(sp-#x@MhB{RMwNsau z%YYJ5R7}OwI4wfJc&Ikw&0>_?0xNPynI{V3(4$drMP@w;T7*MBpdPisQ|cSB5Sp`C z4ASDroIl}A0!Xyr`4f*TjP0R!{a55W5Z@cIr90Im)!i@ zpu|Ep;KmrDNi0b)t?0lC~g;G{khN>+WWzB;0y(=Vn&{O5?gdG3@qtmbO@jW%J~2Yb%Y4( zDlwauYX`~-wagOfZgMg0_Z2dy44t1zoa ze+$dh(Q5Ev8{#{+xHeO2M5A-;qVz{f`YXy}8v?buC~Pc3Kq{2AKBfj#Sysmfd^T^SV~zo)5=S529WU(!mdKXV7q9nclv~+~UfH+mFwH@+&5c_=-K)|QG@stUy!f1+%VifN; zx*+dA6j7%$Kn$n4IO?V?zZ&uYc6?zBC%%A1NF0UO^%|Hcbam%c@O&Lp9W;MedlQV_ zq=cHpBQmby4vNg0IOa=VqoH?^{p)OTvapCm6n%pxl=6wkL_s6pGz?Crx#oTNeeSgC zpt-NH{;+WD?HX-+2&|@Vz!9>v0*A;{3{(e#9wxdFrt!m>n!qBn6wzq%vTis{!Y;Zs z>8ge)*bkT|`cvW{-S|_wOFqJcBwFqHvFfwp{PTJ}*~C)jRfxB$a0=dp>$4J$p{Y3$I3Oz7^Hyqq&{XP+LDPKKwPlb7fnXDAYwy` zMg>JIRBj83YtvZ17&Nio9Gvx7)T6l9kXXd*yWq;$?DXPR72M9!jLTYF{7r))u0;`h z>gv>F3mRK1IxHAj8D|qXl-g{}t6U2O@un2Z1Kr&*K|Ym5KGc`gUYaA}m~|23Xf$>b z+h#0+XlIL~JVwpH=MT%qzvKp12SZv$4EPvbRX;Cd{KI(ZrQtdful z?9D|nOMEK`TEn9JS(oy=z^K-E8`1TZ)PUmnnbuBdYp0U6$0qNeO<~MOVMQI^VLV8gqGU|E+Q$+weBi?l|pVjVlT2JXfUnf*nb!v1p-`e_x zSgo_$hqy-=))p!&UG7+=0xy7}78mjCrJgZ{gb;K65v|9NN_Z z`MD+T0T{=-Xfir#3f6opbmBr?7{ApX}p;VJ{Ab>z@mm{B@&T5!(Gt>md5FK=)GlPI;Y5uqY zj+U^~ZL!*$GGqwEeGfa^4-n011g&8L8q7nO?nX##UYE%8oQ=9pc|&M=m1~~s!^`kx z0-c0oV3CS#i{-Wg%qI zREuW^KLl=%^Y9C2-7UE!B3gKBI>NXn8Wm-}Vp^1nf{Ik0J^9X^jvQBkPSaknuBKhQ z>lnd=wEck`-qRETOdX?PqIRCQs<;=lp7ITLbVSHHibO?)T*hu|gE6ma6tb(BlopMB z86u#t1pR|x%W%R+M4<_vj~{q{uwn~(MYmWOQ9_%}Jkk10QUyPCJy9uF46#3qm**>I z^}{=>=hBTPRj4iUo+^Ie2_J%Yc*xf!@sygAaH3BmQbHmN&K_qZJZEs45lO+fBKbVr zXq?F}b{)zBsBa7yu!#z`ptW}K$YPCO$0>9XM=xB#ff|q+Ib0N#F~Pv_e6H@Jz`=d{ z`gTSe>1oH*=i0k?>46VAiXkFJYqa)DDacb+0!Lu*jutJ?sIrru<+dg$qSkd%IOg)C zv@z@w3`Wj9wi?#CRKh!#T7{$1t%aZrH$WJLbMI!{0U?~O2}oxSxsRlAfPJ&kIDq8> zD<%eTv^Z|>>=+QJSF}KZkZW2qKT!J;y zEbjtWPp&k0|D4YSZ=euS6d1p&DYFAgRtD3s7N|=PpoBFyibw$WJiDgADY-%!qe-dkHN+lfp3^-fx$CW6K zeC#1o9E4`Te>70+a@X_N4ls}MtYR_EKt%X=RWL9tRpPQeRli5L_rq1N(&!Y#=G?$uDF*wnK+ZbII3lu&gha-9f{%w8Rac;e zM}yW8>I&=~{Q-HGj+)GKIhLNRFXWP3KZWo`#uw9&T%SXc346mj)b&&DRvXpXFySE- z$c}1Pv^T9p7ada0U=T9j%8WWSSRjXy0C`5+X+j9n4ky>v*_}ny+$`^ivRvMXHe}Y0 zt@w+3DL^ko$%Wt@;C4LxH{|$fV=^MaVq9pam7Iww(xk&-n&rU#6+eojz8=o?qX4zR>1q?SqPSk}m5DsIiR8T`uSD-Sn(GNA+O037sL*f~ z9gRLz0c(qB9XzX28OGwGby2itkt@GYaiKK%t|Cb;>X5j~L4OW<4e6lm!RiLih+DF&<$~80JP2mtO@(kl?q} zedv2P%;F5=`i)vg@GSMRxlY8O^HJiWFw92J2BvUJZc(Z;EAi~=C}SJ%^5TevHPx>A z!*}#2HqZm1?TDrYu^8?_fWI3ei1+|vR5?s_+mV7Bca3}9&pr5-9&mG zpv((sZIy?EAw@h4g6(wv-C4m6{mLQs@D8y9Q9kmi)2JMpx)L)K0m{`X#w)mw0xKzM zPfWA04xAy-*}#BW2tfoDpxhHGfvF@X?ZDbZf%|d@{Mepi;72k$W6ik5gf7zNEk!|o zfq4I9yWH!a2fwO%g|^Y|1lk^640bh?+FHe693f(mKU&c+&H;j?eCWtY3!%ANpTcd} z42i(gwV{G1NNm1=M(Uv6F=xwp!dkb{OLf_x2wh(gfGiCDI()EFGVG zwv}s3`JLNd@|a&@%%5z}?R0$;Im^l{Qr}S3l1O*rBtk6MmEXDA1s*r#Ze$;;J@o$c zMK)7BvN}M@*Zn493!$}W&9(9RScS{;y?>%Lh|{IqMR?04xMm+PtIm#?cdhY*5<_`Q z-C&?DR|iZNW-%8-eZ477Mp!{UM?7XtNt$@o5H_e6{XV$aUST4K$3&Vf4yf?#aq`7Y z!=@DSH!vrAV98)!rzXg4*)|ywLEBI#Z z-Uz{_z&-0YEY+G#(i5~g*8NTDcwTMhXl!w)*!N23s>1uws!MdPqPe6&sqzex<4D4w z2h>Lhd(<9VWc18Xyg0d3ds6wXI#A<#kUYXDJ6cW8rfgd#)v+TFwGC%4LwO4`3@e1$rjjgsY=3_T9Ea-^7iX!v%Ugb(MK1^aEq%yLBPHh>PGyb!LS{ z8>SG2*~fjX0qfAQ6{!43HmbNhSlg(okdV}StHl{>2wK_rDg{E3@Puoo#v7}7I}-5c zIk+!Q7<1K!v2cP)7AdxQy|b}ljk7hjc>~w4IOkf6%(YQM)dR8m4V#v)L43(iQ;m7p zOB1U9`~ji**CA9t^!eVL4nuik6w^Kl@hFWr7b>?s%&S*W4_;*Xir!)m~^k6Q&J2q0?Zii)$g}-AcfEHA4e=---_e;@~0e$lHx|Y3aGJl(1L?0ouF~U^1OB1!;%2 zyQdRQ%+SDq`|HGA;pT!Z>{IQqtE2pOBkW}kNX^)lGFlA+tsu^4p@5xM8b)=lb;?5O z(^0SqH`sP}DdNr(Jv5rcS8$$Uw5^!POgxp#YP()H@(6C1%vo8)-fX6!Kmx3>4)UQ+ zYl8xK;L_Bk7 z+E5r1*J7pR>LL{muREJ;wh(Iu0a8;v4t%fTM6A1-wzvSpfs_=!#$L86>oyfygE8tBbtZRpOlcW2hdvu!nm%7W+G(S^bvY)tMH zUE(^qDH7W!nPK2MWkAKkMOgsf-DPSi3aed;*Pp0Qw>O9mXbhZ(dY)w}MtzUfoNB=h zBT2-VQwN0Dk4#eb4MkZ>jTp%N4#goCj*v7&{0Sh3)g2a2yTp*pF;RCR3~fxc_`~f6 zEu19;9vG|M1Q^&I%5#v%d~Q%DCqseWLB*6`8M|+Tx>TxoKo`7#3@RkxR(A&$MZ#42 z6ctEwa0x7-55x>|7*#^#vow{XewnhubU0e=1rcW)Me&K`@w*5sa@)Ij&j~)3*qst^ z6?+K#M;*1hXMO4(6G=2)4=;mb_`roBqC`;R7>+6dSB$F6;*@@rh0mg3gYCz_3=M4~ zckd)l)FJa1ymyj^?dFaK@7~EeN5_-UJk>z+0-}U?bG!?bk$#7AO_|Z<#PmS`E~pb~ zs?M~1NQJnB6$o+x$JpJmqocF8gWMzKb8s-J^#5fbnEe7)oJYgrOw;FzSS&Xb$9GRJ|HCbQb-Os#?`K{b_aq`i@q@11uZ2Gw{sM4Alj&HkZ&}lJ!%uM zs%lr+X;mTFHz3+4biQ<@Z-;dqeD6A%fxJ_fbtA=___4F4l+4VNFwhm{L@2xrWPx}) zu1taF1r;LF_uvt0jl(P7=>e;M9qT3|?G3bOcAB%D`lPh{K>QP5q0fbk>gr5SJ1 zk$Zule}gu7Ckra*AL%`?&>S1mbd%H+%7y?O!@m=%bp+wbWocCbNxw_qCxSj2cB2(O z4RCHOV0MwunH@bt=UsN;UG@f#3SAMJ?Air}8Jep>4W&7o#(Y-Ir>DIlNH&H6`Fo!8j?eOe7-j3?K2HrEfveOx{49LB{pR0fRS` z)0@`m!MP3!!u3rE2n%5fuJOEqrv>*;($ZNOshB%hint@)3!=EO204kcc7x!ZOTY@s z6bSAqk|4WV9NWMANR29ZBvAICv3Jd-xoO;KrWEpWPmke@RKRA&_=1sIsvRejA{Ke} zVg|zw&>m>}H(2gCmb(tZ_siil!)Sv-D!wGBQi0H&?}v72(e{cf z{fV1iv+~w!v7o?CB-=>RGmsPfH(1rLusWMAO(t^C^<+D{Gq{&aiU!=JkHB&v3?aBL z6Lm<)H0CsLEmv<$-DB9x7#4B~GYp~-f`|>l;s;0iI8^`x=1tNVkXU&bZ^F@K5uZwN zAQ+nOLQIi%pihCvEli-rEm|YTp{}mt6!{Etju^^Oc~Hh;jf`)j?tLi|9>Y19)I{n$1JoM(DO<*h_<1Uex$ilf~rk7I~-L=H59)Qt}ym4%mNEQTU`jPXQp; zN)$<-w#cQ6o_U?$<#lu{4gEqy(P(a+cWX*M`oX;*`s9wD!xGA6KnNAho)+Xva4o1s8P^Z>Z#L5uDo zX~lPr+%eNpI-}{>I6a_nNr?mqIz3n*%U2|gqn9*X_?$x!z$VK>eBz4squn<;u&2F12$wGWStFeO%}>ad60?FJRM zh+&-_sl*PtwiwbdZ@J}p)RT{(-xW|35Cs_uIe6FFs{G~2=e0);@tWxRU0pHo%GmN^ z@<;OJ1sG@kRMPOQe|GMGWeNibI2Jdcl zP)Q6A!iEDIGrRewiJ#R?AutUd%g->nvw93>e0 z;6d4x(GaJxTEIqP7Mvf@Tx|qp<3$7o32p~47DP}vkfUhFxErZf+%8JnGrdWCW(^HE zrIf34<&;wWJ_MdraxN&y*LNPzRO&EH#!OW8cGw8cea{$nTy&S!@Gd)r~(exf_f1L);kTbe4-5E zo2|g%??^F|(u0(7tTpHDh5Qb!fQOHF2k6kK2Z%~ttufrIN*Fhdp+jh7epLK~J{!PE z&MSh~v1-sDc)0<FS^FAmY3N%7=GA3duIbL075gOwH8$({ z80r>>tZ#~8Z4eqH-kruF6ls1}XF8S8o^EOej>P;HvRxWRsX;GVD;lPa)+m-uIpcwH z!^TkfNSHxz#}2M5L)ZfDZtpO05%?s-iW-sD@f7_`3R=tyd2#QY!S8dY;V@!=Nri%8 zaTOh^M?u{j-4J;PoIV z84XGtUTE||`U7n^EU^(7FI#4RB(>1otCpz{1klp|A3GM_UcHKB@nIJb1^d; zT>K;8R<@ZIPyiwEErViQg_~-i1fiowW4e(gpobe%hbR(&LUB{>`8~~fw8d|8w~EAX z&I!!~cvC{VZ(I$=I2NdeI>e4lvK41Y$&AxQb)>=dBdo;=hFUgrJSzGW(FhHL6nXYX z1UKA(qy?si%}3zZ%}Nlg>dE#d;=3H$chQW#t6{4XTZpR^c=63f%Coo_*G)j6kwpmB zj8OP3Zd6F0gdSA(9GyZp)q#r=eXLd-5jw4Qa11q|5)>k)dMugDwH=Rp8 z?75pR+_?JXGh$)W4A2zg6kw48jm{3xNf%!hW!FS&jnF4F6t+-Mj(28~9tDsXl<@8z z5Hs6E%PtN#v9K*kVy`BsaCihAZE*l;0)qf2lg>;kw~II6$>nzWO4AlFRXKPeEmJ;8 z#j~obrB4mSjGS{>H|*#TT>>`tF8TEiVDg!WBTxvCA1!E%wJR!C)fKSq@#w?|a|j+L zF7h6$0RrWLliUtOb3>Ohj_`;P{Le6Qt||wxA-?nq9S^DIf_Bh&^?|)pu@u^y7^%#M zsfK0fb5t66rO%{+meI)bfKs1I4d62jPc%4pYV@PvPr;NT^gayfyKrlo(}b9p!lV`l zjp^;4FEJcZ%O#q&0qQnr$Drk-^g|_{_66X@h&)wHWt(9t+}{~1-w6_h#bb-Cm7h!Y3ZPeE~AEoD)2^YRySqjhx{p1&E_KJQ)0~)o z2E9B1+5jmEbq$+6a$=xM34etpKd|3Q@Y}>Y-Z2yT3{zr2EH2qd#pE{XQ&~|;0Tb-l z2)oyGL$a8-Qr!cak~MNMnCslAr*(mg z(i{vx-_#KV>lz;&A06&rtSXOE6!fX>hum-RoiWCz>Og!_V8TzF|p{#o5PSeqIROM91J8j7{aO^Volz@_hEQvwbN91Di&ZP#{dt?QU zRsoDK5{e$Jf1+m2hJ{nJew=Dhgc?whZ7c`ggKc8p@emF&MA8>PaP&OCqIJP5WVrpb zs^5T|(F_eVMRH-WARzEDszNmGvt8g z3A!nGgyM{H%X&v{bASrFrBm)D$?|}N@ku?Va-{bytar7Jli^m5ZBTG-#ZX*+XAH!v zSnM(~K^gFfh?a_5g!rLzma^<7-@*$&wIqqHgG;|#xkgFc?b(8Nvj2_<&C!}goB<7t zqZ+h(f#Mrv$9L(|wj#1F(Q4750IE|tDpLIL71XW1a_T(1Q)j;`=Lq8ii5q}kZH#xN zE^MG&h5K(DxW4-R8%DSfHQ2odCl!b|e{47^AMs+Z7UmB@E882_^fW@e@njxzx`Q9+ z$G^a7?1{Z{1U+|}hF=)!;~?=^0#>0QtVH9N;zSoKQuqM3T4Lj@YJ&?lusMM<)grLz zXUi3jZE2a#+R;N4jq3{?+_&zAtTHZ-K-ymTT75s2;VX-w=w4u~+;{ZgvIkyV&+8Xq zrGuDM=AJU1Y&&rFlM^Jh<3@Pj(Lr19yTKwH(eBefL3xI%l)<22Ni#wPE^1_)1{cQ* zbTp-!QyGNU!e|k<&HTn2td0$Q=1w!_y~kGmtYa&tBI6i{$!DQQ!yBL@N$y4KbKGSV zosJS1&0KSv85zK6at7i#IlAR3Hgfw!G#EPNSy`I{gNBwEbZ@Y^+=-pl)!6}+T(nwb z@{BmF`h$){7p(AE+$rr^3!RW*8V(K$y`!g$8?G@110>P8LIW!qybk5Nz9P7hI2B~G zD3P$XGW;R9G|JmjQf;6B1QVi%rMEpwx44@6rc_5fgL_R8us6{byflR*Y$$sVx_icA zDGCay6a&a`1VtPGh_JTW(k^{bpD&DZ=fm&77urBEpfEAe7UcH`j=_}w-{S*#UZt(= zh%5^41ph{En?`X-9&CQv8DaK^Ea!a+l`Ba%5e zBKE+t<_ILNxgd!{gT(>VeBC3A+>Lu02tL3hfI4uwArEejaH2*g#$P0g;HO0#OwiYk zQGq3BSJL%q)K-Mr8OX=$3oXZik}vkC;#UO4q#Qsx!D zFcpLEG^Il$10bu+eVWdB_I)&&B9|uTx6b-}-<5o0D|%|u@Ymeqb15V1Nub_t`sDke zy2gL_r6yGr2qVGs2^0>KwF5^B14JxzVJ$>7DnjojxpqM5FliJ&*3Gn2JLMiGEDarL z^6!!nal{o)7-Y&j(?>F9V@qy9ujp6@ofuJ2>55&$YTt$#*18^*4TkZG^x@l}2{mFy z<9Lr&>C2aU>Q3MFU_qYO@N&V_S9s%0yI-GVd=m^u;wSgOKN=tV11cQxQFw24I=&0w@i|yh|H)De9ZVkrqzb8J|-^R5*xz?=r!dC`N8hbp#_f z4>Y_9sJvbV1RPhLDcFsXb@%Ur8r(IxL*o8!07P56#WdED%Q4&FRD;_ophm?EDTk(H zvvFxMnq(uc4BX|#wI-ocyr`b?0w?;+`L>?3F1G2S*k)N}*WAPtMUG^5QtP)n$rm*L zUv*akCRbIZU(yh^Y;0ieyx`P8`tB@w0PMZWJELM6;x=3&Ik_8+H5SC#? zK|f~@Q1i_Q0*=bCD!4=hhb1bTEP~LALPWrb!iRh;bIv{gU-jzMtM0BO^}2oE*L~{# z@7(+DyYH^&oO|v7vI^T2Cc=tzFOf4W<_rQuZk7di*=c9nHZbN}g3OdpUMuH)_vmf| ztNbHOTR>fS&^&dS94}+>UIuirjr}ak5lQkLb+NZ!(@}V<WxrWJ>~iiAeT5S8&}BJ9M;k`oMM4{D-k{OKE>yRLfE=$Amh3TkNP|G&Lszt4Tw z=y$kei z(BFc-2>O|)?RlqzHi0&SUI@AY^m@=6KpzRv<~AB20SpZ!Ep(px1-m0QwSWKj@6-ji0n0VbYG@+I`aX2%iUf zDd=UOcLwN#2>%@P4bV41-+R&0CtdxLh1)pZ=in%oph+h zCp{8j(yt;+`YnV>4?4`^lXfE91-dane}wRlLH`W;I_O@LExoaz^8)l7gh{VPnDj=3 zZvq`X)zTwfj4Xb3`?UjE;0Iw2!l$R+VeV$(PwKH%5gZQ(j2 z-kZR;0{eBJ^4Y~*tZ z@QFV#KH`@EZ@<^@Uhv-y{CD>mK26?7uUCPW{Mf>8gMaF-_S|=WVmOGm6!<-x4POBM zjle(pso{8S?;hZH-Ea8*;C~JHBR|W=4;y38edz(i%fMd;e8SJO@eROF{y)RVf&X^k zwGSB%A%pii@FO2K{01Z5^xf>an}H)+UMuiR9x=vmga2#5OCQa~p8@_Ha4ep@y`@2o z@(%Dequs4Q`IiEpg8I1v>CX{dzx&f$3^)$>EslS@*FW7}^m9^g^y-m$fVTHdGYr2H znq4BR`~@F&54^0{ef$$VdL6&gnyLGH#UUK zE5i8ibNmN*8v^~+GlF|w?tJDo$3H1|{;;n;_kf&v!dT$JdBs5v&z-kS6MUMK1EmW` z;C+_>=PIBN_*wfIKc$#1+j4Isa7wwx+Gp_QN&#y6oJ~tLaMzW<)2n>cw-R|(<)>8Q zB=E<8Q%YG1{2t(3O|TukD>(B8R!8=c4qVgcYG4s={s=gw))xSO9yq0$oC&oZ=*d^l zz0Mb0)8}gDaIoA7oKnG~f!7>l{9NrmjQ0Bsa7uBd*}7|zO!YK;Hk>?w`H)d)$|5U-l>Q9;AnhvE7bx7y-Jn1|HeoEKK-!sk9 z3D&pWrGabul#a8W91WaOE9sWqb&lZsc)9DcF7p50_}ai&fL(F_fRuJ>u2V z4X4!pT%`XpaB6$bML#zF^Q!V(v2AtBNYk*gM!uTnL84vs!;FK0WjP%bt#`q~+ znF;>=jy0UpFsTOa`Vw$zmDuj?7CfvTZUH}~I-f>5XG;gG^EKpPATxdyV--ka=fKa?U;05?uwTgPAw+!yMYJ$|Att2 z{x1_;^B-&%4+EzbkmcMLH-1WIzf@xRIi@_kpE*VFu=4-3;QCx@IpuS^>wVyq7D}^n z*XJsXpIT40i}96)QwuAf)m>%4sdZWo{6^r^uCZQCnqlcs8^iQ(6?`Ab=V5nR0&gLm zpMalQ-Zz1duCjC}-Kz$Ev*1(k+fVKXKc(^Gaq|P<)RwSbt&s$^+$i1u7x3Q(oLT{< z^M>GI<^MkTsU2yhfX!?{MGM!t1Q;PjOzI&BKRQ_OpvK2VB036rWe^U8#udfIm zmd|fE{;?jlLCX;JD0y*xF14DI(Z0r=7@p1)!Ihs{Ue>p}fCuMAPXZ6l$CjUD=}?Qjwrc8*?okO1C*5 zn>5exGhQ{uZt#B(IJJMQSF;nwPbqdE_}>CfZC@?$jq}6b>juH~I|j$iPn}};m|t1~ zEdN`9(>j9b#}^nsrSxZwws`*oJUE|x3plkqlB~Nfl@F@-jXPFv?G@`wx)*uYpsWc+M!hv23yN)9Pj_`2Pr;)+Eelmo#KepIU6n z0gnGvc)86HT=|3Rmy3YY8elx0J9~-o(+VUB{0`v3{`?K#!FlhsOEo_k`7o04zA3oo zht@4$#ErwH!&IEwOqtHPs}eZ1i=R0Zywew#SQ3xAFSnEyg$w{8tLD>C@_Df8e8+ zBYn>*j#LkKZ3Ip$Fs48D4C4>3za9WiYbxe{(V5}puu5>vPq6?058$*8<9Ih+Iy&X2 zR{K%p^Bv%I{RVswc=y@HPb(vNeRoa%tl_i{IvDs5fYX{{Z{VMhimK1O=_3PX0Uxu% zaB5*W4!#1M)()(1?*pgSH-&WC8!R1aJJa(n;ItZJ`9A@i+I|^6+_kXL(xH}~_3%-_ z!}`6Kz)x-YG^8`B$-q zYXH`tGh4&!`9*?j{h{>`+uaAigX_fql!~GJv}WY^KK&fSX$8RdItw_p@%thF{lIC3 zcNFmM)t1hO{RXhy4wsHapBwB?ZUr8cBfTbgSoypUep-)7H|4JL+bw-+`}y2kfd}Um zB^|~eY^Prrd}4@P^hWRp$GaJwmJY37jzfOFD|lG{{44O&I!vm!yDsdqbZE6!4g4eE z!FA{*(y?oK2K&#wx(%mQA=}aUz=Lv|>wweRstnJaC=Dd6{3i>p&!siyufSgqJSa!K z1~{#?SU<=0S~|4K;k@N)!Nbb!zri0|UsU&n^VbWm`J~m0bZhQ<6nJpG-?}C|ol6DR zbZD(A)!toy2Ttp;6{8Gax7PS+t;Bk`$2!AlWmu1N{scU@p8cXsobr|J3s>{h+-165zr9 z-;=}1dV2d-iZ=?`#LsPCN%5#JoAWzld!8J))R|n>*4Zdt+?r+Mdrz{VZ=HDVXzywh z=cvu4RWoK*g)=2(UwleDm3PZ=P2yyyd%d@^yP>@$DaQk~i#z_LFXTLkX0htVyxQ2z zu9WN&T$}1?(Jh4H_MxxIdA&09v$MCUu+M`EZG8U`I_DK`Q5)*eH^hOwVe+In-48T;ShrT8| zSBJ!}cDH2fQOLbmnY$@2Jv+O@ZpYp2REO+9N;QWuSbVoZDeQK+i?5#GoIfAk?2%nQ zmbu=B#<1A-bajRzz7RtgD)iXDM18C}*IiS6{TjC?I`A}2OY3N!NY05#c9d8-OTWN~ z;+~_>>{a!3$u=-?Dva9DQZ45r^>{y)^J#Hi8+fz0<4{;x)J8ee1oivIo z%(A5V!!-X%-1u8$*G@k7|K=@1{JkV|HXAGEcdZa`k1IZ(W+xLdnN-v-Ni36d0q37q zH+Nah;<}{n4Ck8+nriWOhSQlxOnj6`ck8?$_Q=tgi8X`fy2$0f{f8V4ch2KwVQ#S0 zr)o%D88ow5)oaKVp+L_aJauf|;FYz=pFLKc_R66yI(oX=q>FJym+NUFq&W2mgnDgr ziIGvT>S5cRmu{11aqByz4#D506ziMoc)z^FcW>GFgfKN6uhOjf@=GVcF`Kdnj!r zihiWh`BQRVEpHK3h$uyUpQW1IoG~SWhVvxU+2IcG))kA2bTnL+5_esLIZI8>Uov}9 zBAKY0C-bOzpDC3C=u4ByRqNIz#Vuoxo-`mw-X_-01&FXoskXYx}lHBpik$~iTWm8 z|I%S#J$)r*e+}`IG$hxRg?jrbo?pU+`TY{gtq{M23zJJv8_4BpnJi-#*>U9wCzXlD z>rK2Y?U2uR3jFq+GP5kngT34b-zi6D+CUpgFPxe94OYRc)3v+3exdtVmd%~$&}D8+ z4snp_&R`2fpZB{V9c>Y-(F1(_$NB1!Y_h0y(nfdSm->>KHS3hxnmKb%KRsDqTA{@; z(tMrC$$9ZSZuUbYg7SLiA!h_FGrPK^oo6KV>e|v3v^#T6PF$u(ZTeXCi8O6RBfX4F zp&R7$xo?M-GT72!$W!P-``WZKOsTqXHJL8$?B0Heoqd7pHvdd7WD=!iYeh6zlgf3a z7;`E1fF`{6gmGGLfj)KC#O0`Ywv12pmG^n(v7Bo-NhJ&cRIgH3Rq*oExjr zZmc4SLpGNBd$B0}d{(EAba&%Gc;BPW1pUs;^G}|J((1*Pt3GRKug!n;oQ#@`2S;Bw zAq6BIUxI0>L)|J><28x)QT$rUEVMh-rQL^|U8KJDWVR$nt?O7oAhB4E##jtr?K_t1 zdDe)cb;8P%#?ozn1gw;2rWeZijA=KVLqGKu9a-EQ&ILWn>g>%phVJaY(B3{V)Co$p z)Ib@g=KA`;Li1MOKwu+%a#g!5p=6=e-RlI*;R}!A3`JSzmnY9?ZmZ9kP)Va4*&fuB z$%Y;$ty|fUYQuCe883~OR{AC_s_f1TtdY#pv&S|kLzEn75x38pB@@=!b83>Y((=-o zny+-%XH_nw^+)cZHx}lhcO>;s8BgJPj-*ruvo%0BlzT3d&y~JTld?~=v#!IPucziP z`kkU#>-yAKJM$FlO#VaaSGHWf_>=FJbKSbJOiw9Gh^EjfwN8~jSc`cFM{t{WNTUjz zSSBNT&U!JLFPc8SEYpAncEXzBi)0=iZ|@UJpEf!6M;yPp;U2?Kk%z!omi|CLJvzXQ zgd*dBexy$qVwU%)GaMh904DnR%}zL}Rw_jJN~OF@Orh+&=XyQt**CfuyULb#w5(00 z4>ii8BF2@ZO#WLRutcoLs z)Bm-1y7LF5x#l0bn?w>SLyMCniUnp%gIEUB1UHlKNJ?}Fr{-ol%_`0k|&Jo4w*mp3$+k-7G(?0*ru}UaTT5NJyHQl78**Zar4k)Y{Ut z+NL4?_b7IC-dPqbnTlNO99QL>{!-(#B6&=fDAq!?FcR@pg?Siht6kC2*lIWTDCFQ8 zLSG?g4Pk{@xVT=2ZnwO2=c$i)!FQJ)F_WI67rB1N~*qv!Lp!Q<; z6?v?f;bTgu&GsUeM6f z8W&rH)H(V&!atZy6Wiqs-94TJE#@7mpT)43U2V1u&ljA+5d#{mt zr=3q|*Q1lnv{r@s^MQ^wlO030Qt~&zD0+YyT@Br`XTa(g$8Y91_bZ9bgWq(cEV+1@ zwm;w7W)y(~y>I3yH@9ri9Ta_ff~mfKB+aT?HT3D#O?u(XH%|<{qLq^k)0vHAapSiy z2s1D`vTg;AzdW)D```i!<14BCuJfF`=e> z(z#>|ltN?*gC$ygb%%`z3tV3iKI(CCWVIzzbUS`N?GPn|eha-Un+*yHb4zh2DZGc6EXUnk!Vv(cEUku3g&@Gb+8ke@PWZKFz41H6+l*I znj5;Kn-ax_OIgSsk_{xXeo1xIAzjiO>5}G1HzmgziM<-RuSvTeB6{JU)+0x+@F)&1;{ ziI#NOu}X1i(vx&fOp)h(*h5_`sYOi;I9FYN*)B_gL9ne52jST*m81(SF)(dW2Z zA%g$vWR_|yk`BnUBwC74ImKLw`qWJ+>xDYIS~}9IoWObTv0J3StT#i=W!@QJ3B;Lyct;Ynm2HlJB>1w=BDbWRzOw9j^$ z178%=OfeRejxoYIDc&B;06^S(wKRyMq@eV}t&g-qMp+E;sz#B6=q7N=`ubgTkV~kFdEsO?}&66=qud*g#3+pC$Dt}pHqqmHEd2f z)P)y}AgyyMc7G|tAJk3tGPFH{oP_M_U0NHptTgCPVMKN3*+bs&O{7xmME){6Lb-`= zudfJMo0C3~;$VYpKs>0mihF(28U(eeZcB>4sm#|Ntdq($4d>pbwE8Bpo?|FdvVn9e zVq~4sAw|+X%;@$hC)v|d{E}IEnK`FU^+J_LBeRcQ64FU08+&?eM^C1P6}tzH`F6!S znYcW9X>YlB4Xa6snTd3+ZVnt-hT=15GLRk`(oQ_1oHfPVBE)G!#6&W5%Cz(pNYgND ziL8t=k~GRywbqFFTT|=mWcGF@x3}Hu&kry6`cnXSLjdN=6MCC*WNiVl$fk@}=GA?i#D06Ia(3>L- z7`zu0k4|p60gr&rFkqQ>f+8TuS|ASCU5Csr2k)~h<zKNy9c<~T>WM^ds}|G3L{ew2I?eYx*cee&F*Rgs zjfFYZ8nt4rvDn_Pygk*Cvy8!vqsZNWPpyi9K4jx=W@gCBBhy9sY-^Utx@(4;vxN)P zV`bvnncmcVdl(}Tp;tK{ex2+6v*$=294mKb9qjnz@i{}yu-NaL@l}}p{=)nUv8F_I+TFK%BNha!SEj=Gg(gxY<#06QFNfSt zDumBkZ7;@5omA1-X~@WFLx?O>#{&*vc_{ zWD5Dn+imuW8HLY|YRQNiX9kjYikr)fymO#77m&9{v81fzewvIj)ftgwDrPhta$HXs zNP7s=Wvv-l_O^C+u1$)8S<-dRxho@9(I^UQSoYQEoOLMvE&coJ^EZvCEds=MqkYV@ znj$q&QxT79nWlp2P844@?{0F-!?4OUV4ur{%Rj;LPoywe#$v--d~|2Z8O%UaInj8_ z6oyl|#Tj3TY1N&7a%gp<*mI5Qw6~AUt_r`{FJv{qffw_C{|2CpkT5NH_D$sqnuD=m z(M&TbM=XBKS>(%%*J+Denj3l>d;{6=&tjxu_~*sB?!da!Uu6gjbKfFgCdQ3rvPzFP zc6PSW_(eB&SLzl(`K&613-NXCcLb*;73FA}B~mERP={tZ(JaAZasR1ACJ0n77!>LZ zkDzs=w7=|p6uCR}3Dl};mz-87d(6^JM*42G(uB*sD66vDWeg#L9=6AQ{NXYe4Cuao z*F*_gp*Tvh3(sLvZ{7nrCXe#9Me4UsI-7thtjIMW9?vq3%?yg#vx@VX9Nh`VrxDQm z>r7s(HN50$9c$IBXh};?U%PJKoI6*%$IHPJ=?*1|Ij{dvkj$ft{Pham$j;71q}a}V zlN4Yw&lfIo&Vy?Hh}Oj+CqZRCRxD)lmt$g5G~o=Umu^q#2vz|MeZ%GeDu9Rti-o#* za6L-e%|kwa7FFW5U}@T~rYH~terB86v}1C@TVLH8{th$ud_m3LkcLQm8}oUu1A{~| zkAtm4@R$(y&+T@;@Y>B5q`))6Yxi2NhZAK2%_~Eu0H#3}y^h3AA7?R81e;jsoFQ9B z;g$BRZx@HFLVLT7w(^&3DQhWR)zMel*e6?9j}t3Z7ity9DPC#w`VNU{w71*ttPxeT zoWhh9NXk9&vDcsx@T;q>*DH0WZkNi>(p8-j6c2Lp-#Irf?e28zwbGVWobKJ)EQv5A zsW&wxTh=wTbjhh-&To=S%ntsxH27Pd+0dS9k|>?M`Yg$=t^`ZPD$*;Jsk|OoDOgKm z-ztgH(6LI6Dn~HYv9goD8yXws@L`79QXMV)Ek(tjh>nxncZ?h&(RY(S9QW=$gmdUg zj|?QgOCVe-;degzsFU%xAwKChHF3F0jCi|)l2pr&z2uMauieA$kS;jT!uniIn(+a9 zvjo$$8NYw31mq%3v|5<|kv#^z1@h}7U;aOKrQISOgM^uXK8N%)VgJwcGw~#isGr;Orep`b>jUw(on+4^ zeSjYZ@q*=lOCbLD6BeIzi#BlI#d0Hbhy2hO%>Tv)i%(iZMo_z(0Uwl~U_v%h_+ zyV>7voWb(u^GP2U?g_s5+Yo;n;*-xMD*qTJ{jFeHM120ci2p9)_annBH^yVWE|$2; z!uZd2TK=Eyw20H?p8sNe(pTiBKfc#x@x3mKzbWwj7?1QXf%u!&So}?EEdEx07-V7! zD9PLML(^jZuUK#KuUK#KSCA2u&n5hx{7}v^0-%h4OaQl3EsowQ5rCMV^$tA0OFMVp z73AF~Py8b;HO@Wm_eGYU!T6KR{FE4Vph2abPip7@3_u6&#Me7I2zouhP%1&X{w=8}?@;_B>cgr8YKgSR4-&lA2DNK39 c(0kAKqq*h3cb@paNmz^f6kim{1cKiG1J%YDsQ>@~ diff --git a/priv/cbase64.dll b/priv/cbase64.dll index c15493090ec61318de8b09f8f416040d216066a2..649ffba83a4b023824247655fad37620d0147a75 100644 GIT binary patch delta 32 lcmZp;!`g6%b%OvSb6r+avoPa!VMazqLl9@XxDn$^YXGf!3D^Jt delta 32 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 jQ(C{MJ%7D+^<23WXe$NIB~6%VLO?Z43M#UdV3B diff --git a/priv/enlfq.exp b/priv/enlfq.exp deleted file mode 100644 index 82ec3c9575e84c1935b4ea96f7fcf12ece60c033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 678 zcmY*X%}N6?5T3UFR1vi|K@WTAp+yPXT1u4)#adC+DpoxOWZg}dV7jT<^amfnm+;`- zr|~H~3f={s>~^aT`R3>AWG0z7Xq>%Ao;5D4xFJLmIbZ<34OcJvWb+-b_X$%diH| z0l*n|l?-K9TZPM_b$Z-Bffv}zfjjb`wM%`jn?eSRG9|7x7a?_TL*>S$fsGj)?9vM* zedYA5LoQfo6~?t{k^a`b;o-m$R!0v!E@{~=?a)@BnZhTAjNUZqm6TZbtb=Z;Ldtk* z+ocM<;{iRAp&nN&)Ijjyo|ulX0=1kO^mK>~SN8gZd-#V^W*-@7beW@xZLi{>V&?Dm z&E*XI-E%r(!pFAL!v81*;6Lpwoyt@pEA>@B4Z>S G?feHZ6okG2 diff --git a/priv/enlfq.lib b/priv/enlfq.lib deleted file mode 100644 index 3ed51cd592b43eacfd3d05ff521f031327e30b86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1678 zcmcIl%}*0S6#wnEf;7f7!hytS)`Uc9VugNaO$aWCi4CQYQW9@N=~CFFYyoS+32vPH z54e!{M{wmzxR}UE4@Nn8^=|OJ*_mw{S#1r>X6AkF&UqGj zvX;gaGoDH*ZaD_v24L(0kwYMQ2J|I(3Xz{%m@k#Z%&WzhrTjv9z9d$?dTk3r6qgEx zKh4;acHdfly;PhPGgIX$@^?2jh3D3U>$yz?OwUoLTTw?T>|Fymi~wgOB>H3;Fkm7| zPlR^uIsULfL-LZ@CY^d+CGxMTXL8w~>dt-yJKqwY`zeQ;lnalfv%2zbZDi4xfEUEP z*Mg!p;?qgjsWdAiDQy}e=9K1FrJ$0)#@Mm)d#cpnqy9xx`?21Jb!-xiV}L~r1W7%_ zV7#9+AEh3+jbR*kcjnyccH?cM*0dg#My*1=IIHX(aI!c~8T^o?0u4|1<%-==%WY~vZLg15aX;^=s7d80nz%wN=F;S%T zKY$YgkXVptmk3E^a%~7PasI|UIg){Kc$Xu@@lCgaBDjICqK7N2W5uQ)+HW%7_r|>Z7zcV diff --git a/priv/enlfq.so b/priv/enlfq.so deleted file mode 100644 index 9a2f05c8762001e1e43069cbb8e6cf197671215c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 278912 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 j;Rp=24{a%G0r_kjm?99zp=#MD!F;m z%M^N?LN_V&1ckm_p(iTzc!j=Dq2Hj;vlaT43cWy~V?{8ILWMrppyD?`6?&mUFH`9K z75XNH?pgKm8=wk(yCPqq(A79rsnFLc^1BpzkV3Cg=rs5Aqeh|U=|sRh@%$1Ai5goM=11FEFu1h zR_OAlCYWJU=pjOs|BX}V6BK%aLVre~Co1$S6#7Di{**$`R_H?%dVxYmf@2(o3jI!l z3jbH=ofUeSLhqu`H!1Yz6neQrAE3}H6uSJW80JTrdbraOQDA=^zI5hLZNR{=pz(*qC$^W=z0oEY@0$Ks>sJFbo``?aU>}8 z>kX==B`WlX6#7DiK2@P-EA%xAy+ENCEA&Ez{-i=*tI#(q^fHA$O`&g6=oc#Va)rK7 zp;st${kM3CU8&IfD)PG&`aFeRrO;<8^csbJi$bqe=miSBPNCCp{OCu$LdQ?u7>8S- zM;lb+R|@?mh2E^tCn|L9a|>hS|2Hf2V1?dOp@%8-aE0DOq2I31Eed_3LJwEy_bBuT zg?_O@AED4EDfDQC-bXt48_F1;KzIkj3mG3pI8DEXY{sJq4<>_#(n-Dm2tGKA-T*39n*&4&hf2Udi|j!mlK}obf4yUqyHs;}Zy{ z6rrJz@lk|dO?WorQG`=yZAfIiAK|nVHN-J~IpO^Xk7m3l;r$7ZVEjD7Db;JRFdjnq zK*GZqHxo`ve}l&OiN6BBhVZ77JpP175MIyt*Mw85-%!i=XM{%)Ud8x_gww5XLnY&H z6HcjbLpkHG6HX~eLmA^Q6HcM7p^)+2gbyb?oAKueA3=B`<4+JilJGdjA0d1c;n9rW zPdKHd4H1mrL--iNEsWnm_*lZj7{871YYEpFUq<*i!kd2O@h3c*@Os7<5l*41p_cLa zgij#6it#yw(~{ay$@mPyX^CnmXM76b6w(^X7@t7+WWoy>A4T|egl97zMYxUdM8^9O z9z%E>LZYG>kyatW&6U%^8=x%5_ z!Q)SO9O3nhe@*xegx50u8R4|VG*mJEA>owzG*mMFHsP}fFK7I9!s7`qWBg^pXA@q? z_-?{)Bs`n(=Ln~zvmuf3CkUrhrXh~;M+l!ocr@ep6Mi${5scqM_*}v*jNd`{Ji@~m zzm4#}5Uw%4jBq>QO~3H?6P`$TJ>!cAr=_Q%mht(7&nLW!@i~N3YS2*0_zc2R2rp-R z3gHgI%NU^KS4MxI7{o<&P71I~x7bZiWvY__?+a9)H|U=-vi6*zZZOMU8~ z(B7+Ae%>pT1$c0j?@Q%Mhf=@C+xnhDdE5R|0XA2YZNGb*&2-TA)+uKXDE!_j6m}+s zZRsl<`vL6L@~*KJjjcujsDwS8Z6#wrE3jzpu^0ij4gVYrdKPjfGN+IX`}ZqQiKs&2 z^BPhiL08CXEH>8)v?;I2=8AXQN~YnAZ?ctGoA*(UVslmRqT=pZi1n|%LS<6Xq)SnA zYO2kBCuYDrrn;KfT2F?)KxY+tC3HiTpreK`uA$V5TB_YA(Z>W+0ENDp5V1jI7mTP( zv6Tc~MT?isWpzhZ!H!B?)!2$w)M(Bapf$DE*anNboI!3YLKzY|+s(r2@w3RPXjoO_ z$~%Cb*~KbRL1(lW7Eu*AwSB>&lice`fy-L|5>?~w05fc^SN2sg_fz*q)QJ-HHdhZc zrC!VH&iYD+euKbGcITmZ<2-bIy{L#ANAr#PL6od^7qXV14{|J;tt1%2#Hq2l{zV!F zA{lACIL39rXz2c*TUugWsI%I=5i1p%7k%^ixyG=>DXmkxy_vfmYOCG7g#og$VW