summaryrefslogtreecommitdiff
path: root/perf
diff options
context:
space:
mode:
Diffstat (limited to 'perf')
-rw-r--r--perf/CMakeLists.txt48
-rw-r--r--perf/benchmark/CMakeLists.txt21
-rw-r--r--perf/benchmark/bcm_benchmark.c153
-rw-r--r--perf/benchmark/bcm_loadconfig.c91
-rw-r--r--perf/benchmark/bcm_queue.c187
-rw-r--r--perf/benchmark/bcm_queue.h10
-rwxr-xr-xperf/benchmark/benchmark.sh107
-rw-r--r--perf/benchmark/config/bbq_debug/debug.ini15
-rw-r--r--perf/benchmark/config/compare/case1_simple_spsc.ini14
-rw-r--r--perf/benchmark/config/compare/case2_simple_spmc.ini14
-rw-r--r--perf/benchmark/config/compare/case3_simple_mpsc.ini14
-rw-r--r--perf/benchmark/config/compare/case4_complex_spmc.ini14
-rw-r--r--perf/benchmark/config/compare/case5_complex_mpsc.ini14
-rw-r--r--perf/benchmark/config/compare/case6_simple_mp0c.ini14
-rw-r--r--perf/benchmark/config/compare/case7_simple_0pmc.ini14
-rw-r--r--perf/benchmark/config/compare/case8_simple_mpmc.ini14
-rw-r--r--perf/benchmark/config/compare/case9_simple_mpmc_overcore.ini14
-rw-r--r--perf/thirdparty/CMakeLists.txt5
-rw-r--r--perf/thirdparty/iniparser/CMakeLists.txt8
-rw-r--r--perf/thirdparty/iniparser/dictionary.c383
-rw-r--r--perf/thirdparty/iniparser/dictionary.h170
-rw-r--r--perf/thirdparty/iniparser/iniparser.c949
-rw-r--r--perf/thirdparty/iniparser/iniparser.h446
-rw-r--r--perf/thirdparty/rmind_ringbuf/CMakeLists.txt7
-rw-r--r--perf/thirdparty/rmind_ringbuf/ringbuf.c430
-rw-r--r--perf/thirdparty/rmind_ringbuf/ringbuf.h29
-rw-r--r--perf/thirdparty/rmind_ringbuf/utils.h115
27 files changed, 3300 insertions, 0 deletions
diff --git a/perf/CMakeLists.txt b/perf/CMakeLists.txt
new file mode 100644
index 0000000..c24c2c7
--- /dev/null
+++ b/perf/CMakeLists.txt
@@ -0,0 +1,48 @@
+cmake_minimum_required(VERSION 3.0)
+project(BBQ_BENCHMARK)
+
+# 头文件目录
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../bbq/include
+ ${CMAKE_CURRENT_SOURCE_DIR}/../bbq/unittest
+ ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/iniparser
+ ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/rmind_ringbuf
+ /root/code/c/dpdk-21.11.4/install/include
+ #/home/liuyu/code/marsio/build/support/dpdk/include
+)
+
+# 将bbq单元测试里的公共文件,添加到perf里。
+SET(UT_BBQ_FUNC_DIR ${PROJECT_SOURCE_DIR}/../bbq/unittest)
+
+# 设置输出目录
+if(NOT DEFINED OUTPUT_DIR)
+ # 如果没有被设置,则设置一个默认值
+ SET(OUTPUT_DIR ${PROJECT_SOURCE_DIR}/build/output)
+endif()
+
+# 设置编译类型,默认Release
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+add_definitions(-D_GNU_SOURCE)
+add_compile_options(-Wall -Wextra)
+
+# 库生成的路径
+set(LIB_PATH ${OUTPUT_DIR}/lib)
+# 测试程序生成的路径
+set(EXEC_PATH ${OUTPUT_DIR}/bin)
+
+# 指定库路径
+link_directories(${LIB_PATH})
+link_directories(../bbq/build/output/lib/)
+link_directories(/root/code/c/dpdk-21.11.4/install/lib64 /root/code/c/dpdk-21.11.4/install/lib64/dpdk/pmds-22.0)
+# link_directories(/home/liuyu/code/marsio/build/support/dpdk/lib64 /home/liuyu/code/marsio/build/support/dpdk/lib64/dpdk/pmds-22.0)
+
+# 可执行程序的名字
+set(BENCHMARK_NAME benchmark)
+
+# 添加子目录
+add_subdirectory(benchmark)
+add_subdirectory(thirdparty)
+
diff --git a/perf/benchmark/CMakeLists.txt b/perf/benchmark/CMakeLists.txt
new file mode 100644
index 0000000..323240b
--- /dev/null
+++ b/perf/benchmark/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.0)
+project(BBQ_BENCHMARK)
+
+# 搜索当前cmake文件所在目录下的c文件
+file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
+file(GLOB SRC_BBQ_UT_FUNC_LIST "${UT_BBQ_FUNC_DIR}/*.c")
+list(APPEND SRC_LIST ${SRC_BBQ_UT_FUNC_LIST})
+
+# 指定可执行文件输出路径
+set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
+
+add_executable(benchmark ${SRC_LIST}) # 添加可执行程序
+target_link_libraries(benchmark dl iniparser pthread rte_ring rte_eal rte_kvargs rte_telemetry rmind_ringbuf bbq m) # 链接库
+#target_link_libraries(benchmark dl iniparser pthread dpdk rmind_ringbuf bbq m) # 链接库
+
+add_custom_command(
+ TARGET benchmark POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.sh
+ ${EXECUTABLE_OUTPUT_PATH}/benchmark.sh
+)
diff --git a/perf/benchmark/bcm_benchmark.c b/perf/benchmark/bcm_benchmark.c
new file mode 100644
index 0000000..5a7db7a
--- /dev/null
+++ b/perf/benchmark/bcm_benchmark.c
@@ -0,0 +1,153 @@
+/*
+ * @Author: liuyu
+ * @LastEditTime: 2024-07-07 21:19:02
+ * @Email: [email protected]
+ * @Describe: TODO
+ */
+#include "bbq.h"
+#include "bcm_queue.h"
+#include "iniparser.h"
+#include "ut_bbq_func.h"
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+void bcm_report_printf(struct ut_cfg *cfg, struct ut_merge_data *data, struct ut_exit_data **raw_data, uint32_t thread_cnt, enum ut_thread_type ttype) {
+ char name[10] = {0};
+ double latency_ns = 0;
+ double throughput = 0;
+
+ printf("\n---------%s---------\n", ttype == UT_THREAD_PRODUCER ? "生产者" : "消费者");
+ double use_time = ut_clock_time_to_double(&data->use_time);
+ printf("执行时间 : %lf 秒\n", use_time);
+ printf("执行次数 : %lu (burst=%u)\n", data->run_times, cfg->ring.burst_cnt);
+ printf("成功%s : %lu\n", ttype == UT_THREAD_PRODUCER ? "入队" : "出队", data->ok_cnt);
+ printf("数据错误次数 : %lu\n", data->data_error_cnt);
+
+ // 同时有生产者、消费者时才输出
+ if (cfg->ring.producer_cnt > 0 && cfg->ring.consumer_cnt > 0) {
+ throughput = data->ok_cnt / use_time;
+ printf("吞吐 :%.0lf/s (%e/s)\n", throughput, throughput);
+
+ // 多生产者单消费者 或 单生产者多消费才输出
+ if ((cfg->ring.producer_cnt == 1 && cfg->ring.consumer_cnt > 1) ||
+ (cfg->ring.producer_cnt > 1 && cfg->ring.consumer_cnt == 1)) {
+ for (uint32_t i = 0, bbq_head_idx = 1; i < thread_cnt; i++) {
+ if (raw_data[i]->arg->ttype == ttype) {
+ struct ut_metric tmp_time = ut_clock_time_sub(raw_data[i]->metric_end, raw_data[i]->metric_start);
+ throughput = raw_data[i]->ok_cnt / ut_clock_time_to_double(&tmp_time);
+ printf(" %s-%d 吞吐 :%.0lf/s (%e/s)", name, bbq_head_idx, throughput, throughput);
+ bbq_head_idx++;
+ }
+ }
+ }
+
+ if (ttype == UT_THREAD_CONSUMER && cfg->ring.workload == UT_WORKLOAD_COMPLEX) {
+ latency_ns = data->latency_ns * 1.0 / data->ok_cnt;
+ printf("数据延迟 :%.0lf 纳秒 (%e)\n", latency_ns, latency_ns);
+ }
+
+ latency_ns = data->op_ok_latency_ns * 1.0 / data->ok_cnt;
+ printf("操作延迟 :%.0lf 纳秒 (%e)\n", latency_ns, latency_ns);
+ } else {
+ latency_ns = data->op_err_latency_ns * 1.0 / (data->run_times - data->ok_cnt);
+ if (ttype == UT_THREAD_PRODUCER) {
+ printf("满队入队操作延迟 :%.0lf 纳秒 (%e)\n", latency_ns, latency_ns);
+ } else {
+ printf("空队出队操作延迟 :%.0lf 纳秒 (%e)\n", latency_ns, latency_ns);
+ }
+ }
+}
+
+void bcm_report_generate(struct ut_cfg *cfg, struct ut_exit_data **exit_data, uint32_t thread_cnt) {
+ // ut_report report;
+
+ struct ut_merge_s merge = {0};
+ ut_merge_all_data(exit_data, thread_cnt, &merge);
+
+ printf("ring类型: %s\n", ut_ring_type_enum2str(cfg->ring.ring_type));
+ printf("简介: %s\n", cfg->base.introduce);
+ printf("配置: %s\n", cfg->base.name);
+ if (cfg->ring.producer_cnt > 0) {
+ bcm_report_printf(cfg, &merge.producer, exit_data, thread_cnt, UT_THREAD_PRODUCER);
+ }
+
+ if (cfg->ring.consumer_cnt > 0) {
+ bcm_report_printf(cfg, &merge.consumer, exit_data, thread_cnt, UT_THREAD_CONSUMER);
+ }
+
+ if (cfg->ring.producer_cnt > 0 && cfg->ring.consumer_cnt > 0) {
+ printf("生产消费个数验证: %s\n", merge.consumer.ok_cnt == merge.producer.ok_cnt ? "相等" : "不等!!!!!!!!!");
+ }
+}
+
+int main(int argc, char *argv[]) {
+ char *config;
+ char *ring_type;
+ uint32_t burst_cnt = 0;
+
+ if (argc == 4) {
+ config = argv[1];
+ ring_type = argv[2];
+ burst_cnt = strtoul(argv[3], NULL, 0);
+ if (burst_cnt <= 0) {
+ burst_cnt = 1;
+ }
+ } else {
+ config = "/root/code/c/bbq/perf/benchmark/config/compare/case1_simple_spsc.ini";
+ ring_type = "dpdk";
+ burst_cnt = 16;
+ UT_ERR_LOG("use default config, ringt_type:%s burst:%u config:%s argc:%d", ring_type, burst_cnt, config, argc);
+ }
+
+ char thread_name[128] = {0};
+ snprintf(thread_name, sizeof(thread_name), "main:%lu", pthread_self());
+ prctl(PR_SET_NAME, thread_name);
+
+ // 加载配置
+ struct ut_info_s info;
+ if (ut_load_config(config, ring_type, burst_cnt, &info.cfg) != 0) {
+ UT_ERR_LOG("load config error");
+ return -1;
+ }
+
+ // 队列初始化
+ int ret = -1;
+ struct ut_queue q;
+ ret = bcm_queue_init(&info.cfg, &q);
+ if (ret != 0) {
+ UT_ERR_LOG("init failed :%d", ret);
+ return ret;
+ }
+
+ // 创建线程
+ pthread_t *threads = ut_threads_create(&info, &q);
+ if (threads == NULL) {
+ UT_ERR_LOG("pthread_arr is NULL");
+ return ret;
+ }
+
+ // 等待所有线程完成,回收数据
+ uint32_t thread_cnt = info.cfg.ring.producer_cnt + info.cfg.ring.consumer_cnt;
+ struct ut_exit_data **exit_data = (struct ut_exit_data **)ut_malloc(UT_MODULE_BCM, sizeof(struct ut_exit_data **) * (thread_cnt));
+ uint32_t i = 0;
+
+ ut_wait_all_threads_exit(&info, thread_cnt, threads, exit_data);
+
+ // 生成benchmark报告
+ bcm_report_generate(&info.cfg, exit_data, thread_cnt);
+
+ // 回收、释放数据
+ for (i = 0; i < thread_cnt; i++) {
+ ut_exit_data_destory(exit_data[i]);
+ }
+ ut_free(UT_MODULE_BCM, exit_data);
+ ut_threads_destory(&info, threads);
+ ut_queue_destory(&q);
+ ut_memory_counter_print();
+
+ return 0;
+} \ No newline at end of file
diff --git a/perf/benchmark/bcm_loadconfig.c b/perf/benchmark/bcm_loadconfig.c
new file mode 100644
index 0000000..68f7bcc
--- /dev/null
+++ b/perf/benchmark/bcm_loadconfig.c
@@ -0,0 +1,91 @@
+/*
+ * @Author: liuyu
+ * @LastEditTime: 2024-07-07 22:13:33
+ * @Email: [email protected]
+ * @Describe: TODO
+ */
+#include "bbq.h"
+#include "iniparser.h"
+#include "ut_bbq_func.h"
+
+#include <string.h>
+
+int ut_load_config(const char *config, const char *ring_type, uint32_t burst_cnt, struct ut_cfg *cfg) {
+ int ret = 0;
+ // 加载配置
+ UT_INFO_LOG("load config:%s", config);
+ dictionary *ini = iniparser_load(config);
+ if (ini == NULL) {
+ return -1;
+ }
+
+ strncpy(cfg->base.name, config, sizeof(cfg->base.name) - 1);
+ cfg->base.name[sizeof(cfg->base.name) - 1] = '\0';
+
+ // 获取键值
+ const char *introduce = iniparser_getstring(ini, "base:introduce", "none");
+ strncpy(cfg->base.introduce, introduce, sizeof(cfg->base.introduce) - 1);
+ cfg->base.introduce[sizeof(cfg->base.introduce) - 1] = '\0'; // 手工写上 \0
+ const char *cores = iniparser_getstring(ini, "base:cores", "unknown");
+ ret = sscanf(cores, "%hu-%hu", &cfg->base.core_begin, &cfg->base.core_end);
+ if (ret != 2) {
+ UT_ERR_LOG("cores error: %s", cores);
+ return -1;
+ }
+
+ const char *workload = iniparser_getstring(ini, "ring:workload", "unknown");
+ cfg->ring.workload = ut_workload_str2enum(workload);
+ cfg->ring.entries_cnt = iniparser_getuint64(ini, "ring:entries_cnt", 0);
+ cfg->ring.producer_cnt = iniparser_getint(ini, "ring:producer_cnt", 0);
+ cfg->ring.consumer_cnt = iniparser_getint(ini, "ring:consumer_cnt", 0);
+ cfg->ring.block_count = iniparser_getint(ini, "ring:block_count", 0);
+ cfg->run.run_ok_times = iniparser_getint(ini, "run:run_ok_times", 0);
+ cfg->run.run_time = iniparser_getuint64(ini, "run:run_time", 0);
+
+ // 设置ring_type
+ cfg->ring.ring_type = ut_ring_type_str2enum(ring_type);
+ if (cfg->ring.ring_type >= UT_RING_TYPE_MAX) {
+ UT_ERR_LOG("unknown ring type:%d", cfg->ring.ring_type);
+ return -1;
+ }
+ cfg->ring.burst_cnt = burst_cnt;
+
+ if (cfg->ring.ring_type == UT_RING_TYPE_RMIND) {
+ // rmind仅支持1个消费者,仅支持burst方式
+ if (cfg->ring.consumer_cnt > 1) {
+ UT_ERR_LOG("ring type:%s only support single consumer", UT_RING_TYPE_RMIND_STR);
+ return -1;
+ }
+
+ if (cfg->ring.burst_cnt <= 1) {
+ UT_ERR_LOG("ring type:%s only support burst_cnt > 1 !", UT_RING_TYPE_RMIND_STR);
+ return -1;
+ }
+
+ if (cfg->ring.workload == UT_WORKLOAD_COMPLEX) {
+ UT_ERR_LOG("ring type:%s only support simple workload !", UT_RING_TYPE_RMIND_STR);
+ return -1;
+ }
+ }
+
+ if (cfg->run.run_time == 0 && cfg->run.run_ok_times == 0) {
+ UT_ERR_LOG("At least one of run_time or run_ok_times is not 0");
+ return -1;
+ }
+
+ UT_INFO_LOG("introduce:%s", cfg->base.introduce);
+ UT_INFO_LOG("cores:%u-%u", cfg->base.core_begin, cfg->base.core_end);
+ UT_INFO_LOG("workload:%s(%u)", workload, cfg->ring.workload);
+ UT_INFO_LOG("entries_cnt:%lu", cfg->ring.entries_cnt);
+ UT_INFO_LOG("producer_cnt:%u", cfg->ring.producer_cnt);
+ UT_INFO_LOG("consumer_cnt:%u", cfg->ring.consumer_cnt);
+ UT_INFO_LOG("block_count:%u", cfg->ring.block_count);
+ UT_INFO_LOG("run_ok_times:%lu", cfg->run.run_ok_times);
+ UT_INFO_LOG("run_time:%lu", cfg->run.run_time);
+ UT_INFO_LOG("ring_type:%s(%u)", ring_type, cfg->ring.ring_type);
+ UT_INFO_LOG("burst_cnt:%u", burst_cnt);
+
+ // 释放dictionary对象
+ iniparser_freedict(ini);
+ return 0;
+} \ No newline at end of file
diff --git a/perf/benchmark/bcm_queue.c b/perf/benchmark/bcm_queue.c
new file mode 100644
index 0000000..b116f25
--- /dev/null
+++ b/perf/benchmark/bcm_queue.c
@@ -0,0 +1,187 @@
+/*
+ * @Author: liuyu
+ * @LastEditTime: 2024-07-07 20:52:05
+ * @Email: [email protected]
+ * @Describe: TODO
+ */
+#include "bcm_queue.h"
+#include "ringbuf.h"
+
+static __rte_always_inline unsigned int
+bcm_dpdk_ring_enqueue_burst(struct rte_ring *r, void **obj_table, uint32_t n, uint16_t thread_idx, uint32_t *wait_consumed) {
+ UT_AVOID_WARNING(thread_idx);
+ int ret = 0;
+
+ if (wait_consumed) {
+ unsigned int free_space = 0;
+ ret = rte_ring_enqueue_burst(r, (void *const *)obj_table, n, &free_space);
+ *wait_consumed = r->size - free_space - 1;
+ } else {
+ ret = rte_ring_enqueue_burst(r, (void *const *)obj_table, n, NULL);
+ }
+
+ return ret;
+}
+
+int ut_queue_init_dpdk(struct ut_cfg *cfg, struct ut_queue *q) {
+ /* generate eal parameters */
+ const char *eal_args[] = {"bcm_dpdk", "-n", "4", "--proc-type", "auto", "--no-huge", "-m", "2048"};
+ if (rte_eal_init(RTE_DIM(eal_args), (char **)eal_args) < 0) {
+ return -1;
+ }
+
+ q->ring_type = UT_RING_TYPE_DPDK;
+ unsigned int flags = 0;
+ if (cfg->ring.producer_cnt <= 1) {
+ flags |= RING_F_SP_ENQ;
+ } else {
+ flags |= RING_F_MP_RTS_ENQ;
+ }
+
+ if (cfg->ring.consumer_cnt <= 1) {
+ flags |= RING_F_SC_DEQ;
+ } else {
+ flags |= RING_F_MC_RTS_DEQ;
+ }
+
+ q->ring = (void *)rte_ring_create("dpdk_ring", cfg->ring.entries_cnt, rte_socket_id(), flags);
+ if (q->ring == NULL) {
+ return BBQ_ERR_INPUT_NULL;
+ }
+
+ q->ring_free_f = (ut_ring_free_f)rte_ring_free;
+ q->enqueue_f = (ut_ring_enqueue_f)rte_ring_enqueue;
+ q->dequeue_f = (ut_ring_dequeue_f)rte_ring_dequeue;
+ q->enqueue_burst_f = (ut_enqueue_burst_f)bcm_dpdk_ring_enqueue_burst;
+ q->dequeue_burst_f = (ut_dequeue_burst_f)rte_ring_dequeue_burst;
+
+ return BBQ_OK;
+}
+
+unsigned char *rmind_buf;
+uint16_t worker_cnt;
+ringbuf_worker_t **rmind_workers;
+
+void ut_queue_free_rmind(void *ring) {
+ for (uint16_t i = 0; i < worker_cnt; i++) {
+ ringbuf_unregister((ringbuf_t *)ring, rmind_workers[i]);
+ }
+
+ ut_free(UT_MODULE_RMIND, rmind_workers);
+ ut_free(UT_MODULE_RMIND, rmind_buf);
+ ut_free(UT_MODULE_RMIND, ring);
+}
+
+uint32_t ut_enqueue_burst_rmind(void *ring, void **obj_table, uint32_t n, uint16_t thread_idx, uint32_t *wait_consumed) {
+ UT_AVOID_WARNING(wait_consumed);
+ uint32_t cnt = 0;
+ int ret = 0;
+ size_t off = 0;
+ void *obj = NULL;
+ ringbuf_worker_t *w = (ringbuf_worker_t *)rmind_workers[thread_idx];
+ size_t len = sizeof(uintptr_t);
+
+ for (cnt = 0; cnt < n; cnt++) {
+ obj = obj_table[cnt];
+ uintptr_t uptr = (uintptr_t)obj;
+
+ if ((ret = ringbuf_acquire(ring, w, len)) != -1) {
+ off = (size_t)ret;
+ memcpy(&rmind_buf[off], &uptr, len);
+ ringbuf_produce(ring, w);
+ } else {
+ break;
+ }
+ }
+
+ return cnt;
+}
+
+uint32_t ut_dequeue_burst_rmind(void *ring, void *obj_table, uint32_t n, uint32_t *wait_consumed) {
+ UT_AVOID_WARNING(n);
+ UT_AVOID_WARNING(wait_consumed);
+ size_t len = 0;
+ size_t off = 0;
+ size_t per_size = sizeof(void *);
+ void **table = (void **)obj_table;
+
+ if ((len = ringbuf_consume(ring, &off)) != 0) {
+ size_t rem = len;
+ size_t i = 0;
+
+ while (rem) {
+ uintptr_t *data = (uintptr_t *)(&rmind_buf[off]);
+ table[i] = (void *)(*data);
+ i++;
+ off += per_size;
+ rem -= sizeof(void *);
+ }
+ ringbuf_release(ring, len);
+ return i;
+ }
+
+ return 0;
+}
+
+int ut_queue_init_rmind(struct ut_cfg *cfg, struct ut_queue *q) {
+ static size_t ringbuf_obj_size;
+ worker_cnt = cfg->ring.producer_cnt + cfg->ring.consumer_cnt;
+
+ ringbuf_get_sizes(worker_cnt, &ringbuf_obj_size, NULL);
+ ringbuf_t *r = ut_malloc(UT_MODULE_RMIND, ringbuf_obj_size);
+ if (r == NULL) {
+ exit(-1);
+ }
+
+ size_t buf_size = sizeof(void *) * cfg->ring.entries_cnt;
+ rmind_buf = ut_malloc(UT_MODULE_RMIND, buf_size);
+ if (rmind_buf == NULL) {
+ exit(-1);
+ }
+ ringbuf_setup(r, worker_cnt, buf_size);
+
+ rmind_workers = ut_malloc(UT_MODULE_RMIND, sizeof(*rmind_workers) * worker_cnt);
+ if (rmind_workers == NULL) {
+ exit(-1);
+ }
+ for (uint32_t i = 0; i < worker_cnt; i++) {
+ rmind_workers[i] = ringbuf_register(r, i);
+ if (rmind_workers[i] == NULL) {
+ exit(-1);
+ }
+ }
+
+ q->ring = r;
+ q->ring_free_f = (ut_ring_free_f)ut_queue_free_rmind;
+ q->enqueue_f = NULL;
+ q->dequeue_f = NULL;
+ q->enqueue_burst_f = (ut_enqueue_burst_f)ut_enqueue_burst_rmind;
+ q->dequeue_burst_f = (ut_dequeue_burst_f)ut_dequeue_burst_rmind;
+
+ return 0;
+}
+
+int bcm_queue_init(struct ut_cfg *cfg, struct ut_queue *q) {
+ if (cfg == NULL || q == NULL) {
+ return BBQ_ERR_INPUT_NULL;
+ }
+
+ memset(q, 0, sizeof(*q));
+ int ret = -1;
+ q->ring_type = cfg->ring.ring_type;
+ switch (q->ring_type) {
+ case UT_RING_TYPE_DPDK:
+ ret = ut_queue_init_dpdk(cfg, q);
+ break;
+ case UT_RING_TYPE_BBQ:
+ ret = ut_queue_init_bbq(cfg, q);
+ break;
+ case UT_RING_TYPE_RMIND:
+ ret = ut_queue_init_rmind(cfg, q);
+ break;
+ default:
+ return BBQ_ERR;
+ }
+
+ return ret;
+}
diff --git a/perf/benchmark/bcm_queue.h b/perf/benchmark/bcm_queue.h
new file mode 100644
index 0000000..11006e4
--- /dev/null
+++ b/perf/benchmark/bcm_queue.h
@@ -0,0 +1,10 @@
+/*
+ * @Author: liuyu
+ * @LastEditTime: 2024-07-07 21:18:56
+ * @Email: [email protected]
+ * @Describe: TODO
+ */
+#include "rte_ring.h"
+#include "ut_bbq_func.h"
+
+extern int bcm_queue_init(struct ut_cfg *cfg, struct ut_queue *q); \ No newline at end of file
diff --git a/perf/benchmark/benchmark.sh b/perf/benchmark/benchmark.sh
new file mode 100755
index 0000000..abde645
--- /dev/null
+++ b/perf/benchmark/benchmark.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+###
+# @Author: liuyu
+ # @LastEditTime: 2024-06-30 21:43:01
+# @Describe: 运行性能测试的脚本
+###
+
+ring_type_arr=("bbq" "dpdk" "rmind")
+burst_arr=("32" "16" "8" "1")
+
+# 检查参数数量
+if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 <path to benchmark> <path to config file or directory> <ring type>"
+ exit 1
+fi
+
+# 获取参数值
+BENCHMARK_PATH=$1
+CONFIG_DIR=$2
+RING_TYPE=$3
+
+function exec_benchmark_ring_type() {
+ local ini="$1"
+ local ring="$2"
+ local log_file=$3
+ local burst=$4
+
+ # 如果以perf开头的配置文件,还要执行perf统计
+ if [[ $(basename "$ini") == perf* ]]; then
+ echo "skip perf*"
+ # echo perf stat -e L1-dcache-loads,L1-dcache-load-misses "$BENCHMARK_PATH" "$ini" "$ring" "$BURST_CNT"
+ # perf stat -e L1-dcache-loads,L1-dcache-load-misses "$BENCHMARK_PATH" "$ini" "$ring" "$BURST_CNT"
+ else
+ "$BENCHMARK_PATH" "$ini" "$ring" "$burst" 2>&1 | tee -a "$log_file"
+ fi
+}
+
+function exec_benchmark() {
+ # 提取配置文件名(不带路径)
+ local INI_FILE=$1
+ local log_file=$2
+ local burst=$3
+
+ # 执行benchmark命令并传递配置文件作为参数
+ echo "Executing benchmark with $INI_FILE"
+
+ # 使用所有ring_type
+ if [ "$RING_TYPE" == "all" ]; then
+ for ring_type_tmp in "${ring_type_arr[@]}"; do
+ echo start ring:"$ring_type_tmp"
+ exec_benchmark_ring_type "$INI_FILE" "$ring_type_tmp" "$log_file" "$burst"
+ done
+ else
+ exec_benchmark_ring_type "$INI_FILE" "$RING_TYPE" "$log_file" "$burst"
+ fi
+}
+
+# 检查benchmark文件是否存在且可执行
+if [ ! -x "$BENCHMARK_PATH" ]; then
+ echo "Error: Benchmark executable '$BENCHMARK_PATH' does not exist or is not executable."
+ exit 1
+fi
+
+# 检查配置文件目录是否存在
+if [ ! -e "$CONFIG_DIR" ]; then
+ echo "Error: Config directory '$CONFIG_DIR' does not exist."
+ exit 1
+fi
+
+# 创建报告目录
+timestamp=$(date +"%Y%m%d_%H%M%S")
+folder_path="/tmp/bbq/$timestamp"
+rm -rf "$folder_path"
+mkdir -p "$folder_path"
+
+# 开始时间
+start_time=$(date +%s)
+
+for burst in "${burst_arr[@]}"; do
+ burst_dir="$folder_path"/burst_"$burst"
+ mkdir -p "$burst_dir"
+ for i in {1..3}; do
+ echo ======"$i"========
+ report_path="$burst_dir"/report_"$i".txt
+ if [[ -f "$CONFIG_DIR" ]]; then
+ # 如果是文件,直接执行
+ exec_benchmark "$CONFIG_DIR" "$report_path" "$burst"
+ else
+ # 使用 find 命令递归地搜索所有的 .ini 文件,并按文件名排序
+ find "$CONFIG_DIR" -type f -name "*.ini" -print0 | sort -z | while IFS= read -r -d '' INI_FILE; do
+ if [ -f "$INI_FILE" ]; then
+ exec_benchmark "$INI_FILE" "$report_path" "$burst"
+ fi
+ done
+ fi
+
+ sleep 1
+ done
+done
+
+# 结束时间
+end_time=$(date +%s)
+# 计算时间差(秒)
+runtime=$((end_time - start_time))
+# 输出运行时间
+echo "done, use $runtime second"
diff --git a/perf/benchmark/config/bbq_debug/debug.ini b/perf/benchmark/config/bbq_debug/debug.ini
new file mode 100644
index 0000000..cead690
--- /dev/null
+++ b/perf/benchmark/config/bbq_debug/debug.ini
@@ -0,0 +1,15 @@
+[base]
+ introduce = "bbq简单负载下,多生产者、单消费者 block=0" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 8 ;ring初始化时分配entry的个数
+ block_count = 2 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 4 ;生产者个数
+ consumer_cnt = 1 ;消费者个数
+
+[run]
+ run_time = 10 ; 整体运行的秒数,大于0生效
+ enqueue_cnt = 0 ;每个线程入队次数,大于0生效
+ dequeue_cnt = 0 ;每个线程出队次数,大于0生效 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case1_simple_spsc.ini b/perf/benchmark/config/compare/case1_simple_spsc.ini
new file mode 100644
index 0000000..e1ee087
--- /dev/null
+++ b/perf/benchmark/config/compare/case1_simple_spsc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case1 简单负载下,单生产者、单消费者 吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 1 ;生产者个数
+ consumer_cnt = 1 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case2_simple_spmc.ini b/perf/benchmark/config/compare/case2_simple_spmc.ini
new file mode 100644
index 0000000..8216381
--- /dev/null
+++ b/perf/benchmark/config/compare/case2_simple_spmc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case2 简单负载下,单生产者、多消费者 吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 1 ;生产者个数
+ consumer_cnt = 4 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。
diff --git a/perf/benchmark/config/compare/case3_simple_mpsc.ini b/perf/benchmark/config/compare/case3_simple_mpsc.ini
new file mode 100644
index 0000000..3476774
--- /dev/null
+++ b/perf/benchmark/config/compare/case3_simple_mpsc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case3 简单负载下,多生产者、单消费者 吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 4 ;生产者个数
+ consumer_cnt = 1 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case4_complex_spmc.ini b/perf/benchmark/config/compare/case4_complex_spmc.ini
new file mode 100644
index 0000000..64e3e41
--- /dev/null
+++ b/perf/benchmark/config/compare/case4_complex_spmc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case4 复杂负载下,单生产者、多消费者 吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "complex" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 1 ;生产者个数
+ consumer_cnt = 4 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case5_complex_mpsc.ini b/perf/benchmark/config/compare/case5_complex_mpsc.ini
new file mode 100644
index 0000000..2404649
--- /dev/null
+++ b/perf/benchmark/config/compare/case5_complex_mpsc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case5 复杂负载下,多生产者、单消费者 吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "complex" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 4 ;生产者个数
+ consumer_cnt = 1 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case6_simple_mp0c.ini b/perf/benchmark/config/compare/case6_simple_mp0c.ini
new file mode 100644
index 0000000..62325e4
--- /dev/null
+++ b/perf/benchmark/config/compare/case6_simple_mp0c.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case6 简单负载下,多生产者、无消费者,测试满队入队操作时延" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 4 ;生产者个数
+ consumer_cnt = 0 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case7_simple_0pmc.ini b/perf/benchmark/config/compare/case7_simple_0pmc.ini
new file mode 100644
index 0000000..1c5ed2c
--- /dev/null
+++ b/perf/benchmark/config/compare/case7_simple_0pmc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case7 简单负载下,无生产者、多消费者,测试空队出队操作时延" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 0 ;生产者个数
+ consumer_cnt = 4 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case8_simple_mpmc.ini b/perf/benchmark/config/compare/case8_simple_mpmc.ini
new file mode 100644
index 0000000..b5026bb
--- /dev/null
+++ b/perf/benchmark/config/compare/case8_simple_mpmc.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case8 简单负载下,多生产者、多消费者 吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 4 ;生产者个数
+ consumer_cnt = 4 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/benchmark/config/compare/case9_simple_mpmc_overcore.ini b/perf/benchmark/config/compare/case9_simple_mpmc_overcore.ini
new file mode 100644
index 0000000..b4b304a
--- /dev/null
+++ b/perf/benchmark/config/compare/case9_simple_mpmc_overcore.ini
@@ -0,0 +1,14 @@
+[base]
+ introduce = "general case9 简单负载下,多生产者、多消费者 线程核心超过已有核心时的吞吐测试" ;测试配置说明
+ cores = "20-27" ;测试用核心起止范围
+
+[ring]
+ workload = "simple" ;负载模式 simple/complex
+ entries_cnt = 4096 ;ring初始化时分配entry的个数
+ block_count = 0 ;bbq配置,等于0则表示根据entries_cnt自动计算
+ producer_cnt = 16 ;生产者个数
+ consumer_cnt = 16 ;消费者个数
+
+[run]
+ run_time = 15 ; 整体运行的秒数,大于0生效
+ run_ok_times = 0 ;成功入队/出队次数,大于0生效。 \ No newline at end of file
diff --git a/perf/thirdparty/CMakeLists.txt b/perf/thirdparty/CMakeLists.txt
new file mode 100644
index 0000000..5ad8bd4
--- /dev/null
+++ b/perf/thirdparty/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.0)
+project(BBQ_THIRDPARTY)
+
+add_subdirectory(iniparser)
+add_subdirectory(rmind_ringbuf)
diff --git a/perf/thirdparty/iniparser/CMakeLists.txt b/perf/thirdparty/iniparser/CMakeLists.txt
new file mode 100644
index 0000000..f24e1e8
--- /dev/null
+++ b/perf/thirdparty/iniparser/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.0)
+project(INIPARSER)
+
+file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.c) #搜索当前cmake所在目录下的c文件
+set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) #设置库生成目录
+
+add_library(iniparser STATIC ${SRC_LIST}) #生成静态库
+# add_library(${BBQ_LIB} SHARED ${SRC_LIST}) #生成动态库 \ No newline at end of file
diff --git a/perf/thirdparty/iniparser/dictionary.c b/perf/thirdparty/iniparser/dictionary.c
new file mode 100644
index 0000000..85dfdc0
--- /dev/null
+++ b/perf/thirdparty/iniparser/dictionary.c
@@ -0,0 +1,383 @@
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.c
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ 128
+
+/*---------------------------------------------------------------------------
+ Private functions
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+ char * t ;
+ size_t len ;
+ if (!s)
+ return NULL ;
+
+ len = strlen(s) + 1 ;
+ t = (char*) malloc(len) ;
+ if (t) {
+ memcpy(t, s, len) ;
+ }
+ return t ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Double the size of the dictionary
+ @param d Dictionary to grow
+ @return This function returns non-zero in case of failure
+ */
+/*--------------------------------------------------------------------------*/
+static int dictionary_grow(dictionary * d)
+{
+ char ** new_val ;
+ char ** new_key ;
+ unsigned * new_hash ;
+
+ new_val = (char**) calloc(d->size * 2, sizeof *d->val);
+ new_key = (char**) calloc(d->size * 2, sizeof *d->key);
+ new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash);
+ if (!new_val || !new_key || !new_hash) {
+ /* An allocation failed, leave the dictionary unchanged */
+ if (new_val)
+ free(new_val);
+ if (new_key)
+ free(new_key);
+ if (new_hash)
+ free(new_hash);
+ return -1 ;
+ }
+ /* Initialize the newly allocated space */
+ memcpy(new_val, d->val, d->size * sizeof(char *));
+ memcpy(new_key, d->key, d->size * sizeof(char *));
+ memcpy(new_hash, d->hash, d->size * sizeof(unsigned));
+ /* Delete previous data */
+ free(d->val);
+ free(d->key);
+ free(d->hash);
+ /* Actually update the dictionary */
+ d->size *= 2 ;
+ d->val = new_val;
+ d->key = new_key;
+ d->hash = new_hash;
+ return 0 ;
+}
+
+/*---------------------------------------------------------------------------
+ Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+ size_t len ;
+ unsigned hash ;
+ size_t i ;
+
+ if (!key)
+ return 0 ;
+
+ len = strlen(key);
+ for (hash=0, i=0 ; i<len ; i++) {
+ hash += (unsigned)key[i] ;
+ hash += (hash<<10);
+ hash ^= (hash>>6) ;
+ }
+ hash += (hash <<3);
+ hash ^= (hash >>11);
+ hash += (hash <<15);
+ return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary object.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*-------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size)
+{
+ dictionary * d ;
+
+ /* If no size was specified, allocate space for DICTMINSZ */
+ if (size<DICTMINSZ) size=DICTMINSZ ;
+
+ d = (dictionary*) calloc(1, sizeof *d) ;
+
+ if (d) {
+ d->size = size ;
+ d->val = (char**) calloc(size, sizeof *d->val);
+ d->key = (char**) calloc(size, sizeof *d->key);
+ d->hash = (unsigned*) calloc(size, sizeof *d->hash);
+ if (!d->size || !d->val || !d->hash) {
+ free((void *) d->size);
+ free((void *) d->val);
+ free((void *) d->hash);
+ free(d);
+ d = NULL;
+ }
+ }
+ return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+ size_t i ;
+
+ if (d==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]!=NULL)
+ free(d->key[i]);
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ }
+ free(d->val);
+ free(d->key);
+ free(d->hash);
+ free(d);
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * dictionary_get(const dictionary * d, const char * key, const char * def)
+{
+ unsigned hash ;
+ size_t i ;
+
+ if(d == NULL || key == NULL)
+ return def ;
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ return d->val[i] ;
+ }
+ }
+ }
+ return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+ size_t i ;
+ unsigned hash ;
+
+ if (d==NULL || key==NULL) return -1 ;
+
+ /* Compute hash for this key */
+ hash = dictionary_hash(key) ;
+ /* Find if value is already in dictionary */
+ if (d->n>0) {
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (hash==d->hash[i]) { /* Same hash value */
+ if (!strcmp(key, d->key[i])) { /* Same key */
+ /* Found a value: modify and return */
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ d->val[i] = (val ? xstrdup(val) : NULL);
+ /* Value has been modified: return */
+ return 0 ;
+ }
+ }
+ }
+ }
+ /* Add a new value */
+ /* See if dictionary needs to grow */
+ if (d->n==d->size) {
+ /* Reached maximum size: reallocate dictionary */
+ if (dictionary_grow(d) != 0)
+ return -1;
+ }
+
+ /* Insert key in the first empty slot. Start at d->n and wrap at
+ d->size. Because d->n < d->size this will necessarily
+ terminate. */
+ for (i=d->n ; d->key[i] ; ) {
+ if(++i == d->size) i = 0;
+ }
+ /* Copy key */
+ d->key[i] = xstrdup(key);
+ d->val[i] = (val ? xstrdup(val) : NULL) ;
+ d->hash[i] = hash;
+ d->n ++ ;
+ return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+ unsigned hash ;
+ size_t i ;
+
+ if (key == NULL || d == NULL) {
+ return;
+ }
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ /* Found key */
+ break ;
+ }
+ }
+ }
+ if (i>=d->size)
+ /* Key not found */
+ return ;
+
+ free(d->key[i]);
+ d->key[i] = NULL ;
+ if (d->val[i]!=NULL) {
+ free(d->val[i]);
+ d->val[i] = NULL ;
+ }
+ d->hash[i] = 0 ;
+ d->n -- ;
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(const dictionary * d, FILE * out)
+{
+ size_t i ;
+
+ if (d==NULL || out==NULL) return ;
+ if (d->n<1) {
+ fprintf(out, "empty dictionary\n");
+ return ;
+ }
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]) {
+ fprintf(out, "%20s\t[%s]\n",
+ d->key[i],
+ d->val[i] ? d->val[i] : "UNDEF");
+ }
+ }
+ return ;
+}
diff --git a/perf/thirdparty/iniparser/dictionary.h b/perf/thirdparty/iniparser/dictionary.h
new file mode 100644
index 0000000..f459cfe
--- /dev/null
+++ b/perf/thirdparty/iniparser/dictionary.h
@@ -0,0 +1,170 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.h
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*---------------------------------------------------------------------------
+ New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dictionary object
+
+ This object contains a list of string/string associations. Each
+ association is identified by a unique string key. Looking up values
+ in the dictionary is speeded up by the use of a (hopefully collision-free)
+ hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+ unsigned n ; /** Number of entries in dictionary */
+ size_t size ; /** Storage size */
+ char ** val ; /** List of string values */
+ char ** key ; /** List of string keys */
+ unsigned * hash ; /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+ Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary object.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * dictionary_get(const dictionary * d, const char * key, const char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(const dictionary * d, FILE * out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/perf/thirdparty/iniparser/iniparser.c b/perf/thirdparty/iniparser/iniparser.c
new file mode 100644
index 0000000..4cffb96
--- /dev/null
+++ b/perf/thirdparty/iniparser/iniparser.c
@@ -0,0 +1,949 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.c
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ (1024)
+#define INI_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+ LINE_UNPROCESSED,
+ LINE_ERROR,
+ LINE_EMPTY,
+ LINE_COMMENT,
+ LINE_SECTION,
+ LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Convert a string to lowercase.
+ @param in String to convert.
+ @param out Output buffer.
+ @param len Size of the out buffer.
+ @return ptr to the out buffer or NULL if an error occured.
+
+ This function convert a string into lowercase.
+ At most len - 1 elements of the input string will be converted.
+ */
+/*--------------------------------------------------------------------------*/
+static const char * strlwc(const char * in, char *out, unsigned len)
+{
+ unsigned i ;
+
+ if (in==NULL || out == NULL || len==0) return NULL ;
+ i=0 ;
+ while (in[i] != '\0' && i < len-1) {
+ out[i] = (char)tolower((int)in[i]);
+ i++ ;
+ }
+ out[i] = '\0';
+ return out ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+ char * t ;
+ size_t len ;
+ if (!s)
+ return NULL ;
+
+ len = strlen(s) + 1 ;
+ t = (char*) malloc(len) ;
+ if (t) {
+ memcpy(t, s, len) ;
+ }
+ return t ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Remove blanks at the beginning and the end of a string.
+ @param str String to parse and alter.
+ @return unsigned New size of the string.
+ */
+/*--------------------------------------------------------------------------*/
+static unsigned strstrip(char * s)
+{
+ char *last = NULL ;
+ char *dest = s;
+
+ if (s==NULL) return 0;
+
+ last = s + strlen(s);
+ while (isspace((int)*s) && *s) s++;
+ while (last > s) {
+ if (!isspace((int)*(last-1)))
+ break ;
+ last -- ;
+ }
+ *last = (char)0;
+
+ memmove(dest,s,last - s + 1);
+ return last - s;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
+ */
+/*--------------------------------------------------------------------------*/
+static int default_error_callback(const char *format, ...)
+{
+ int ret;
+ va_list argptr;
+ va_start(argptr, format);
+ ret = vfprintf(stderr, format, argptr);
+ va_end(argptr);
+ return ret;
+}
+
+static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Configure a function to receive the error messages.
+ @param errback Function to call.
+
+ By default, the error will be printed on stderr. If a null pointer is passed
+ as errback the error callback will be switched back to default.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_set_error_callback(int (*errback)(const char *, ...))
+{
+ if (errback) {
+ iniparser_error_callback = errback;
+ } else {
+ iniparser_error_callback = default_error_callback;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(const dictionary * d)
+{
+ size_t i ;
+ int nsec ;
+
+ if (d==NULL) return -1 ;
+ nsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ nsec ++ ;
+ }
+ }
+ return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+const char * iniparser_getsecname(const dictionary * d, int n)
+{
+ size_t i ;
+ int foundsec ;
+
+ if (d==NULL || n<0) return NULL ;
+ foundsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ foundsec++ ;
+ if (foundsec>n)
+ break ;
+ }
+ }
+ if (foundsec<=n) {
+ return NULL ;
+ }
+ return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(const dictionary * d, FILE * f)
+{
+ size_t i ;
+
+ if (d==NULL || f==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (d->val[i]!=NULL) {
+ fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+ } else {
+ fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+ }
+ }
+ return ;
+}
+
+static void escape_value(char *escaped, char *value) {
+ char c;
+ int v = 0;
+ int e = 0;
+
+ if(!escaped || !value)
+ return;
+
+ while((c = value[v]) != '\0') {
+ if(c == '\\' || c == '"') {
+ escaped[e] = '\\';
+ e++;
+ }
+ escaped[e] = c;
+ v++;
+ e++;
+ }
+ escaped[e] = '\0';
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(const dictionary * d, FILE * f)
+{
+ size_t i ;
+ size_t nsec ;
+ const char * secname ;
+ char escaped[ASCIILINESZ+1] = "";
+
+ if (d==NULL || f==NULL) return ;
+
+ nsec = iniparser_getnsec(d);
+ if (nsec<1) {
+ /* No section in file: dump all keys as they are */
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ escape_value(escaped, d->val[i]);
+ fprintf(f, "%s = \"%s\"\n", d->key[i], escaped);
+ }
+ return ;
+ }
+ for (i=0 ; i<nsec ; i++) {
+ secname = iniparser_getsecname(d, i) ;
+ iniparser_dumpsection_ini(d, secname, f);
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
+{
+ size_t j ;
+ char keym[ASCIILINESZ+1];
+ int seclen ;
+ char escaped[ASCIILINESZ+1] = "";
+
+ if (d==NULL || f==NULL) return ;
+ if (! iniparser_find_entry(d, s)) return ;
+
+ seclen = (int)strlen(s);
+ fprintf(f, "\n[%s]\n", s);
+ sprintf(keym, "%s:", s);
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ escape_value(escaped, d->val[j]);
+ fprintf(f, "%-30s = \"%s\"\n", d->key[j]+seclen+1, escaped);
+ }
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(const dictionary * d, const char * s)
+{
+ int seclen, nkeys ;
+ char keym[ASCIILINESZ+1];
+ size_t j ;
+
+ nkeys = 0;
+
+ if (d==NULL) return nkeys;
+ if (! iniparser_find_entry(d, s)) return nkeys;
+
+ seclen = (int)strlen(s);
+ strlwc(s, keym, sizeof(keym));
+ keym[seclen] = ':';
+
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1))
+ nkeys++;
+ }
+
+ return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @param keys Already allocated array to store the keys in
+ @return The pointer passed as `keys` argument or NULL in case of error
+
+ This function queries a dictionary and finds all keys in a given section.
+ The keys argument should be an array of pointers which size has been
+ determined by calling `iniparser_getsecnkeys` function prior to this one.
+
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+ */
+/*--------------------------------------------------------------------------*/
+const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
+{
+ size_t i, j, seclen ;
+ char keym[ASCIILINESZ+1];
+
+ if (d==NULL || keys==NULL) return NULL;
+ if (! iniparser_find_entry(d, s)) return NULL;
+
+ seclen = strlen(s);
+ strlwc(s, keym, sizeof(keym));
+ keym[seclen] = ':';
+
+ i = 0;
+
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ keys[i] = d->key[j];
+ i++;
+ }
+ }
+
+ return keys;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
+{
+ const char * lc_key ;
+ const char * sval ;
+ char tmp_str[ASCIILINESZ+1];
+
+ if (d==NULL || key==NULL)
+ return def ;
+
+ lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
+ sval = dictionary_get(d, lc_key, def);
+ return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an long int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return long integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ "42" -> 42
+ "042" -> 34 (octal -> decimal)
+ "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
+{
+ const char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==NULL || str==INI_INVALID_KEY) return notfound ;
+ return strtol(str, NULL, 0);
+}
+
+int64_t iniparser_getint64(const dictionary * d, const char * key, int64_t notfound)
+{
+ const char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==NULL || str==INI_INVALID_KEY) return notfound ;
+ return strtoimax(str, NULL, 0);
+}
+
+uint64_t iniparser_getuint64(const dictionary * d, const char * key, uint64_t notfound)
+{
+ const char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==NULL || str==INI_INVALID_KEY) return notfound ;
+ return strtoumax(str, NULL, 0);
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ "42" -> 42
+ "042" -> 34 (octal -> decimal)
+ "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(const dictionary * d, const char * key, int notfound)
+{
+ return (int)iniparser_getlongint(d, key, notfound);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
+{
+ const char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==NULL || str==INI_INVALID_KEY) return notfound ;
+ return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
+{
+ int ret ;
+ const char * c ;
+
+ c = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (c==NULL || c==INI_INVALID_KEY) return notfound ;
+ if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+ ret = 1 ;
+ } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+ ret = 0 ;
+ } else {
+ ret = notfound ;
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(const dictionary * ini, const char * entry)
+{
+ int found=0 ;
+ if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+ found = 1 ;
+ }
+ return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, the entry is created.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+ char tmp_str[ASCIILINESZ+1];
+ return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+ char tmp_str[ASCIILINESZ+1];
+ dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
+}
+
+static void parse_quoted_value(char *value, char quote) {
+ char c;
+ char *quoted;
+ int q = 0, v = 0;
+ int esc = 0;
+
+ if(!value)
+ return;
+
+ quoted = xstrdup(value);
+
+ if(!quoted) {
+ iniparser_error_callback("iniparser: memory allocation failure\n");
+ goto end_of_value;
+ }
+
+ while((c = quoted[q]) != '\0') {
+ if(!esc) {
+ if(c == '\\') {
+ esc = 1;
+ q++;
+ continue;
+ }
+
+ if(c == quote) {
+ goto end_of_value;
+ }
+ }
+ esc = 0;
+ value[v] = c;
+ v++;
+ q++;
+ }
+end_of_value:
+ value[v] = '\0';
+ free(quoted);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Load a single line from an INI file
+ @param input_line Input line, may be concatenated multi-line input
+ @param section Output space to store section
+ @param key Output space to store key
+ @param value Output space to store value
+ @return line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+ const char * input_line,
+ char * section,
+ char * key,
+ char * value)
+{
+ line_status sta ;
+ char * line = NULL;
+ size_t len ;
+ int d_quote;
+
+ line = xstrdup(input_line);
+ len = strstrip(line);
+
+ sta = LINE_UNPROCESSED ;
+ if (len<1) {
+ /* Empty line */
+ sta = LINE_EMPTY ;
+ } else if (line[0]=='#' || line[0]==';') {
+ /* Comment line */
+ sta = LINE_COMMENT ;
+ } else if (line[0]=='[' && line[len-1]==']') {
+ /* Section name without opening square bracket */
+ sscanf(line, "[%[^\n]", section);
+ len = strlen(section);
+ /* Section name without closing square bracket */
+ if(section[len-1] == ']')
+ {
+ section[len-1] = '\0';
+ }
+ strstrip(section);
+ strlwc(section, section, len);
+ sta = LINE_SECTION ;
+ } else if ((d_quote = sscanf (line, "%[^=] = \"%[^\n]\"", key, value)) == 2
+ || sscanf (line, "%[^=] = '%[^\n]'", key, value) == 2) {
+ /* Usual key=value with quotes, with or without comments */
+ strstrip(key);
+ strlwc(key, key, len);
+ if(d_quote == 2)
+ parse_quoted_value(value, '"');
+ else
+ parse_quoted_value(value, '\'');
+ /* Don't strip spaces from values surrounded with quotes */
+ sta = LINE_VALUE ;
+ } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
+ /* Usual key=value without quotes, with or without comments */
+ strstrip(key);
+ strlwc(key, key, len);
+ strstrip(value);
+ /*
+ * sscanf cannot handle '' or "" as empty values
+ * this is done here
+ */
+ if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+ value[0]=0 ;
+ }
+ sta = LINE_VALUE ;
+ } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+ || sscanf(line, "%[^=] %[=]", key, value) == 2) {
+ /*
+ * Special cases:
+ * key=
+ * key=;
+ * key=#
+ */
+ strstrip(key);
+ strlwc(key, key, len);
+ value[0]=0 ;
+ sta = LINE_VALUE ;
+ } else {
+ /* Generate syntax error */
+ sta = LINE_ERROR ;
+ }
+
+ free(line);
+ return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param in File to read.
+ @param ininame Name of the ini file to read (only used for nicer error messages)
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the file to be read. It returns a dictionary object that should not
+ be accessed directly, but through accessor functions instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load_file(FILE * in, const char * ininame)
+{
+ char line [ASCIILINESZ+1] ;
+ char section [ASCIILINESZ+1] ;
+ char key [ASCIILINESZ+1] ;
+ char tmp [(ASCIILINESZ * 2) + 2] ;
+ char val [ASCIILINESZ+1] ;
+
+ int last=0 ;
+ int len ;
+ int lineno=0 ;
+ int errs=0;
+ int mem_err=0;
+
+ dictionary * dict ;
+
+ dict = dictionary_new(0) ;
+ if (!dict) {
+ return NULL ;
+ }
+
+ memset(line, 0, ASCIILINESZ);
+ memset(section, 0, ASCIILINESZ);
+ memset(key, 0, ASCIILINESZ);
+ memset(val, 0, ASCIILINESZ);
+ last=0 ;
+
+ while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+ lineno++ ;
+ len = (int)strlen(line)-1;
+ if (len<=0)
+ continue;
+ /* Safety check against buffer overflows */
+ if (line[len]!='\n' && !feof(in)) {
+ iniparser_error_callback(
+ "iniparser: input line too long in %s (%d)\n",
+ ininame,
+ lineno);
+ dictionary_del(dict);
+ return NULL ;
+ }
+ /* Get rid of \n and spaces at end of line */
+ while ((len>=0) &&
+ ((line[len]=='\n') || (isspace(line[len])))) {
+ line[len]=0 ;
+ len-- ;
+ }
+ if (len < 0) { /* Line was entirely \n and/or spaces */
+ len = 0;
+ }
+ /* Detect multi-line */
+ if (line[len]=='\\') {
+ /* Multi-line value */
+ last=len ;
+ continue ;
+ } else {
+ last=0 ;
+ }
+ switch (iniparser_line(line, section, key, val)) {
+ case LINE_EMPTY:
+ case LINE_COMMENT:
+ break ;
+
+ case LINE_SECTION:
+ mem_err = dictionary_set(dict, section, NULL);
+ break ;
+
+ case LINE_VALUE:
+ sprintf(tmp, "%s:%s", section, key);
+ mem_err = dictionary_set(dict, tmp, val);
+ break ;
+
+ case LINE_ERROR:
+ iniparser_error_callback(
+ "iniparser: syntax error in %s (%d):\n-> %s\n",
+ ininame,
+ lineno,
+ line);
+ errs++ ;
+ break;
+
+ default:
+ break ;
+ }
+ memset(line, 0, ASCIILINESZ);
+ last=0;
+ if (mem_err<0) {
+ iniparser_error_callback("iniparser: memory allocation failure\n");
+ break ;
+ }
+ }
+ if (errs) {
+ dictionary_del(dict);
+ dict = NULL ;
+ }
+ return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+ FILE * in ;
+ dictionary * dict ;
+
+ if ((in=fopen(ininame, "r"))==NULL) {
+ iniparser_error_callback("iniparser: cannot open %s\n", ininame);
+ return NULL ;
+ }
+
+ dict = iniparser_load_file(in, ininame);
+ fclose(in);
+
+ return dict ;
+}
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+ dictionary_del(d);
+}
diff --git a/perf/thirdparty/iniparser/iniparser.h b/perf/thirdparty/iniparser/iniparser.h
new file mode 100644
index 0000000..d8026a8
--- /dev/null
+++ b/perf/thirdparty/iniparser/iniparser.h
@@ -0,0 +1,446 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.h
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include "dictionary.h"
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Configure a function to receive the error messages.
+ @param errback Function to call.
+
+ By default, the error will be printed on stderr. If a null pointer is passed
+ as errback the error callback will be switched back to default.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_set_error_callback(int (*errback)(const char *, ...));
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(const dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+const char * iniparser_getsecname(const dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+
+ All values are quoted, these charecters are escaped:
+
+ - ' : the quote character (e.g. "String with \"Quotes\"")
+ - \ : the backslash character (e.g. "C:\\tmp")
+
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(const dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(const dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(const dictionary * d, const char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @param keys Already allocated array to store the keys in
+ @return The pointer passed as `keys` argument or NULL in case of error
+
+ This function queries a dictionary and finds all keys in a given section.
+ The keys argument should be an array of pointers which size has been
+ determined by calling `iniparser_getsecnkeys` function prior to this one.
+
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+ */
+/*--------------------------------------------------------------------------*/
+const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * iniparser_getstring(const dictionary * d, const char * key, const char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(const dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an long int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+ */
+/*--------------------------------------------------------------------------*/
+long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int64_t
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtoimax(), see the associated man page for overflow
+ handling.
+
+ This function is usefull on 32bit architectures where `long int` is only
+ 32bit.
+ */
+/*--------------------------------------------------------------------------*/
+int64_t iniparser_getint64(const dictionary * d, const char * key, int64_t notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an uint64_t
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtoumax(), see the associated man page for overflow
+ handling.
+
+ This function is usefull on 32bit architectures where `long int` is only
+ 32bit.
+ */
+/*--------------------------------------------------------------------------*/
+uint64_t iniparser_getuint64(const dictionary * d, const char * key, uint64_t notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(const dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(const dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, the entry is created.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(const dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ Iff the value is a quoted string it supports some escape sequences:
+
+ - \" or ' : the quote character
+ (e.g. 'String with "Quotes"' or "String with 'Quotes'")
+ - \ : the backslash character (e.g. "C:\tmp")
+
+ Escape sequences always start with a backslash. Additional escape sequences
+ might be added in the future. Backslash characters must be escaped. Any other
+ sequence then those outlined above is invalid and may lead to unpredictable
+ results.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param in File to read.
+ @param ininame Name of the ini file to read (only used for nicer error messages)
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the file to be read. It returns a dictionary object that should not
+ be accessed directly, but through accessor functions instead.
+
+ Iff the value is a quoted string it supports some escape sequences:
+
+ - \" or ' : the quote character
+ (e.g. 'String with "Quotes"' or "String with 'Quotes'")
+ - \ : the backslash character (e.g. "C:\tmp")
+
+ Escape sequences always start with a backslash. Additional escape sequences
+ might be added in the future. Backslash characters must be escaped. Any other
+ sequence then those outlined above is invalid and may lead to unpredictable
+ results.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load_file(FILE * in, const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/perf/thirdparty/rmind_ringbuf/CMakeLists.txt b/perf/thirdparty/rmind_ringbuf/CMakeLists.txt
new file mode 100644
index 0000000..7b65eaa
--- /dev/null
+++ b/perf/thirdparty/rmind_ringbuf/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.0)
+project(RMIND_RINGBUF)
+
+file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.c) #搜索当前cmake所在目录下的c文件
+set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) #设置库生成目录
+
+add_library(rmind_ringbuf STATIC ${SRC_LIST}) #生成静态库 \ No newline at end of file
diff --git a/perf/thirdparty/rmind_ringbuf/ringbuf.c b/perf/thirdparty/rmind_ringbuf/ringbuf.c
new file mode 100644
index 0000000..75cfee9
--- /dev/null
+++ b/perf/thirdparty/rmind_ringbuf/ringbuf.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2016-2017 Mindaugas Rasiukevicius <rmind at noxt eu>
+ * All rights reserved.
+ *
+ * Use is subject to license terms, as specified in the LICENSE file.
+ */
+
+/*
+ * Atomic multi-producer single-consumer ring buffer, which supports
+ * contiguous range operations and which can be conveniently used for
+ * message passing.
+ *
+ * There are three offsets -- think of clock hands:
+ * - NEXT: marks the beginning of the available space,
+ * - WRITTEN: the point up to which the data is actually written.
+ * - Observed READY: point up to which data is ready to be written.
+ *
+ * Producers
+ *
+ * Observe and save the 'next' offset, then request N bytes from
+ * the ring buffer by atomically advancing the 'next' offset. Once
+ * the data is written into the "reserved" buffer space, the thread
+ * clears the saved value; these observed values are used to compute
+ * the 'ready' offset.
+ *
+ * Consumer
+ *
+ * Writes the data between 'written' and 'ready' offsets and updates
+ * the 'written' value. The consumer thread scans for the lowest
+ * seen value by the producers.
+ *
+ * Key invariant
+ *
+ * Producers cannot go beyond the 'written' offset; producers are
+ * also not allowed to catch up with the consumer. Only the consumer
+ * is allowed to catch up with the producer i.e. set the 'written'
+ * offset to be equal to the 'next' offset.
+ *
+ * Wrap-around
+ *
+ * If the producer cannot acquire the requested length due to little
+ * available space at the end of the buffer, then it will wraparound.
+ * WRAP_LOCK_BIT in 'next' offset is used to lock the 'end' offset.
+ *
+ * There is an ABA problem if one producer stalls while a pair of
+ * producer and consumer would both successfully wrap-around and set
+ * the 'next' offset to the stale value of the first producer, thus
+ * letting it to perform a successful CAS violating the invariant.
+ * A counter in the 'next' offset (masked by WRAP_COUNTER) is used
+ * to prevent from this problem. It is incremented on wraparounds.
+ *
+ * The same ABA problem could also cause a stale 'ready' offset,
+ * which could be observed by the consumer. We set WRAP_LOCK_BIT in
+ * the 'seen' value before advancing the 'next' and clear this bit
+ * after the successful advancing; this ensures that only the stable
+ * 'ready' is observed by the consumer.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "ringbuf.h"
+#include "utils.h"
+
+#define RBUF_OFF_MASK (0x00000000ffffffffUL)
+#define WRAP_LOCK_BIT (0x8000000000000000UL)
+#define RBUF_OFF_MAX (UINT64_MAX & ~WRAP_LOCK_BIT)
+
+#define WRAP_COUNTER (0x7fffffff00000000UL)
+#define WRAP_INCR(x) (((x) + 0x100000000UL) & WRAP_COUNTER)
+
+typedef uint64_t ringbuf_off_t;
+
+struct ringbuf_worker {
+ volatile ringbuf_off_t seen_off;
+ int registered;
+};
+
+struct ringbuf {
+ /* Ring buffer space. */
+ size_t space;
+
+ /*
+ * The NEXT hand is atomically updated by the producer.
+ * WRAP_LOCK_BIT is set in case of wrap-around; in such case,
+ * the producer can update the 'end' offset.
+ */
+ volatile ringbuf_off_t next;
+ ringbuf_off_t end;
+
+ /* The following are updated by the consumer. */
+ ringbuf_off_t written;
+ unsigned nworkers;
+ ringbuf_worker_t workers[];
+};
+
+/*
+ * ringbuf_setup: initialise a new ring buffer of a given length.
+ */
+int
+ringbuf_setup(ringbuf_t *rbuf, unsigned nworkers, size_t length)
+{
+ if (length >= RBUF_OFF_MASK) {
+ errno = EINVAL;
+ return -1;
+ }
+ memset(rbuf, 0, offsetof(ringbuf_t, workers[nworkers]));
+ rbuf->space = length;
+ rbuf->end = RBUF_OFF_MAX;
+ rbuf->nworkers = nworkers;
+ return 0;
+}
+
+/*
+ * ringbuf_get_sizes: return the sizes of the ringbuf_t and ringbuf_worker_t.
+ */
+void
+ringbuf_get_sizes(unsigned nworkers,
+ size_t *ringbuf_size, size_t *ringbuf_worker_size)
+{
+ if (ringbuf_size)
+ *ringbuf_size = offsetof(ringbuf_t, workers[nworkers]);
+ if (ringbuf_worker_size)
+ *ringbuf_worker_size = sizeof(ringbuf_worker_t);
+}
+
+/*
+ * ringbuf_register: register the worker (thread/process) as a producer
+ * and pass the pointer to its local store.
+ */
+ringbuf_worker_t *
+ringbuf_register(ringbuf_t *rbuf, unsigned i)
+{
+ ringbuf_worker_t *w = &rbuf->workers[i];
+
+ w->seen_off = RBUF_OFF_MAX;
+ atomic_store_explicit(&w->registered, true, memory_order_release);
+ return w;
+}
+
+void
+ringbuf_unregister(ringbuf_t *rbuf, ringbuf_worker_t *w)
+{
+ w->registered = false;
+ (void)rbuf;
+}
+
+/*
+ * stable_nextoff: capture and return a stable value of the 'next' offset.
+ */
+static inline ringbuf_off_t
+stable_nextoff(ringbuf_t *rbuf)
+{
+ unsigned count = SPINLOCK_BACKOFF_MIN;
+ ringbuf_off_t next;
+retry:
+ next = atomic_load_explicit(&rbuf->next, memory_order_acquire);
+ if (next & WRAP_LOCK_BIT) {
+ SPINLOCK_BACKOFF(count);
+ goto retry;
+ }
+ ASSERT((next & RBUF_OFF_MASK) < rbuf->space);
+ return next;
+}
+
+/*
+ * stable_seenoff: capture and return a stable value of the 'seen' offset.
+ */
+static inline ringbuf_off_t
+stable_seenoff(ringbuf_worker_t *w)
+{
+ unsigned count = SPINLOCK_BACKOFF_MIN;
+ ringbuf_off_t seen_off;
+retry:
+ seen_off = atomic_load_explicit(&w->seen_off, memory_order_acquire);
+ if (seen_off & WRAP_LOCK_BIT) {
+ SPINLOCK_BACKOFF(count);
+ goto retry;
+ }
+ return seen_off;
+}
+
+/*
+ * ringbuf_acquire: request a space of a given length in the ring buffer.
+ *
+ * => On success: returns the offset at which the space is available.
+ * => On failure: returns -1.
+ */
+ssize_t
+ringbuf_acquire(ringbuf_t *rbuf, ringbuf_worker_t *w, size_t len)
+{
+ ringbuf_off_t seen, next, target;
+
+ ASSERT(len > 0 && len <= rbuf->space);
+ ASSERT(w->seen_off == RBUF_OFF_MAX);
+
+ do {
+ ringbuf_off_t written;
+
+ /*
+ * Get the stable 'next' offset. Save the observed 'next'
+ * value (i.e. the 'seen' offset), but mark the value as
+ * unstable (set WRAP_LOCK_BIT).
+ *
+ * Note: CAS will issue a memory_order_release for us and
+ * thus ensures that it reaches global visibility together
+ * with new 'next'.
+ */
+ seen = stable_nextoff(rbuf);
+ next = seen & RBUF_OFF_MASK;
+ ASSERT(next < rbuf->space);
+ atomic_store_explicit(&w->seen_off, next | WRAP_LOCK_BIT,
+ memory_order_relaxed);
+
+ /*
+ * Compute the target offset. Key invariant: we cannot
+ * go beyond the WRITTEN offset or catch up with it.
+ */
+ target = next + len;
+ written = rbuf->written;
+ if (__predict_false(next < written && target >= written)) {
+ /* The producer must wait. */
+ atomic_store_explicit(&w->seen_off,
+ RBUF_OFF_MAX, memory_order_release);
+ return -1;
+ }
+
+ if (__predict_false(target >= rbuf->space)) {
+ const bool exceed = target > rbuf->space;
+
+ /*
+ * Wrap-around and start from the beginning.
+ *
+ * If we would exceed the buffer, then attempt to
+ * acquire the WRAP_LOCK_BIT and use the space in
+ * the beginning. If we used all space exactly to
+ * the end, then reset to 0.
+ *
+ * Check the invariant again.
+ */
+ target = exceed ? (WRAP_LOCK_BIT | len) : 0;
+ if ((target & RBUF_OFF_MASK) >= written) {
+ atomic_store_explicit(&w->seen_off,
+ RBUF_OFF_MAX, memory_order_release);
+ return -1;
+ }
+ /* Increment the wrap-around counter. */
+ target |= WRAP_INCR(seen & WRAP_COUNTER);
+ } else {
+ /* Preserve the wrap-around counter. */
+ target |= seen & WRAP_COUNTER;
+ }
+ } while (!atomic_compare_exchange_weak(&rbuf->next, &seen, target));
+
+ /*
+ * Acquired the range. Clear WRAP_LOCK_BIT in the 'seen' value
+ * thus indicating that it is stable now.
+ *
+ * No need for memory_order_release, since CAS issued a fence.
+ */
+ atomic_store_explicit(&w->seen_off, w->seen_off & ~WRAP_LOCK_BIT,
+ memory_order_relaxed);
+
+ /*
+ * If we set the WRAP_LOCK_BIT in the 'next' (because we exceed
+ * the remaining space and need to wrap-around), then save the
+ * 'end' offset and release the lock.
+ */
+ if (__predict_false(target & WRAP_LOCK_BIT)) {
+ /* Cannot wrap-around again if consumer did not catch-up. */
+ ASSERT(rbuf->written <= next);
+ ASSERT(rbuf->end == RBUF_OFF_MAX);
+ rbuf->end = next;
+ next = 0;
+
+ /*
+ * Unlock: ensure the 'end' offset reaches global
+ * visibility before the lock is released.
+ */
+ atomic_store_explicit(&rbuf->next,
+ (target & ~WRAP_LOCK_BIT), memory_order_release);
+ }
+ ASSERT((target & RBUF_OFF_MASK) <= rbuf->space);
+ return (ssize_t)next;
+}
+
+/*
+ * ringbuf_produce: indicate the acquired range in the buffer is produced
+ * and is ready to be consumed.
+ */
+void
+ringbuf_produce(ringbuf_t *rbuf, ringbuf_worker_t *w)
+{
+ (void)rbuf;
+ ASSERT(w->registered);
+ ASSERT(w->seen_off != RBUF_OFF_MAX);
+ atomic_store_explicit(&w->seen_off, RBUF_OFF_MAX, memory_order_release);
+}
+
+/*
+ * ringbuf_consume: get a contiguous range which is ready to be consumed.
+ */
+size_t
+ringbuf_consume(ringbuf_t *rbuf, size_t *offset)
+{
+ ringbuf_off_t written = rbuf->written, next, ready;
+ size_t towrite;
+retry:
+ /*
+ * Get the stable 'next' offset. Note: stable_nextoff() issued
+ * a load memory barrier. The area between the 'written' offset
+ * and the 'next' offset will be the *preliminary* target buffer
+ * area to be consumed.
+ */
+ next = stable_nextoff(rbuf) & RBUF_OFF_MASK;
+ if (written == next) {
+ /* If producers did not advance, then nothing to do. */
+ return 0;
+ }
+
+ /*
+ * Observe the 'ready' offset of each producer.
+ *
+ * At this point, some producer might have already triggered the
+ * wrap-around and some (or all) seen 'ready' values might be in
+ * the range between 0 and 'written'. We have to skip them.
+ */
+ ready = RBUF_OFF_MAX;
+
+ for (unsigned i = 0; i < rbuf->nworkers; i++) {
+ ringbuf_worker_t *w = &rbuf->workers[i];
+ ringbuf_off_t seen_off;
+
+ /*
+ * Skip if the worker has not registered.
+ *
+ * Get a stable 'seen' value. This is necessary since we
+ * want to discard the stale 'seen' values.
+ */
+ if (!atomic_load_explicit(&w->registered, memory_order_relaxed))
+ continue;
+ seen_off = stable_seenoff(w);
+
+ /*
+ * Ignore the offsets after the possible wrap-around.
+ * We are interested in the smallest seen offset that is
+ * not behind the 'written' offset.
+ */
+ if (seen_off >= written) {
+ ready = MIN(seen_off, ready);
+ }
+ ASSERT(ready >= written);
+ }
+
+ /*
+ * Finally, we need to determine whether wrap-around occurred
+ * and deduct the safe 'ready' offset.
+ */
+ if (next < written) {
+ const ringbuf_off_t end = MIN(rbuf->space, rbuf->end);
+
+ /*
+ * Wrap-around case. Check for the cut off first.
+ *
+ * Reset the 'written' offset if it reached the end of
+ * the buffer or the 'end' offset (if set by a producer).
+ * However, we must check that the producer is actually
+ * done (the observed 'ready' offsets are clear).
+ */
+ if (ready == RBUF_OFF_MAX && written == end) {
+ /*
+ * Clear the 'end' offset if was set.
+ */
+ if (rbuf->end != RBUF_OFF_MAX) {
+ rbuf->end = RBUF_OFF_MAX;
+ }
+
+ /*
+ * Wrap-around the consumer and start from zero.
+ */
+ written = 0;
+ atomic_store_explicit(&rbuf->written,
+ written, memory_order_release);
+ goto retry;
+ }
+
+ /*
+ * We cannot wrap-around yet; there is data to consume at
+ * the end. The ready range is smallest of the observed
+ * 'ready' or the 'end' offset. If neither is set, then
+ * the actual end of the buffer.
+ */
+ ASSERT(ready > next);
+ ready = MIN(ready, end);
+ ASSERT(ready >= written);
+ } else {
+ /*
+ * Regular case. Up to the observed 'ready' (if set)
+ * or the 'next' offset.
+ */
+ ready = MIN(ready, next);
+ }
+ towrite = ready - written;
+ *offset = written;
+
+ ASSERT(ready >= written);
+ ASSERT(towrite <= rbuf->space);
+ return towrite;
+}
+
+/*
+ * ringbuf_release: indicate that the consumed range can now be released.
+ */
+void
+ringbuf_release(ringbuf_t *rbuf, size_t nbytes)
+{
+ const size_t nwritten = rbuf->written + nbytes;
+
+ ASSERT(rbuf->written <= rbuf->space);
+ ASSERT(rbuf->written <= rbuf->end);
+ ASSERT(nwritten <= rbuf->space);
+
+ rbuf->written = (nwritten == rbuf->space) ? 0 : nwritten;
+}
diff --git a/perf/thirdparty/rmind_ringbuf/ringbuf.h b/perf/thirdparty/rmind_ringbuf/ringbuf.h
new file mode 100644
index 0000000..e8fc767
--- /dev/null
+++ b/perf/thirdparty/rmind_ringbuf/ringbuf.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu>
+ * All rights reserved.
+ *
+ * Use is subject to license terms, as specified in the LICENSE file.
+ */
+
+#ifndef _RINGBUF_H_
+#define _RINGBUF_H_
+
+__BEGIN_DECLS
+
+typedef struct ringbuf ringbuf_t;
+typedef struct ringbuf_worker ringbuf_worker_t;
+
+int ringbuf_setup(ringbuf_t *, unsigned, size_t);
+void ringbuf_get_sizes(unsigned, size_t *, size_t *);
+
+ringbuf_worker_t *ringbuf_register(ringbuf_t *, unsigned);
+void ringbuf_unregister(ringbuf_t *, ringbuf_worker_t *);
+
+ssize_t ringbuf_acquire(ringbuf_t *, ringbuf_worker_t *, size_t);
+void ringbuf_produce(ringbuf_t *, ringbuf_worker_t *);
+size_t ringbuf_consume(ringbuf_t *, size_t *);
+void ringbuf_release(ringbuf_t *, size_t);
+
+__END_DECLS
+
+#endif
diff --git a/perf/thirdparty/rmind_ringbuf/utils.h b/perf/thirdparty/rmind_ringbuf/utils.h
new file mode 100644
index 0000000..413157b
--- /dev/null
+++ b/perf/thirdparty/rmind_ringbuf/utils.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)cdefs.h 8.8 (Berkeley) 1/9/95
+ */
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <assert.h>
+
+/*
+ * A regular assert (debug/diagnostic only).
+ */
+#if defined(DEBUG)
+#define ASSERT assert
+#else
+#define ASSERT(x)
+#endif
+
+/*
+ * Minimum, maximum and rounding macros.
+ */
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+/*
+ * Branch prediction macros.
+ */
+#ifndef __predict_true
+#define __predict_true(x) __builtin_expect((x) != 0, 1)
+#define __predict_false(x) __builtin_expect((x) != 0, 0)
+#endif
+
+/*
+ * Atomic operations and memory barriers. If C11 API is not available,
+ * then wrap the GCC builtin routines.
+ *
+ * Note: This atomic_compare_exchange_weak does not do the C11 thing of
+ * filling *(expected) with the actual value, because we don't need
+ * that here.
+ */
+#ifndef atomic_compare_exchange_weak
+#define atomic_compare_exchange_weak(ptr, expected, desired) \
+ __sync_bool_compare_and_swap(ptr, *(expected), desired)
+#endif
+
+#ifndef atomic_thread_fence
+#define memory_order_relaxed __ATOMIC_RELAXED
+#define memory_order_acquire __ATOMIC_ACQUIRE
+#define memory_order_release __ATOMIC_RELEASE
+#define memory_order_seq_cst __ATOMIC_SEQ_CST
+#define atomic_thread_fence(m) __atomic_thread_fence(m)
+#endif
+#ifndef atomic_store_explicit
+#define atomic_store_explicit __atomic_store_n
+#endif
+#ifndef atomic_load_explicit
+#define atomic_load_explicit __atomic_load_n
+#endif
+
+/*
+ * Exponential back-off for the spinning paths.
+ */
+#define SPINLOCK_BACKOFF_MIN 4
+#define SPINLOCK_BACKOFF_MAX 128
+#if defined(__x86_64__) || defined(__i386__)
+#define SPINLOCK_BACKOFF_HOOK __asm volatile("pause" ::: "memory")
+#else
+#define SPINLOCK_BACKOFF_HOOK
+#endif
+#define SPINLOCK_BACKOFF(count) \
+do { \
+ for (int __i = (count); __i != 0; __i--) { \
+ SPINLOCK_BACKOFF_HOOK; \
+ } \
+ if ((count) < SPINLOCK_BACKOFF_MAX) \
+ (count) += (count); \
+} while (/* CONSTCOND */ 0);
+
+#endif