From d40599923167e43addcc61696b8ea75b2e2abcd7 Mon Sep 17 00:00:00 2001
From: Dmitriy Gerasimov <naeper@demlabs.net>
Date: Sun, 26 May 2019 18:03:59 +0700
Subject: [PATCH] [*] New files

---
 include/dap_circular_buffer.h          |   52 ++
 include/dap_common.h                   |  147 ++++
 include/dap_config.h                   |   79 ++
 include/dap_file_utils.h               |   80 ++
 include/dap_list.h                     |   62 ++
 include/dap_math_ops.h                 |   27 +
 include/dap_module.h                   |   46 +
 include/dap_strfuncs.h                 |   73 ++
 include/dap_string.h                   |   94 +++
 include/uthash.h                       | 1074 ++++++++++++++++++++++++
 include/utlist.h                       |  895 ++++++++++++++++++++
 src/circular_buffer.h                  |   54 ++
 src/common/int-util.h                  |  249 ++++++
 src/common/memwipe.c                   |  106 +++
 src/common/memwipe.h                   |   36 +
 src/core.pri                           |   31 +
 src/dap_circular_buffer.c              |  346 ++++++++
 src/dap_common.c                       |  678 +++++++++++++++
 src/dap_config.c                       |  575 +++++++++++++
 src/dap_file_utils.c                   |  346 ++++++++
 src/dap_list.c                         | 1005 ++++++++++++++++++++++
 src/dap_module.c                       |   29 +
 src/dap_strfuncs.c                     |  713 ++++++++++++++++
 src/dap_string.c                       |  917 ++++++++++++++++++++
 src/darwin/CMakeLists.txt              |   17 +
 src/darwin/darwin.pri                  |    5 +
 src/darwin/macos/dap_network_monitor.c |  329 ++++++++
 src/darwin/macos/dap_network_monitor.h |   53 ++
 src/darwin/macos/macos.pri             |   10 +
 src/darwin/macos/pthread_barrier.h     |   65 ++
 src/unix/CMakeLists.txt                |   23 +
 src/unix/dap_cpu_monitor.c             |  113 +++
 src/unix/dap_cpu_monitor.h             |   69 ++
 src/unix/dap_process_manager.c         |   79 ++
 src/unix/dap_process_manager.h         |   56 ++
 src/unix/dap_process_memory.c          |   64 ++
 src/unix/dap_process_memory.h          |   56 ++
 src/unix/linux/dap_network_monitor.c   |  245 ++++++
 src/unix/linux/dap_network_monitor.h   |   91 ++
 src/unix/linux/linux.pri               |    5 +
 src/unix/unix.pri                      |   17 +
 test/dap_circular_test.c               |  214 +++++
 test/dap_circular_test.h               |    5 +
 test/dap_common_test.c                 |   14 +
 test/dap_common_test.h                 |    5 +
 test/dap_config_test.c                 |  184 ++++
 test/dap_config_test.h                 |    5 +
 test/dap_strfuncs_test.c               |  145 ++++
 test/dap_strfuncs_test.h               |    5 +
 test/main.c                            |   24 +
 test/unix/dap_cpu_monitor_test.c       |   38 +
 test/unix/dap_cpu_monitor_test.h       |    6 +
 test/unix/dap_network_monitor_test.c   |  188 +++++
 test/unix/dap_network_monitor_test.h   |    4 +
 test/unix/dap_process_mem_test.c       |   23 +
 test/unix/dap_process_mem_test.h       |    4 +
 56 files changed, 9875 insertions(+)
 create mode 100755 include/dap_circular_buffer.h
 create mode 100755 include/dap_common.h
 create mode 100755 include/dap_config.h
 create mode 100755 include/dap_file_utils.h
 create mode 100755 include/dap_list.h
 create mode 100755 include/dap_math_ops.h
 create mode 100755 include/dap_module.h
 create mode 100755 include/dap_strfuncs.h
 create mode 100755 include/dap_string.h
 create mode 100755 include/uthash.h
 create mode 100755 include/utlist.h
 create mode 100755 src/circular_buffer.h
 create mode 100755 src/common/int-util.h
 create mode 100755 src/common/memwipe.c
 create mode 100755 src/common/memwipe.h
 create mode 100755 src/core.pri
 create mode 100755 src/dap_circular_buffer.c
 create mode 100755 src/dap_common.c
 create mode 100755 src/dap_config.c
 create mode 100755 src/dap_file_utils.c
 create mode 100755 src/dap_list.c
 create mode 100755 src/dap_module.c
 create mode 100755 src/dap_strfuncs.c
 create mode 100755 src/dap_string.c
 create mode 100755 src/darwin/CMakeLists.txt
 create mode 100755 src/darwin/darwin.pri
 create mode 100755 src/darwin/macos/dap_network_monitor.c
 create mode 100755 src/darwin/macos/dap_network_monitor.h
 create mode 100755 src/darwin/macos/macos.pri
 create mode 100755 src/darwin/macos/pthread_barrier.h
 create mode 100755 src/unix/CMakeLists.txt
 create mode 100755 src/unix/dap_cpu_monitor.c
 create mode 100755 src/unix/dap_cpu_monitor.h
 create mode 100755 src/unix/dap_process_manager.c
 create mode 100755 src/unix/dap_process_manager.h
 create mode 100755 src/unix/dap_process_memory.c
 create mode 100755 src/unix/dap_process_memory.h
 create mode 100755 src/unix/linux/dap_network_monitor.c
 create mode 100755 src/unix/linux/dap_network_monitor.h
 create mode 100755 src/unix/linux/linux.pri
 create mode 100755 src/unix/unix.pri
 create mode 100755 test/dap_circular_test.c
 create mode 100755 test/dap_circular_test.h
 create mode 100755 test/dap_common_test.c
 create mode 100755 test/dap_common_test.h
 create mode 100755 test/dap_config_test.c
 create mode 100755 test/dap_config_test.h
 create mode 100755 test/dap_strfuncs_test.c
 create mode 100755 test/dap_strfuncs_test.h
 create mode 100755 test/main.c
 create mode 100755 test/unix/dap_cpu_monitor_test.c
 create mode 100755 test/unix/dap_cpu_monitor_test.h
 create mode 100755 test/unix/dap_network_monitor_test.c
 create mode 100755 test/unix/dap_network_monitor_test.h
 create mode 100755 test/unix/dap_process_mem_test.c
 create mode 100755 test/unix/dap_process_mem_test.h

diff --git a/include/dap_circular_buffer.h b/include/dap_circular_buffer.h
new file mode 100755
index 0000000..ed7a1fa
--- /dev/null
+++ b/include/dap_circular_buffer.h
@@ -0,0 +1,52 @@
+//
+//  CircularBuffer.h
+//
+//  Created by 罗亮富(Roen)zxllf23@163.com on 14-1-14.
+//  Copyright (c) 2014å¹´ All rights reserved.
+//
+//  Note: edited by Anatolii Kurotych
+
+#ifndef YYDJ_Roen_CircularBuffer_h
+#define YYDJ_Roen_CircularBuffer_h
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+/*
+ A circular buffer(circular queue, cyclic buffer or ring buffer), is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams. visit https://en.wikipedia.org/wiki/Circular_buffer to see more information.
+ */
+
+typedef struct s_circularBuffer* circular_buffer_t;
+
+// Construct CircularBuffer with ‘size' in byte. You must call CircularBufferFree() in balance for destruction.
+extern circular_buffer_t circular_buffer_create(size_t size);
+
+// Destruct CircularBuffer
+extern void circular_buffer_free(circular_buffer_t cBuf);
+
+// Reset the CircularBuffer
+extern void circular_buffer_reset(circular_buffer_t cBuf);
+
+//get the capacity of CircularBuffer
+extern size_t circular_buffer_get_capacity(circular_buffer_t cBuf);
+
+//get occupied data size of CircularBuffer
+extern size_t circular_buffer_get_data_size(circular_buffer_t cBuf);
+
+// Push data to the tail of a circular buffer from 'src' with 'length' size in byte.
+extern void circular_buffer_push(circular_buffer_t cBuf, const void *src, size_t length);
+
+// Pop data from a circular buffer to 'dataOut'  with wished 'length' size in byte,return the actual data size in byte popped out,which is less or equal to the input 'length parameter.
+extern size_t circular_buffer_pop(circular_buffer_t cBuf, size_t length, void *dataOut);
+
+// Read data from a circular buffer to 'dataOut'  with wished 'length' size in byte,return the actual data size in byte popped out,which is less or equal to the input 'length parameter.
+extern size_t circular_buffer_read(circular_buffer_t cBuf, size_t length, void *dataOut);
+
+//for test purpose, print the circular buffer's data content by printf(...); the 'hex' parameters indicates that if the data should be printed in asscii string or hex data format.
+extern void circular_buffer_print(circular_buffer_t cBuf, bool hex);
+
+#ifdef __unix__
+// Read data from a circular buffer to socketFd
+extern int circular_buffer_write_In_socket(circular_buffer_t cBuf, int sockfd);
+#endif
+#endif
diff --git a/include/dap_common.h b/include/dap_common.h
new file mode 100755
index 0000000..d7a7299
--- /dev/null
+++ b/include/dap_common.h
@@ -0,0 +1,147 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <kahovski@gmail.com>
+ * Anatolii Kurotych <akurotych@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+#define DAP_NEW(a)    ( (a*) malloc(sizeof(a)))
+#define DAP_NEW_SIZE(a,b)    ( (a*) malloc(b))
+#define DAP_NEW_Z(a) ( (a*) calloc(1,sizeof(a)))
+#define DAP_NEW_Z_SIZE(a,b) ( (a*) calloc(1,b))
+#define DAP_REALLOC(a,b) (realloc(a,b))
+
+#define DAP_DELETE(a)   free(a)
+#define DAP_DUP(a) (__typeof(a) ret = memcpy(ret,a,sizeof(*a)) )
+
+#define DAP_PROTOCOL_VERSION 22
+
+#if defined(__GNUC__) ||defined (__clang__)
+#define DAP_ALIGN_PACKED  __attribute__((aligned(1),packed))
+#else
+#define DAP_ALIGN_PACKED  __attribute__((aligned(1),packed))
+#endif
+
+/**
+ * @brief The log_level enum
+ */
+typedef enum dap_log_level{L_CRITICAL=5,L_ERROR=4, L_WARNING=3,L_NOTICE=2,L_INFO=1,L_DEBUG=0} dap_log_level_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    DAP_ASCII_ALNUM = 1 << 0,
+    DAP_ASCII_ALPHA = 1 << 1,
+    DAP_ASCII_CNTRL = 1 << 2,
+    DAP_ASCII_DIGIT = 1 << 3,
+    DAP_ASCII_GRAPH = 1 << 4,
+    DAP_ASCII_LOWER = 1 << 5,
+    DAP_ASCII_PRINT = 1 << 6,
+    DAP_ASCII_PUNCT = 1 << 7,
+    DAP_ASCII_SPACE = 1 << 8,
+    DAP_ASCII_UPPER = 1 << 9,
+    DAP_ASCII_XDIGIT = 1 << 10
+} DapAsciiType;
+
+static const uint16_t s_ascii_table_data[256] = {
+    0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
+    0x004, 0x104, 0x104, 0x004, 0x104, 0x104, 0x004, 0x004,
+    0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
+    0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
+    0x140, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
+    0x459, 0x459, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x0d0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
+    0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
+    0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
+    0x253, 0x253, 0x253, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x0d0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
+    0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
+    0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
+    0x073, 0x073, 0x073, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x004
+/* the upper 128 are all zeroes */
+};
+
+//const uint16_t * const c_dap_ascii_table = s_ascii_table_data;
+
+#define dap_ascii_isspace(c) (s_ascii_table_data[(unsigned char) (c)] & DAP_ASCII_SPACE) != 0
+#define dap_ascii_isalpha(c) (s_ascii_table_data[(unsigned char) (c)] & DAP_ASCII_ALPHA) != 0
+
+int dap_common_init( const char * a_log_file );
+void dap_common_deinit(void);
+
+// list of logs
+typedef struct dap_list_logs_item{
+    time_t t;
+    char *str;
+} dap_list_logs_item_t;
+
+// set max items in log list
+void dap_log_set_max_item(unsigned int a_max);
+// get logs from list
+char *dap_log_get_item(time_t a_start_time, int a_limit);
+
+void _log_it(const char * log_tag, enum dap_log_level, const char * format,...);
+void _vlog_it(const char * log_tag, enum dap_log_level, const char * format, va_list ap );
+#define log_it(_log_level,...) _log_it(LOG_TAG,_log_level,##__VA_ARGS__)
+#define vlog_it(a_log_level,a_format,a_ap) _vlog_it(LOG_TAG,a_log_level,a_format,a_ap)
+
+const char * log_error(void);
+void dap_log_level_set(enum dap_log_level ll);
+enum dap_log_level dap_log_level_get(void);
+
+void dap_set_log_tag_width(size_t width);
+
+char *dap_itoa(int i);
+
+int dap_time_to_str_rfc822(char * out, size_t out_size_max, time_t t);
+
+int get_select_breaker(void);
+int send_select_break(void);
+char * exec_with_ret(const char * a_cmd);
+char * exec_with_ret_multistring(const char * a_cmd);
+char * dap_random_string_create_alloc(size_t a_length);
+void dap_random_string_fill(char *str, size_t length);
+void *memzero(void *mem, size_t n);
+void dap_dump_hex(const void* data, size_t size);
+
+size_t dap_hex2bin(uint8_t *a_out, const char *a_in, size_t a_len);
+size_t dap_bin2hex(char *a_out, const void *a_in, size_t a_len);
+void dap_digit_from_string(const char *num_str, uint8_t *raw, size_t raw_len);
+
+
+#ifdef __MINGW32__
+int exec_silent(const char *a_cmd);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/dap_config.h b/include/dap_config.h
new file mode 100755
index 0000000..227e281
--- /dev/null
+++ b/include/dap_config.h
@@ -0,0 +1,79 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <kahovski@gmail.com>
+ * Anatolii Kurotych <akurotych@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DAP_CONFIG_H_
+#define _DAP_CONFIG_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct dap_config{
+    void * _internal;
+} dap_config_t;
+
+int dap_config_init(const char * a_configs_path);
+void dap_config_deinit();
+dap_config_t * dap_config_open(const char * a_name);
+void dap_config_close(dap_config_t * a_config);
+
+const char * dap_config_path();
+
+uint16_t dap_config_get_item_uint16(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+uint16_t dap_config_get_item_uint16_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, uint16_t a_default);
+
+int32_t dap_config_get_item_int32(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+int32_t dap_config_get_item_int32_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, int32_t a_default);
+
+int64_t dap_config_get_item_int64(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+int64_t dap_config_get_item_int64_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, int64_t a_default);
+
+uint64_t dap_config_get_item_uint64(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+uint64_t dap_config_get_item_uint64_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, uint64_t a_default);
+
+const char * dap_config_get_item_str(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+const char * dap_config_get_item_str_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, const char * a_value_default);
+char** dap_config_get_array_str(dap_config_t * a_config, const char * a_section_path,
+                                      const char * a_item_name, uint16_t * array_length);
+
+bool dap_config_get_item_bool(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+bool dap_config_get_item_bool_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, bool a_default);
+
+double dap_config_get_item_double(dap_config_t * a_config, const char * a_section_path, const char * a_item_name);
+double dap_config_get_item_double_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, double a_default);
+
+extern dap_config_t * g_config;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/include/dap_file_utils.h b/include/dap_file_utils.h
new file mode 100755
index 0000000..e2bbb2e
--- /dev/null
+++ b/include/dap_file_utils.h
@@ -0,0 +1,80 @@
+/*
+ * Authors:
+ * Aleksandr Lysikov <alexander.lysikov@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdbool.h>
+
+#ifndef _DAP_FILE_UTILS_H_
+#define _DAP_FILE_UTILS_H_
+
+#ifdef _WIN32
+
+/* On Win32, the canonical directory separator is the backslash, and
+ * the search path separator is the semicolon. Note that also the
+ * (forward) slash works as directory separator.
+ */
+#define DAP_DIR_SEPARATOR '\\'
+#define DAP_DIR_SEPARATOR_S "\\"
+#define DAP_IS_DIR_SEPARATOR(c) ((c) == DAP_DIR_SEPARATOR || (c) == '/')
+#define DAP_SEARCHPATH_SEPARATOR ';'
+#define DAP_SEARCHPATH_SEPARATOR_S ";"
+
+#else
+
+#define DAP_DIR_SEPARATOR '/'
+#define DAP_DIR_SEPARATOR_S "/"
+#define DAP_IS_DIR_SEPARATOR(c) ((c) == DAP_DIR_SEPARATOR)
+#define DAP_SEARCHPATH_SEPARATOR ':'
+#define DAP_SEARCHPATH_SEPARATOR_S ":"
+
+#endif
+
+/**
+ * Check the directory path for unsupported symbols
+ *
+ * @dir_path directory pathname
+ * @return true, if the directory path contains only ASCII symbols
+ */
+bool dap_valid_ascii_symbols(const char *a_dir_path);
+
+/**
+ * Check the directory for exists
+ *
+ * @dir_path directory pathname
+ * @return true, if the file is a directory
+ */
+bool dap_dir_test(const char * a_dir_path);
+
+/**
+ * Create a new directory with intermediate sub-directories
+ *
+ * @dir_path new directory pathname
+ * @return 0, if the directory was created or already exist, else -1
+ */
+int dap_mkdir_with_parents(const char *a_dir_path);
+
+
+char* dap_path_get_basename(const char *a_file_name);
+bool  dap_path_is_absolute(const char *a_file_name);
+char* dap_path_get_dirname(const char *a_file_name);
+
+#endif // _FILE_UTILS_H_
diff --git a/include/dap_list.h b/include/dap_list.h
new file mode 100755
index 0000000..1ae4957
--- /dev/null
+++ b/include/dap_list.h
@@ -0,0 +1,62 @@
+/*
+ * Doubly-Linked Lists — linked lists that can be iterated over in both directions
+ */
+
+#ifndef __DAP_LIST_H__
+#define __DAP_LIST_H__
+
+typedef void (*dap_callback_destroyed_t)(void* data);
+typedef void (*dap_callback_t)(void* data, void* user_data);
+typedef void* (*dap_callback_copy_t)(const void * src, void* data);
+typedef int (*dap_callback_compare_t)(const void * a, const void * b);
+typedef int (*dap_callback_compare_data_t)(const void * a, const void * b, void* user_data);
+
+typedef struct _dap_list dap_list_t;
+
+struct _dap_list
+{
+    void* data;
+    dap_list_t *next;
+    dap_list_t *prev;
+};
+
+/* Doubly linked lists
+ */
+dap_list_t* dap_list_alloc(void);
+void dap_list_free(dap_list_t *list);
+void dap_list_free1(dap_list_t *list);
+void dap_list_free_full(dap_list_t *list, dap_callback_destroyed_t free_func);
+dap_list_t* dap_list_append(dap_list_t *list, void* data);
+dap_list_t* dap_list_prepend(dap_list_t *list, void* data);
+dap_list_t* dap_list_insert(dap_list_t *list, void* data, int position);
+dap_list_t* dap_list_insert_sorted(dap_list_t *list, void* data, dap_callback_compare_t func);
+dap_list_t* dap_list_insert_sorted_with_data(dap_list_t *list, void* data, dap_callback_compare_data_t func, void* user_data);
+dap_list_t* dap_list_insert_before(dap_list_t *list, dap_list_t *sibling, void* data);
+dap_list_t* dap_list_concat(dap_list_t *list1, dap_list_t *list2);
+dap_list_t* dap_list_remove(dap_list_t *list, const void * data);
+dap_list_t* dap_list_remove_all(dap_list_t *list, const void * data);
+dap_list_t* dap_list_remove_link(dap_list_t *list, dap_list_t *llink);
+dap_list_t* dap_list_delete_link(dap_list_t *list, dap_list_t *link_);
+dap_list_t* dap_list_reverse(dap_list_t *list);
+dap_list_t* dap_list_copy(dap_list_t *list);
+
+dap_list_t* dap_list_copy_deep(dap_list_t *list, dap_callback_copy_t func, void* user_data);
+
+dap_list_t* dap_list_nth(dap_list_t *list, unsigned int n);
+dap_list_t* dap_list_nth_prev(dap_list_t *list, unsigned int n);
+dap_list_t* dap_list_find(dap_list_t *list, const void * data);
+dap_list_t* dap_list_find_custom(dap_list_t *list, const void * data, dap_callback_compare_t func);
+int dap_list_position(dap_list_t *list, dap_list_t *llink);
+int dap_list_index(dap_list_t *list, const void * data);
+dap_list_t* dap_list_last(dap_list_t *list);
+dap_list_t* dap_list_first(dap_list_t *list);
+unsigned int dap_list_length(dap_list_t *list);
+void dap_list_foreach(dap_list_t *list, dap_callback_t func, void* user_data);
+dap_list_t* dap_list_sort(dap_list_t *list, dap_callback_compare_t compare_func);
+dap_list_t* dap_list_sort_with_data(dap_list_t *list, dap_callback_compare_data_t compare_func, void* user_data);
+void* dap_list_nth_data(dap_list_t *list, unsigned int n);
+
+#define dap_list_previous(list)	        ((list) ? (((dap_list_t *)(list))->prev) : NULL)
+#define dap_list_next(list)	        ((list) ? (((dap_list_t *)(list))->next) : NULL)
+
+#endif /* __DAP_LIST_H__ */
diff --git a/include/dap_math_ops.h b/include/dap_math_ops.h
new file mode 100755
index 0000000..993939f
--- /dev/null
+++ b/include/dap_math_ops.h
@@ -0,0 +1,27 @@
+#pragma once
+#include <stdint.h>
+//#include "common/int-util.h"
+
+#if defined(__GNUC__) ||defined (__clang__)
+
+#if __SIZEOF_INT128__ == 16
+
+#define DAP_GLOBAL_IS_INT128
+typedef __int128 _dap_int128_t;
+
+#if !defined (int128_t)
+typedef __int128 int128_t;
+#endif
+#if !defined (uint128_t)
+typedef unsigned __int128 uint128_t;
+#endif
+#endif
+#endif
+
+typedef union dap_uint128{
+    uint8_t data_raw[16];
+#if defined(DAP_GLOBAL_IS_INT128)
+    _dap_int128_t data_int128;
+#endif
+} dap_uint128_t;
+
diff --git a/include/dap_module.h b/include/dap_module.h
new file mode 100755
index 0000000..0263531
--- /dev/null
+++ b/include/dap_module.h
@@ -0,0 +1,46 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2018
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+typedef int (*dap_module_callback_init_t)(void * arg0, ...);
+typedef void (*dap_module_callback_deinit_t)(void);
+
+typedef struct dap_module {
+    const char * name;
+    unsigned int version;
+    const char * dependensies[];
+} dap_module_t;
+
+#define DAP_MODULE_ARGS_MAX  10
+typedef struct dap_module_args {
+    const char * name;
+    const char * args[DAP_MODULE_ARGS_MAX]; // ARGS could me not more than DAP_MODULE_ARGS_MAX define
+} dap_module_args_t;
+
+int dap_module_add(const char * a_name, unsigned int a_version, const char * a_dependensies,
+                   dap_module_callback_init_t a_init_callback, dap_module_args_t a_init_args[],
+                   dap_module_callback_deinit_t a_deinit_callback );
+
+int dap_module_init_all(void);
+void dap_module_deinit_all(void);
diff --git a/include/dap_strfuncs.h b/include/dap_strfuncs.h
new file mode 100755
index 0000000..0ecf963
--- /dev/null
+++ b/include/dap_strfuncs.h
@@ -0,0 +1,73 @@
+/* DAP String Functions */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+
+#define dap_return_if_fail(expr)            {if(!(expr)) {return;}}
+#define dap_return_val_if_fail(expr,val)    {if(!(expr)) {return (val);}}
+
+#define POINTER_TO_INT(p)   ((int)   (p))
+#define POINTER_TO_UINT(p)  ((unsigned int)  (p))
+
+#define INT_TO_POINTER(i)   ((void*)  (i))
+#define UINT_TO_POINTER(u)  ((void*)  (u))
+
+#undef  max
+#define max(a, b)  (((a) > (b)) ? (a) : (b))
+
+#undef  min
+#define min(a, b)  (((a) < (b)) ? (a) : (b))
+
+#undef  abs
+#define abs(a)     (((a) < 0) ? -(a) : (a))
+
+#undef  clamp
+#define clamp(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+size_t dap_strlen(const char *a_str);
+// compare a_str1 and a_str2
+int dap_strcmp(const char *a_str1, const char *a_str2);
+// compare a_n characters of a_str1 and a_str2
+int dap_strncmp(const char *a_str1, const char *a_str2, size_t a_n);
+// duplicates a string
+char* dap_strdup(const char *a_str);
+char* dap_strdup_vprintf(const char *a_format, va_list a_args);
+char* dap_strdup_printf(const char *a_format, ...);
+
+char* dap_stpcpy(char *a_dest, const char *a_src);
+char* dap_strstr_len(const char *a_haystack, ssize_t a_haystack_len, const char *a_needle);
+// concatenates all of str_array's strings, sliding in an optional separator, the returned string is newly allocated.
+char* dap_strjoinv(const char *a_separator, char **a_str_array);
+char* dap_strjoin(const char *a_separator, ...);
+// split up string into max_tokens tokens at delimiter and return a newly allocated string array
+char** dap_strsplit(const char *a_string, const char *a_delimiter, int a_max_tokens);
+size_t dap_str_countv(char **a_str_array);
+// copies a NULL-terminated array of strings
+char** dap_strdupv(const char **a_str_array);
+// frees the array itself and all of its strings.
+void dap_strfreev(char **a_str_array);
+
+// removes leading spaces
+char* dap_strchug(char *a_string);
+// removes trailing spaces
+char* dap_strchomp(char *a_string);
+// removes leading & trailing spaces 
+#define dap_strstrip( a_string )    dap_strchomp (dap_strchug (a_string))
+
+// Converts all lower case ASCII letters to upper case ASCII letters.
+char* dap_strup(const char *a_str, ssize_t a_len);
+// Converts a string to lower case.
+char* dap_strdown(const char *a_str, ssize_t a_len);
+char* dap_strreverse(char *a_string);
+
+#define DAP_USEC_PER_SEC 1000000
+void dap_usleep(time_t a_microseconds);
+
diff --git a/include/dap_string.h b/include/dap_string.h
new file mode 100755
index 0000000..cacb8d0
--- /dev/null
+++ b/include/dap_string.h
@@ -0,0 +1,94 @@
+// dap_string_t is an object that handles the memory management of a C string for you.
+
+#ifndef __DAP_STRING_H__
+#define __DAP_STRING_H__
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+
+typedef struct _dap_string dap_string_t;
+
+struct _dap_string
+{
+    char *str;
+    size_t len;
+    size_t allocated_len;
+};
+
+dap_string_t* dap_string_new(const char *init);
+dap_string_t* dap_string_new_len(const char *init, ssize_t len);
+dap_string_t * dap_string_sized_new(size_t a_dfl_size);
+char* dap_string_free(dap_string_t *string, bool free_segment);
+
+bool dap_string_equal(const dap_string_t *v, const dap_string_t *v2);
+
+unsigned int dap_string_hash(const dap_string_t *str);
+
+dap_string_t* dap_string_assign(dap_string_t *string, const char *rval);
+
+dap_string_t* dap_string_truncate(dap_string_t *string, size_t len);
+
+dap_string_t* dap_string_set_size(dap_string_t *string, size_t len);
+
+dap_string_t* dap_string_insert_len(dap_string_t *string, ssize_t pos, const char *val, ssize_t len);
+
+dap_string_t* dap_string_append(dap_string_t *string, const char *val);
+
+dap_string_t* dap_string_append_len(dap_string_t *string, const char *val, ssize_t len);
+
+dap_string_t* dap_string_append_c(dap_string_t *string, char a_c);
+
+dap_string_t* dap_string_append_unichar(dap_string_t *string, uint32_t wc);
+
+dap_string_t* dap_string_prepend(dap_string_t *string, const char *val);
+
+dap_string_t* dap_string_prepend_c(dap_string_t *string, char a_c);
+
+dap_string_t* dap_string_prepend_unichar(dap_string_t *string, uint32_t wc);
+
+dap_string_t* dap_string_prepend_len(dap_string_t *string, const char *val, ssize_t len);
+
+dap_string_t* dap_string_insert(dap_string_t *string, ssize_t pos, const char *val);
+
+dap_string_t* dap_string_insert_c(dap_string_t *string, ssize_t pos, char a_c);
+
+dap_string_t* dap_string_insert_unichar(dap_string_t *string, ssize_t pos, uint32_t wc);
+
+dap_string_t* dap_string_overwrite(dap_string_t *string, size_t pos, const char *val);
+
+dap_string_t* dap_string_overwrite_len(dap_string_t *string, size_t pos, const char *val, ssize_t len);
+
+dap_string_t* dap_string_erase(dap_string_t *string, ssize_t pos, ssize_t len);
+
+void dap_string_vprintf(dap_string_t *string, const char *format, va_list args);
+void dap_string_printf(dap_string_t *string, const char *format, ...);
+void dap_string_append_vprintf(dap_string_t *string, const char *format, va_list args);
+void dap_string_append_printf(dap_string_t *string, const char *format, ...);
+
+/* -- optimize dap_strig_append_c --- */
+#ifdef G_CAN_INLINE
+static inline dap_string_t* dap_string_append_c_inline(dap_string_t *a_string, char a_c)
+{
+    if(a_string->len + 1 < a_string->allocated_len)
+    {
+        a_string->str[a_string->len++] = a_c;
+        a_string->str[a_string->len] = 0;
+    }
+    else
+    dap_string_insert_c(a_string, -1, a_c);
+    return a_string;
+}
+#define dap_string_append_c(a_string,a_c)       dap_string_append_c_inline (a_string, a_c)
+#endif /* G_CAN_INLINE */
+
+dap_string_t *dap_string_down(dap_string_t *string);
+
+dap_string_t *dap_string_up(dap_string_t *string);
+
+#ifndef G_DISABLE_DEPRECATED
+#define  dap_string_sprintf  dap_string_printf
+#define  dap_string_sprintfa dap_string_append_printf
+#endif
+
+#endif /* __DAP_STRING_H__ */
diff --git a/include/uthash.h b/include/uthash.h
new file mode 100755
index 0000000..775599c
--- /dev/null
+++ b/include/uthash.h
@@ -0,0 +1,1074 @@
+/*
+Copyright (c) 2003-2016, Troy D. Hanson     http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.0.1
+
+#include <string.h>   /* memcmp,strlen */
+#include <stddef.h>   /* ptrdiff_t */
+#include <stdlib.h>   /* exit() */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ source) this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#if defined(_MSC_VER)   /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else                   /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#else                   /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  char **_da_dst = (char**)(&(dst));                                             \
+  *_da_dst = (char*)(src);                                                       \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  (dst) = DECLTYPE(dst)(src);                                                    \
+} while (0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
+#endif
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+#ifndef uthash_memcmp
+#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_VALUE(keyptr,keylen,hashv)                                          \
+do {                                                                             \
+  HASH_FCN(keyptr, keylen, hashv);                                               \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
+do {                                                                             \
+  (out) = NULL;                                                                  \
+  if (head) {                                                                    \
+    unsigned _hf_bkt;                                                            \
+    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \
+    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) {                         \
+      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
+do {                                                                             \
+  unsigned _hf_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen, _hf_hashv);                                         \
+  HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);               \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl)                                                     \
+do {                                                                             \
+  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
+  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
+  if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
+  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                                \
+  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl)                                                     \
+do {                                                                             \
+  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv)                                                \
+  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv)                                               \
+  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head)                                                 \
+do {                                                                             \
+  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
+                  sizeof(UT_hash_table));                                        \
+  if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
+  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                              \
+  (head)->hh.tbl->tail = &((head)->hh);                                          \
+  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
+  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
+  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
+  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
+  memset((head)->hh.tbl->buckets, 0,                                             \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
+  (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do {                                                                             \
+  (replaced) = NULL;                                                             \
+  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+  if (replaced) {                                                                \
+     HASH_DELETE(hh, head, replaced);                                            \
+  }                                                                              \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do {                                                                             \
+  (replaced) = NULL;                                                             \
+  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+  if (replaced) {                                                                \
+     HASH_DELETE(hh, head, replaced);                                            \
+  }                                                                              \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add)                                          \
+do {                                                                             \
+  (add)->hh.next = NULL;                                                         \
+  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \
+  (head)->hh.tbl->tail->next = (add);                                            \
+  (head)->hh.tbl->tail = &((add)->hh);                                           \
+} while (0)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do {                                                                             \
+  unsigned _ha_bkt;                                                              \
+  (add)->hh.hashv = (hashval);                                                   \
+  (add)->hh.key = (char*) (keyptr);                                              \
+  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
+  if (!(head)) {                                                                 \
+    (add)->hh.next = NULL;                                                       \
+    (add)->hh.prev = NULL;                                                       \
+    (head) = (add);                                                              \
+    HASH_MAKE_TABLE(hh, head);                                                   \
+  } else {                                                                       \
+    void *_hs_iter = (head);                                                     \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    do {                                                                         \
+      if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0)                             \
+        break;                                                                   \
+    } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));         \
+    if (_hs_iter) {                                                              \
+      (add)->hh.next = _hs_iter;                                                 \
+      if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) {     \
+        HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add);              \
+      } else {                                                                   \
+        (head) = (add);                                                          \
+      }                                                                          \
+      HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add);                      \
+    } else {                                                                     \
+      HASH_APPEND_LIST(hh, head, add);                                           \
+    }                                                                            \
+  }                                                                              \
+  (head)->hh.tbl->num_items++;                                                   \
+  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
+  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh);                 \
+  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
+  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
+  HASH_FSCK(hh, head);                                                           \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \
+do {                                                                             \
+  unsigned _hs_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \
+  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \
+do {                                                                             \
+  unsigned _ha_bkt;                                                              \
+  (add)->hh.hashv = (hashval);                                                   \
+  (add)->hh.key = (char*) (keyptr);                                              \
+  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
+  if (!(head)) {                                                                 \
+    (add)->hh.next = NULL;                                                       \
+    (add)->hh.prev = NULL;                                                       \
+    (head) = (add);                                                              \
+    HASH_MAKE_TABLE(hh, head);                                                   \
+  } else {                                                                       \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_APPEND_LIST(hh, head, add);                                             \
+  }                                                                              \
+  (head)->hh.tbl->num_items++;                                                   \
+  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
+  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh);                 \
+  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
+  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
+  HASH_FSCK(hh, head);                                                           \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do {                                                                             \
+  unsigned _ha_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \
+do {                                                                             \
+  bkt = ((hashv) & ((num_bkts) - 1U));                                           \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ *  HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr)                                              \
+do {                                                                             \
+    struct UT_hash_handle *_hd_hh_del;                                           \
+    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
+        uthash_free((head)->hh.tbl->buckets,                                     \
+                    (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+        HASH_BLOOM_FREE((head)->hh.tbl);                                         \
+        uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                      \
+        head = NULL;                                                             \
+    } else {                                                                     \
+        unsigned _hd_bkt;                                                        \
+        _hd_hh_del = &((delptr)->hh);                                            \
+        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
+            (head)->hh.tbl->tail =                                               \
+                (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +               \
+                (head)->hh.tbl->hho);                                            \
+        }                                                                        \
+        if ((delptr)->hh.prev != NULL) {                                         \
+            ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +                  \
+                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
+        } else {                                                                 \
+            DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
+        }                                                                        \
+        if (_hd_hh_del->next != NULL) {                                          \
+            ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next +                     \
+                    (head)->hh.tbl->hho))->prev =                                \
+                    _hd_hh_del->prev;                                            \
+        }                                                                        \
+        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
+        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
+        (head)->hh.tbl->num_items--;                                             \
+    }                                                                            \
+    HASH_FSCK(hh,head);                                                          \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                          \
+    HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add)                                          \
+    HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out)                                          \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                          \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out)                                          \
+    HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add)                                          \
+    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr)                                                    \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head)                                                       \
+do {                                                                             \
+    struct UT_hash_handle *_thh;                                                 \
+    if (head) {                                                                  \
+        unsigned _bkt_i;                                                         \
+        unsigned _count;                                                         \
+        char *_prev;                                                             \
+        _count = 0;                                                              \
+        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
+            unsigned _bkt_count = 0;                                             \
+            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
+            _prev = NULL;                                                        \
+            while (_thh) {                                                       \
+               if (_prev != (char*)(_thh->hh_prev)) {                            \
+                   HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
+                    _thh->hh_prev, _prev );                                      \
+               }                                                                 \
+               _bkt_count++;                                                     \
+               _prev = (char*)(_thh);                                            \
+               _thh = _thh->hh_next;                                             \
+            }                                                                    \
+            _count += _bkt_count;                                                \
+            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
+               HASH_OOPS("invalid bucket count %u, actual %u\n",                 \
+                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
+            }                                                                    \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid hh item count %u, actual %u\n",                   \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+        /* traverse hh in app order; check next/prev integrity, count */         \
+        _count = 0;                                                              \
+        _prev = NULL;                                                            \
+        _thh =  &(head)->hh;                                                     \
+        while (_thh) {                                                           \
+           _count++;                                                             \
+           if (_prev !=(char*)(_thh->prev)) {                                    \
+              HASH_OOPS("invalid prev %p, actual %p\n",                          \
+                    _thh->prev, _prev );                                         \
+           }                                                                     \
+           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
+           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
+                                  (head)->hh.tbl->hho) : NULL );                 \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid app item count %u, actual %u\n",                  \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+    }                                                                            \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
+do {                                                                             \
+    unsigned _klen = fieldlen;                                                   \
+    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
+    write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                      \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _hb_keylen=(unsigned)keylen;                                          \
+  const unsigned char *_hb_key=(const unsigned char*)(key);                      \
+  (hashv) = 0;                                                                   \
+  while (_hb_keylen-- != 0U) {                                                   \
+      (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                         \
+  }                                                                              \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _sx_i;                                                                \
+  const unsigned char *_hs_key=(const unsigned char*)(key);                      \
+  hashv = 0;                                                                     \
+  for(_sx_i=0; _sx_i < keylen; _sx_i++) {                                        \
+      hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                     \
+  }                                                                              \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _fn_i;                                                                \
+  const unsigned char *_hf_key=(const unsigned char*)(key);                      \
+  hashv = 2166136261U;                                                           \
+  for(_fn_i=0; _fn_i < keylen; _fn_i++) {                                        \
+      hashv = hashv ^ _hf_key[_fn_i];                                            \
+      hashv = hashv * 16777619U;                                                 \
+  }                                                                              \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _ho_i;                                                                \
+  const unsigned char *_ho_key=(const unsigned char*)(key);                      \
+  hashv = 0;                                                                     \
+  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
+      hashv += _ho_key[_ho_i];                                                   \
+      hashv += (hashv << 10);                                                    \
+      hashv ^= (hashv >> 6);                                                     \
+  }                                                                              \
+  hashv += (hashv << 3);                                                         \
+  hashv ^= (hashv >> 11);                                                        \
+  hashv += (hashv << 15);                                                        \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c)                                                      \
+do {                                                                             \
+  a -= b; a -= c; a ^= ( c >> 13 );                                              \
+  b -= c; b -= a; b ^= ( a << 8 );                                               \
+  c -= a; c -= b; c ^= ( b >> 13 );                                              \
+  a -= b; a -= c; a ^= ( c >> 12 );                                              \
+  b -= c; b -= a; b ^= ( a << 16 );                                              \
+  c -= a; c -= b; c ^= ( b >> 5 );                                               \
+  a -= b; a -= c; a ^= ( c >> 3 );                                               \
+  b -= c; b -= a; b ^= ( a << 10 );                                              \
+  c -= a; c -= b; c ^= ( b >> 15 );                                              \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _hj_i,_hj_j,_hj_k;                                                    \
+  unsigned const char *_hj_key=(unsigned const char*)(key);                      \
+  hashv = 0xfeedbeefu;                                                           \
+  _hj_i = _hj_j = 0x9e3779b9u;                                                   \
+  _hj_k = (unsigned)(keylen);                                                    \
+  while (_hj_k >= 12U) {                                                         \
+    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
+        + ( (unsigned)_hj_key[2] << 16 )                                         \
+        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
+    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
+        + ( (unsigned)_hj_key[6] << 16 )                                         \
+        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
+    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
+        + ( (unsigned)_hj_key[10] << 16 )                                        \
+        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
+                                                                                 \
+     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
+                                                                                 \
+     _hj_key += 12;                                                              \
+     _hj_k -= 12U;                                                               \
+  }                                                                              \
+  hashv += (unsigned)(keylen);                                                   \
+  switch ( _hj_k ) {                                                             \
+     case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */        \
+     case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */        \
+     case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */        \
+     case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */        \
+     case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */        \
+     case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */        \
+     case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */        \
+     case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */        \
+     case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */        \
+     case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */        \
+     case 1:  _hj_i += _hj_key[0];                                               \
+  }                                                                              \
+  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
+  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
+                       +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \
+  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \
+                                                                                 \
+  unsigned _sfh_rem = _sfh_len & 3U;                                             \
+  _sfh_len >>= 2;                                                                \
+  hashv = 0xcafebabeu;                                                           \
+                                                                                 \
+  /* Main loop */                                                                \
+  for (;_sfh_len > 0U; _sfh_len--) {                                             \
+    hashv    += get16bits (_sfh_key);                                            \
+    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \
+    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
+    _sfh_key += 2U*sizeof (uint16_t);                                            \
+    hashv    += hashv >> 11;                                                     \
+  }                                                                              \
+                                                                                 \
+  /* Handle end cases */                                                         \
+  switch (_sfh_rem) {                                                            \
+    case 3: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 16;                                                \
+            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \
+            hashv += hashv >> 11;                                                \
+            break;                                                               \
+    case 2: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 11;                                                \
+            hashv += hashv >> 17;                                                \
+            break;                                                               \
+    case 1: hashv += *_sfh_key;                                                  \
+            hashv ^= hashv << 10;                                                \
+            hashv += hashv >> 1;                                                 \
+  }                                                                              \
+                                                                                 \
+    /* Force "avalanching" of final 127 bits */                                  \
+    hashv ^= hashv << 3;                                                         \
+    hashv += hashv >> 5;                                                         \
+    hashv ^= hashv << 4;                                                         \
+    hashv += hashv >> 17;                                                        \
+    hashv ^= hashv << 25;                                                        \
+    hashv += hashv >> 6;                                                         \
+} while (0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
+ *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__)  || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p)   ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >>  8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p)   ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) <<  8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) :           \
+                            (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+                             (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) :  \
+                                                      MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do {                 \
+  _h ^= _h >> 16;    \
+  _h *= 0x85ebca6bu; \
+  _h ^= _h >> 13;    \
+  _h *= 0xc2b2ae35u; \
+  _h ^= _h >> 16;    \
+} while (0)
+
+#define HASH_MUR(key,keylen,hashv)                                     \
+do {                                                                   \
+  const uint8_t *_mur_data = (const uint8_t*)(key);                    \
+  const int _mur_nblocks = (int)(keylen) / 4;                          \
+  uint32_t _mur_h1 = 0xf88D5353u;                                      \
+  uint32_t _mur_c1 = 0xcc9e2d51u;                                      \
+  uint32_t _mur_c2 = 0x1b873593u;                                      \
+  uint32_t _mur_k1 = 0;                                                \
+  const uint8_t *_mur_tail;                                            \
+  const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
+  int _mur_i;                                                          \
+  for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) {                   \
+    _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i);                        \
+    _mur_k1 *= _mur_c1;                                                \
+    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
+    _mur_k1 *= _mur_c2;                                                \
+                                                                       \
+    _mur_h1 ^= _mur_k1;                                                \
+    _mur_h1 = MUR_ROTL32(_mur_h1,13);                                  \
+    _mur_h1 = (_mur_h1*5U) + 0xe6546b64u;                              \
+  }                                                                    \
+  _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4));          \
+  _mur_k1=0;                                                           \
+  switch((keylen) & 3U) {                                              \
+    case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
+    case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8;  /* FALLTHROUGH */ \
+    case 1: _mur_k1 ^= (uint32_t)_mur_tail[0];                         \
+    _mur_k1 *= _mur_c1;                                                \
+    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
+    _mur_k1 *= _mur_c2;                                                \
+    _mur_h1 ^= _mur_k1;                                                \
+  }                                                                    \
+  _mur_h1 ^= (uint32_t)(keylen);                                       \
+  MUR_FMIX(_mur_h1);                                                   \
+  hashv = _mur_h1;                                                     \
+} while (0)
+#endif  /* HASH_USING_NO_STRICT_ALIASING */
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \
+do {                                                                             \
+  if ((head).hh_head != NULL) {                                                  \
+    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \
+  } else {                                                                       \
+    (out) = NULL;                                                                \
+  }                                                                              \
+  while ((out) != NULL) {                                                        \
+    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
+      if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) {                \
+        break;                                                                   \
+      }                                                                          \
+    }                                                                            \
+    if ((out)->hh.hh_next != NULL) {                                             \
+      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \
+    } else {                                                                     \
+      (out) = NULL;                                                              \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(head,addhh)                                              \
+do {                                                                             \
+ head.count++;                                                                   \
+ (addhh)->hh_next = head.hh_head;                                                \
+ (addhh)->hh_prev = NULL;                                                        \
+ if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); }                \
+ (head).hh_head=addhh;                                                           \
+ if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH))          \
+     && ((addhh)->tbl->noexpand != 1U)) {                                        \
+       HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
+ }                                                                               \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
+    (head).count--;                                                              \
+    if ((head).hh_head == hh_del) {                                              \
+      (head).hh_head = hh_del->hh_next;                                          \
+    }                                                                            \
+    if (hh_del->hh_prev) {                                                       \
+        hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
+    }                                                                            \
+    if (hh_del->hh_next) {                                                       \
+        hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
+    }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ *      ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl)                                                 \
+do {                                                                             \
+    unsigned _he_bkt;                                                            \
+    unsigned _he_bkt_i;                                                          \
+    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
+    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
+    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
+             2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));            \
+    if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
+    memset(_he_new_buckets, 0,                                                   \
+            2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));             \
+    tbl->ideal_chain_maxlen =                                                    \
+       (tbl->num_items >> (tbl->log2_num_buckets+1U)) +                          \
+       (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);        \
+    tbl->nonideal_items = 0;                                                     \
+    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
+    {                                                                            \
+        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
+        while (_he_thh != NULL) {                                                \
+           _he_hh_nxt = _he_thh->hh_next;                                        \
+           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt);           \
+           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
+           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
+             tbl->nonideal_items++;                                              \
+             _he_newbkt->expand_mult = _he_newbkt->count /                       \
+                                        tbl->ideal_chain_maxlen;                 \
+           }                                                                     \
+           _he_thh->hh_prev = NULL;                                              \
+           _he_thh->hh_next = _he_newbkt->hh_head;                               \
+           if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev =     \
+                _he_thh; }                                                       \
+           _he_newbkt->hh_head = _he_thh;                                        \
+           _he_thh = _he_hh_nxt;                                                 \
+        }                                                                        \
+    }                                                                            \
+    uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+    tbl->num_buckets *= 2U;                                                      \
+    tbl->log2_num_buckets++;                                                     \
+    tbl->buckets = _he_new_buckets;                                              \
+    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?         \
+        (tbl->ineff_expands+1U) : 0U;                                            \
+    if (tbl->ineff_expands > 1U) {                                               \
+        tbl->noexpand=1;                                                         \
+        uthash_noexpand_fyi(tbl);                                                \
+    }                                                                            \
+    uthash_expand_fyi(tbl);                                                      \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn)                                                 \
+do {                                                                             \
+  unsigned _hs_i;                                                                \
+  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
+  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
+  if (head != NULL) {                                                            \
+      _hs_insize = 1;                                                            \
+      _hs_looping = 1;                                                           \
+      _hs_list = &((head)->hh);                                                  \
+      while (_hs_looping != 0U) {                                                \
+          _hs_p = _hs_list;                                                      \
+          _hs_list = NULL;                                                       \
+          _hs_tail = NULL;                                                       \
+          _hs_nmerges = 0;                                                       \
+          while (_hs_p != NULL) {                                                \
+              _hs_nmerges++;                                                     \
+              _hs_q = _hs_p;                                                     \
+              _hs_psize = 0;                                                     \
+              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                  \
+                  _hs_psize++;                                                   \
+                  _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?              \
+                          ((void*)((char*)(_hs_q->next) +                        \
+                          (head)->hh.tbl->hho)) : NULL);                         \
+                  if (! (_hs_q) ) { break; }                                     \
+              }                                                                  \
+              _hs_qsize = _hs_insize;                                            \
+              while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\
+                  if (_hs_psize == 0U) {                                         \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) {           \
+                      _hs_e = _hs_p;                                             \
+                      if (_hs_p != NULL){                                        \
+                        _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
+                                ((void*)((char*)(_hs_p->next) +                  \
+                                (head)->hh.tbl->hho)) : NULL);                   \
+                       }                                                         \
+                      _hs_psize--;                                               \
+                  } else if ((                                                   \
+                      cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+                             DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+                             ) <= 0) {                                           \
+                      _hs_e = _hs_p;                                             \
+                      if (_hs_p != NULL){                                        \
+                        _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
+                               ((void*)((char*)(_hs_p->next) +                   \
+                               (head)->hh.tbl->hho)) : NULL);                    \
+                       }                                                         \
+                      _hs_psize--;                                               \
+                  } else {                                                       \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  }                                                              \
+                  if ( _hs_tail != NULL ) {                                      \
+                      _hs_tail->next = ((_hs_e != NULL) ?                        \
+                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
+                  } else {                                                       \
+                      _hs_list = _hs_e;                                          \
+                  }                                                              \
+                  if (_hs_e != NULL) {                                           \
+                  _hs_e->prev = ((_hs_tail != NULL) ?                            \
+                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
+                  }                                                              \
+                  _hs_tail = _hs_e;                                              \
+              }                                                                  \
+              _hs_p = _hs_q;                                                     \
+          }                                                                      \
+          if (_hs_tail != NULL){                                                 \
+            _hs_tail->next = NULL;                                               \
+          }                                                                      \
+          if ( _hs_nmerges <= 1U ) {                                             \
+              _hs_looping=0;                                                     \
+              (head)->hh.tbl->tail = _hs_tail;                                   \
+              DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
+          }                                                                      \
+          _hs_insize *= 2U;                                                      \
+      }                                                                          \
+      HASH_FSCK(hh,head);                                                        \
+ }                                                                               \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
+do {                                                                             \
+  unsigned _src_bkt, _dst_bkt;                                                   \
+  void *_last_elt=NULL, *_elt;                                                   \
+  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
+  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
+  if (src != NULL) {                                                             \
+    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
+      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
+          _src_hh != NULL;                                                       \
+          _src_hh = _src_hh->hh_next) {                                          \
+          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
+          if (cond(_elt)) {                                                      \
+            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
+            _dst_hh->key = _src_hh->key;                                         \
+            _dst_hh->keylen = _src_hh->keylen;                                   \
+            _dst_hh->hashv = _src_hh->hashv;                                     \
+            _dst_hh->prev = _last_elt;                                           \
+            _dst_hh->next = NULL;                                                \
+            if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; }             \
+            if (dst == NULL) {                                                   \
+              DECLTYPE_ASSIGN(dst,_elt);                                         \
+              HASH_MAKE_TABLE(hh_dst,dst);                                       \
+            } else {                                                             \
+              _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
+            }                                                                    \
+            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
+            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
+            (dst)->hh_dst.tbl->num_items++;                                      \
+            _last_elt = _elt;                                                    \
+            _last_elt_hh = _dst_hh;                                              \
+          }                                                                      \
+      }                                                                          \
+    }                                                                            \
+  }                                                                              \
+  HASH_FSCK(hh_dst,dst);                                                         \
+} while (0)
+
+#define HASH_CLEAR(hh,head)                                                      \
+do {                                                                             \
+  if (head != NULL) {                                                            \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
+    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head)=NULL;                                                                 \
+  }                                                                              \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head)                                                   \
+ ((head != NULL) ? (                                                             \
+ (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \
+          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \
+           sizeof(UT_hash_table)                                   +             \
+           (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \
+  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;
+
+   /* expand_mult is normally set to 0. In this situation, the max chain length
+    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+    * the bucket's chain exceeds this length, bucket expansion is triggered).
+    * However, setting expand_mult to a non-zero value delays bucket expansion
+    * (that would be triggered by additions to this particular bucket)
+    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+    * (The multiplier is simply expand_mult+1). The whole idea of this
+    * multiplier is to reduce bucket expansions, since they are expensive, in
+    * situations where we know that a particular bucket tends to be overused.
+    * It is better to let its chain length grow to a longer yet-still-bounded
+    * value, than to do an O(n) bucket expansion too often.
+    */
+   unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets, log2_num_buckets;
+   unsigned num_items;
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+   /* in an ideal situation (all buckets used equally), no bucket would have
+    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+   unsigned ideal_chain_maxlen;
+
+   /* nonideal_items is the number of items in the hash whose chain position
+    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+    * hash distribution; reaching them in a chain traversal takes >ideal steps */
+   unsigned nonideal_items;
+
+   /* ineffective expands occur when a bucket doubling was performed, but
+    * afterward, more than half the items in the hash had nonideal chain
+    * positions. If this happens on two consecutive expansions we inhibit any
+    * further expansion, as it's not helping; this happens when the hash
+    * function isn't a good fit for the key domain. When expansion is inhibited
+    * the hash will still work, albeit no longer in constant time. */
+   unsigned ineff_expands, noexpand;
+
+   uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+   uint8_t *bloom_bv;
+   uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   void *key;                        /* ptr to enclosing struct's key  */
+   unsigned keylen;                  /* enclosing struct's key len     */
+   unsigned hashv;                   /* result of hash-fcn(key)        */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/include/utlist.h b/include/utlist.h
new file mode 100755
index 0000000..9b5534f
--- /dev/null
+++ b/include/utlist.h
@@ -0,0 +1,895 @@
+/*
+Copyright (c) 2007-2016, Troy D. Hanson   http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 2.0.1
+
+#include <assert.h>
+
+/*
+ * This file contains macros to manipulate singly and doubly-linked lists.
+ *
+ * 1. LL_ macros:  singly-linked lists.
+ * 2. DL_ macros:  doubly-linked lists.
+ * 3. CDL_ macros: circular doubly-linked lists.
+ *
+ * To use singly-linked lists, your structure must have a "next" pointer.
+ * To use doubly-linked lists, your structure must "prev" and "next" pointers.
+ * Either way, the pointer to the head of the list must be initialized to NULL.
+ *
+ * ----------------.EXAMPLE -------------------------
+ * struct item {
+ *      int id;
+ *      struct item *prev, *next;
+ * }
+ *
+ * struct item *list = NULL:
+ *
+ * int main() {
+ *      struct item *item;
+ *      ... allocate and populate item ...
+ *      DL_APPEND(list, item);
+ * }
+ * --------------------------------------------------
+ *
+ * For doubly-linked lists, the append and delete macros are O(1)
+ * For singly-linked lists, append and delete are O(n) but prepend is O(1)
+ * The sort macro is O(n log(n)) for all types of single/double/circular lists.
+ */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ code), this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER            /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define LDECLTYPE(x) decltype(x)
+#else                     /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__ICCARM__)
+#define NO_DECLTYPE
+#else                      /* GNU, Sun and other compilers */
+#define LDECLTYPE(x) __typeof(x)
+#endif
+
+/* for VS2008 we use some workarounds to get around the lack of decltype,
+ * namely, we always reassign our tmp variable to the list head if we need
+ * to dereference its prev/next pointers, and save/restore the real head.*/
+#ifdef NO_DECLTYPE
+#define IF_NO_DECLTYPE(x) x
+#define LDECLTYPE(x) char*
+#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
+#define _NEXT(elt,list,next) ((char*)((list)->next))
+#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
+/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
+#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
+#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
+#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
+#else
+#define IF_NO_DECLTYPE(x)
+#define _SV(elt,list)
+#define _NEXT(elt,list,next) ((elt)->next)
+#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
+/* #define _PREV(elt,list,prev) ((elt)->prev) */
+#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
+#define _RS(list)
+#define _CASTASGN(a,b) (a)=(b)
+#endif
+
+/******************************************************************************
+ * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort    *
+ * Unwieldy variable names used here to avoid shadowing passed-in variables.  *
+ *****************************************************************************/
+#define LL_SORT(list, cmp)                                                                     \
+    LL_SORT2(list, cmp, next)
+
+#define LL_SORT2(list, cmp, next)                                                              \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;)                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      (list) = NULL;                                                                           \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list);                          \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list);                \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      if (_ls_tail) {                                                                          \
+        _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list);                     \
+      }                                                                                        \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+
+#define DL_SORT(list, cmp)                                                                     \
+    DL_SORT2(list, cmp, prev, next)
+
+#define DL_SORT2(list, cmp, prev, next)                                                        \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;)                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      (list) = NULL;                                                                           \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list);                          \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) {                                \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          } else if ((_ls_qsize == 0) || (!_ls_q)) {                                           \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list);                \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list);                     \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN((list)->prev, _ls_tail);                                                       \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list);                       \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+#define CDL_SORT(list, cmp)                                                                    \
+    CDL_SORT2(list, cmp, prev, next)
+
+#define CDL_SORT2(list, cmp, prev, next)                                                       \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      (list) = NULL;                                                                           \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list);                                                                     \
+          if (_NEXT(_ls_q,list,next) == _ls_oldhead) {                                         \
+            _ls_q = NULL;                                                                      \
+          } else {                                                                             \
+            _ls_q = _NEXT(_ls_q,list,next);                                                    \
+          }                                                                                    \
+          _RS(list);                                                                           \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list);                \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list);                     \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN((list)->prev,_ls_tail);                                                        \
+      _CASTASGN(_tmp,list);                                                                    \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list);                       \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+/******************************************************************************
+ * singly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define LL_PREPEND(head,add)                                                                   \
+    LL_PREPEND2(head,add,next)
+
+#define LL_PREPEND2(head,add,next)                                                             \
+do {                                                                                           \
+  (add)->next = (head);                                                                        \
+  (head) = (add);                                                                              \
+} while (0)
+
+#define LL_CONCAT(head1,head2)                                                                 \
+    LL_CONCAT2(head1,head2,next)
+
+#define LL_CONCAT2(head1,head2,next)                                                           \
+do {                                                                                           \
+  LDECLTYPE(head1) _tmp;                                                                       \
+  if (head1) {                                                                                 \
+    _tmp = (head1);                                                                            \
+    while (_tmp->next) { _tmp = _tmp->next; }                                                  \
+    _tmp->next=(head2);                                                                        \
+  } else {                                                                                     \
+    (head1)=(head2);                                                                           \
+  }                                                                                            \
+} while (0)
+
+#define LL_APPEND(head,add)                                                                    \
+    LL_APPEND2(head,add,next)
+
+#define LL_APPEND2(head,add,next)                                                              \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  (add)->next=NULL;                                                                            \
+  if (head) {                                                                                  \
+    _tmp = (head);                                                                             \
+    while (_tmp->next) { _tmp = _tmp->next; }                                                  \
+    _tmp->next=(add);                                                                          \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+} while (0)
+
+#define LL_DELETE(head,del)                                                                    \
+    LL_DELETE2(head,del,next)
+
+#define LL_DELETE2(head,del,next)                                                              \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    _tmp = (head);                                                                             \
+    while (_tmp->next && (_tmp->next != (del))) {                                              \
+      _tmp = _tmp->next;                                                                       \
+    }                                                                                          \
+    if (_tmp->next) {                                                                          \
+      _tmp->next = (del)->next;                                                                \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+#define LL_COUNT(head,el,counter)                                                              \
+    LL_COUNT2(head,el,counter,next)                                                            \
+
+#define LL_COUNT2(head,el,counter,next)                                                        \
+do {                                                                                           \
+  (counter) = 0;                                                                               \
+  LL_FOREACH2(head,el,next) { ++(counter); }                                                   \
+} while (0)
+
+#define LL_FOREACH(head,el)                                                                    \
+    LL_FOREACH2(head,el,next)
+
+#define LL_FOREACH2(head,el,next)                                                              \
+    for ((el) = (head); el; (el) = (el)->next)
+
+#define LL_FOREACH_SAFE(head,el,tmp)                                                           \
+    LL_FOREACH_SAFE2(head,el,tmp,next)
+
+#define LL_FOREACH_SAFE2(head,el,tmp,next)                                                     \
+  for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))
+
+#define LL_SEARCH_SCALAR(head,out,field,val)                                                   \
+    LL_SEARCH_SCALAR2(head,out,field,val,next)
+
+#define LL_SEARCH_SCALAR2(head,out,field,val,next)                                             \
+do {                                                                                           \
+    LL_FOREACH2(head,out,next) {                                                               \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while (0)
+
+#define LL_SEARCH(head,out,elt,cmp)                                                            \
+    LL_SEARCH2(head,out,elt,cmp,next)
+
+#define LL_SEARCH2(head,out,elt,cmp,next)                                                      \
+do {                                                                                           \
+    LL_FOREACH2(head,out,next) {                                                               \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while (0)
+
+#define LL_REPLACE_ELEM2(head, el, add, next)                                                  \
+do {                                                                                           \
+ LDECLTYPE(head) _tmp;                                                                         \
+ assert((head) != NULL);                                                                       \
+ assert((el) != NULL);                                                                         \
+ assert((add) != NULL);                                                                        \
+ (add)->next = (el)->next;                                                                     \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+ } else {                                                                                      \
+  _tmp = (head);                                                                               \
+  while (_tmp->next && (_tmp->next != (el))) {                                                 \
+   _tmp = _tmp->next;                                                                          \
+  }                                                                                            \
+  if (_tmp->next) {                                                                            \
+    _tmp->next = (add);                                                                        \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)
+
+#define LL_REPLACE_ELEM(head, el, add)                                                         \
+    LL_REPLACE_ELEM2(head, el, add, next)
+
+#define LL_PREPEND_ELEM2(head, el, add, next)                                                  \
+do {                                                                                           \
+ if (el) {                                                                                     \
+  LDECLTYPE(head) _tmp;                                                                        \
+  assert((head) != NULL);                                                                      \
+  assert((add) != NULL);                                                                       \
+  (add)->next = (el);                                                                          \
+  if ((head) == (el)) {                                                                        \
+   (head) = (add);                                                                             \
+  } else {                                                                                     \
+   _tmp = (head);                                                                              \
+   while (_tmp->next && (_tmp->next != (el))) {                                                \
+    _tmp = _tmp->next;                                                                         \
+   }                                                                                           \
+   if (_tmp->next) {                                                                           \
+     _tmp->next = (add);                                                                       \
+   }                                                                                           \
+  }                                                                                            \
+ } else {                                                                                      \
+  LL_APPEND2(head, add, next);                                                                 \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+#define LL_PREPEND_ELEM(head, el, add)                                                         \
+    LL_PREPEND_ELEM2(head, el, add, next)
+
+#define LL_APPEND_ELEM2(head, el, add, next)                                                   \
+do {                                                                                           \
+ if (el) {                                                                                     \
+  assert((head) != NULL);                                                                      \
+  assert((add) != NULL);                                                                       \
+  (add)->next = (el)->next;                                                                    \
+  (el)->next = (add);                                                                          \
+ } else {                                                                                      \
+  LL_PREPEND2(head, add, next);                                                                \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+#define LL_APPEND_ELEM(head, el, add)                                                          \
+    LL_APPEND_ELEM2(head, el, add, next)
+
+#ifdef NO_DECLTYPE
+/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */
+
+#undef LL_CONCAT2
+#define LL_CONCAT2(head1,head2,next)                                                           \
+do {                                                                                           \
+  char *_tmp;                                                                                  \
+  if (head1) {                                                                                 \
+    _tmp = (char*)(head1);                                                                     \
+    while ((head1)->next) { (head1) = (head1)->next; }                                         \
+    (head1)->next = (head2);                                                                   \
+    _RS(head1);                                                                                \
+  } else {                                                                                     \
+    (head1)=(head2);                                                                           \
+  }                                                                                            \
+} while (0)
+
+#undef LL_APPEND2
+#define LL_APPEND2(head,add,next)                                                              \
+do {                                                                                           \
+  if (head) {                                                                                  \
+    (add)->next = head;     /* use add->next as a temp variable */                             \
+    while ((add)->next->next) { (add)->next = (add)->next->next; }                             \
+    (add)->next->next=(add);                                                                   \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+  (add)->next=NULL;                                                                            \
+} while (0)
+
+#undef LL_DELETE2
+#define LL_DELETE2(head,del,next)                                                              \
+do {                                                                                           \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    char *_tmp = (char*)(head);                                                                \
+    while ((head)->next && ((head)->next != (del))) {                                          \
+      (head) = (head)->next;                                                                   \
+    }                                                                                          \
+    if ((head)->next) {                                                                        \
+      (head)->next = ((del)->next);                                                            \
+    }                                                                                          \
+    _RS(head);                                                                                 \
+  }                                                                                            \
+} while (0)
+
+#undef LL_REPLACE_ELEM2
+#define LL_REPLACE_ELEM2(head, el, add, next)                                                  \
+do {                                                                                           \
+  assert((head) != NULL);                                                                      \
+  assert((el) != NULL);                                                                        \
+  assert((add) != NULL);                                                                       \
+  if ((head) == (el)) {                                                                        \
+    (head) = (add);                                                                            \
+  } else {                                                                                     \
+    (add)->next = head;                                                                        \
+    while ((add)->next->next && ((add)->next->next != (el))) {                                 \
+      (add)->next = (add)->next->next;                                                         \
+    }                                                                                          \
+    if ((add)->next->next) {                                                                   \
+      (add)->next->next = (add);                                                               \
+    }                                                                                          \
+  }                                                                                            \
+  (add)->next = (el)->next;                                                                    \
+} while (0)
+
+#undef LL_PREPEND_ELEM2
+#define LL_PREPEND_ELEM2(head, el, add, next)                                                  \
+do {                                                                                           \
+  if (el) {                                                                                    \
+    assert((head) != NULL);                                                                    \
+    assert((add) != NULL);                                                                     \
+    if ((head) == (el)) {                                                                      \
+      (head) = (add);                                                                          \
+    } else {                                                                                   \
+      (add)->next = (head);                                                                    \
+      while ((add)->next->next && ((add)->next->next != (el))) {                               \
+        (add)->next = (add)->next->next;                                                       \
+      }                                                                                        \
+      if ((add)->next->next) {                                                                 \
+        (add)->next->next = (add);                                                             \
+      }                                                                                        \
+    }                                                                                          \
+    (add)->next = (el);                                                                        \
+  } else {                                                                                     \
+    LL_APPEND2(head, add, next);                                                               \
+  }                                                                                            \
+} while (0)                                                                                    \
+
+#endif /* NO_DECLTYPE */
+
+/******************************************************************************
+ * doubly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define DL_PREPEND(head,add)                                                                   \
+    DL_PREPEND2(head,add,prev,next)
+
+#define DL_PREPEND2(head,add,prev,next)                                                        \
+do {                                                                                           \
+ (add)->next = (head);                                                                         \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (head)->prev = (add);                                                                       \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+ }                                                                                             \
+ (head) = (add);                                                                               \
+} while (0)
+
+#define DL_APPEND(head,add)                                                                    \
+    DL_APPEND2(head,add,prev,next)
+
+#define DL_APPEND2(head,add,prev,next)                                                         \
+do {                                                                                           \
+  if (head) {                                                                                  \
+      (add)->prev = (head)->prev;                                                              \
+      (head)->prev->next = (add);                                                              \
+      (head)->prev = (add);                                                                    \
+      (add)->next = NULL;                                                                      \
+  } else {                                                                                     \
+      (head)=(add);                                                                            \
+      (head)->prev = (head);                                                                   \
+      (head)->next = NULL;                                                                     \
+  }                                                                                            \
+} while (0)
+
+#define DL_CONCAT(head1,head2)                                                                 \
+    DL_CONCAT2(head1,head2,prev,next)
+
+#define DL_CONCAT2(head1,head2,prev,next)                                                      \
+do {                                                                                           \
+  LDECLTYPE(head1) _tmp;                                                                       \
+  if (head2) {                                                                                 \
+    if (head1) {                                                                               \
+        _CASTASGN(_tmp, (head2)->prev);                                                        \
+        (head2)->prev = (head1)->prev;                                                         \
+        (head1)->prev->next = (head2);                                                         \
+        _CASTASGN((head1)->prev, _tmp);                                                        \
+    } else {                                                                                   \
+        (head1)=(head2);                                                                       \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+#define DL_DELETE(head,del)                                                                    \
+    DL_DELETE2(head,del,prev,next)
+
+#define DL_DELETE2(head,del,prev,next)                                                         \
+do {                                                                                           \
+  assert((del)->prev != NULL);                                                                 \
+  if ((del)->prev == (del)) {                                                                  \
+      (head)=NULL;                                                                             \
+  } else if ((del)==(head)) {                                                                  \
+      (del)->next->prev = (del)->prev;                                                         \
+      (head) = (del)->next;                                                                    \
+  } else {                                                                                     \
+      (del)->prev->next = (del)->next;                                                         \
+      if ((del)->next) {                                                                       \
+          (del)->next->prev = (del)->prev;                                                     \
+      } else {                                                                                 \
+          (head)->prev = (del)->prev;                                                          \
+      }                                                                                        \
+  }                                                                                            \
+} while (0)
+
+#define DL_COUNT(head,el,counter)                                                              \
+    DL_COUNT2(head,el,counter,next)                                                            \
+
+#define DL_COUNT2(head,el,counter,next)                                                        \
+do {                                                                                           \
+  (counter) = 0;                                                                               \
+  DL_FOREACH2(head,el,next) { ++(counter); }                                                   \
+} while (0)
+
+#define DL_FOREACH(head,el)                                                                    \
+    DL_FOREACH2(head,el,next)
+
+#define DL_FOREACH2(head,el,next)                                                              \
+    for ((el) = (head); el; (el) = (el)->next)
+
+/* this version is safe for deleting the elements during iteration */
+#define DL_FOREACH_SAFE(head,el,tmp)                                                           \
+    DL_FOREACH_SAFE2(head,el,tmp,next)
+
+#define DL_FOREACH_SAFE2(head,el,tmp,next)                                                     \
+  for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))
+
+/* these are identical to their singly-linked list counterparts */
+#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
+#define DL_SEARCH LL_SEARCH
+#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
+#define DL_SEARCH2 LL_SEARCH2
+
+#define DL_REPLACE_ELEM2(head, el, add, prev, next)                                            \
+do {                                                                                           \
+ assert((head) != NULL);                                                                       \
+ assert((el) != NULL);                                                                         \
+ assert((add) != NULL);                                                                        \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+  (add)->next = (el)->next;                                                                    \
+  if ((el)->next == NULL) {                                                                    \
+   (add)->prev = (add);                                                                        \
+  } else {                                                                                     \
+   (add)->prev = (el)->prev;                                                                   \
+   (add)->next->prev = (add);                                                                  \
+  }                                                                                            \
+ } else {                                                                                      \
+  (add)->next = (el)->next;                                                                    \
+  (add)->prev = (el)->prev;                                                                    \
+  (add)->prev->next = (add);                                                                   \
+  if ((el)->next == NULL) {                                                                    \
+   (head)->prev = (add);                                                                       \
+  } else {                                                                                     \
+   (add)->next->prev = (add);                                                                  \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)
+
+#define DL_REPLACE_ELEM(head, el, add)                                                         \
+    DL_REPLACE_ELEM2(head, el, add, prev, next)
+
+#define DL_PREPEND_ELEM2(head, el, add, prev, next)                                            \
+do {                                                                                           \
+ if (el) {                                                                                     \
+  assert((head) != NULL);                                                                      \
+  assert((add) != NULL);                                                                       \
+  (add)->next = (el);                                                                          \
+  (add)->prev = (el)->prev;                                                                    \
+  (el)->prev = (add);                                                                          \
+  if ((head) == (el)) {                                                                        \
+   (head) = (add);                                                                             \
+  } else {                                                                                     \
+   (add)->prev->next = (add);                                                                  \
+  }                                                                                            \
+ } else {                                                                                      \
+  DL_APPEND2(head, add, prev, next);                                                           \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+#define DL_PREPEND_ELEM(head, el, add)                                                         \
+    DL_PREPEND_ELEM2(head, el, add, prev, next)
+
+#define DL_APPEND_ELEM2(head, el, add, prev, next)                                             \
+do {                                                                                           \
+ if (el) {                                                                                     \
+  assert((head) != NULL);                                                                      \
+  assert((add) != NULL);                                                                       \
+  (add)->next = (el)->next;                                                                    \
+  (add)->prev = (el);                                                                          \
+  (el)->next = (add);                                                                          \
+  if ((add)->next) {                                                                           \
+   (add)->next->prev = (add);                                                                  \
+  } else {                                                                                     \
+   (head)->prev = (add);                                                                       \
+  }                                                                                            \
+ } else {                                                                                      \
+  DL_PREPEND2(head, add, prev, next);                                                          \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+#define DL_APPEND_ELEM(head, el, add)                                                          \
+   DL_APPEND_ELEM2(head, el, add, prev, next)
+
+/******************************************************************************
+ * circular doubly linked list macros                                         *
+ *****************************************************************************/
+#define CDL_APPEND(head,add)                                                                   \
+    CDL_APPEND2(head,add,prev,next)
+
+#define CDL_APPEND2(head,add,prev,next)                                                        \
+do {                                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (add)->next = (head);                                                                       \
+   (head)->prev = (add);                                                                       \
+   (add)->prev->next = (add);                                                                  \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+   (add)->next = (add);                                                                        \
+   (head) = (add);                                                                             \
+ }                                                                                             \
+} while (0)
+
+#define CDL_PREPEND(head,add)                                                                  \
+    CDL_PREPEND2(head,add,prev,next)
+
+#define CDL_PREPEND2(head,add,prev,next)                                                       \
+do {                                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (add)->next = (head);                                                                       \
+   (head)->prev = (add);                                                                       \
+   (add)->prev->next = (add);                                                                  \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+   (add)->next = (add);                                                                        \
+ }                                                                                             \
+ (head) = (add);                                                                               \
+} while (0)
+
+#define CDL_DELETE(head,del)                                                                   \
+    CDL_DELETE2(head,del,prev,next)
+
+#define CDL_DELETE2(head,del,prev,next)                                                        \
+do {                                                                                           \
+  if (((head)==(del)) && ((head)->next == (head))) {                                           \
+      (head) = NULL;                                                                           \
+  } else {                                                                                     \
+     (del)->next->prev = (del)->prev;                                                          \
+     (del)->prev->next = (del)->next;                                                          \
+     if ((del) == (head)) (head)=(del)->next;                                                  \
+  }                                                                                            \
+} while (0)
+
+#define CDL_COUNT(head,el,counter)                                                             \
+    CDL_COUNT2(head,el,counter,next)                                                           \
+
+#define CDL_COUNT2(head, el, counter,next)                                                     \
+do {                                                                                           \
+  (counter) = 0;                                                                               \
+  CDL_FOREACH2(head,el,next) { ++(counter); }                                                  \
+} while (0)
+
+#define CDL_FOREACH(head,el)                                                                   \
+    CDL_FOREACH2(head,el,next)
+
+#define CDL_FOREACH2(head,el,next)                                                             \
+    for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next))
+
+#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2)                                                    \
+    CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
+
+#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)                                         \
+  for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL;                                   \
+       (el) && ((tmp2) = (el)->next, 1);                                                       \
+       (el) = ((el) == (tmp1) ? NULL : (tmp2)))
+
+#define CDL_SEARCH_SCALAR(head,out,field,val)                                                  \
+    CDL_SEARCH_SCALAR2(head,out,field,val,next)
+
+#define CDL_SEARCH_SCALAR2(head,out,field,val,next)                                            \
+do {                                                                                           \
+    CDL_FOREACH2(head,out,next) {                                                              \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while (0)
+
+#define CDL_SEARCH(head,out,elt,cmp)                                                           \
+    CDL_SEARCH2(head,out,elt,cmp,next)
+
+#define CDL_SEARCH2(head,out,elt,cmp,next)                                                     \
+do {                                                                                           \
+    CDL_FOREACH2(head,out,next) {                                                              \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while (0)
+
+#define CDL_REPLACE_ELEM2(head, el, add, prev, next)                                           \
+do {                                                                                           \
+ assert((head) != NULL);                                                                       \
+ assert((el) != NULL);                                                                         \
+ assert((add) != NULL);                                                                        \
+ if ((el)->next == (el)) {                                                                     \
+  (add)->next = (add);                                                                         \
+  (add)->prev = (add);                                                                         \
+  (head) = (add);                                                                              \
+ } else {                                                                                      \
+  (add)->next = (el)->next;                                                                    \
+  (add)->prev = (el)->prev;                                                                    \
+  (add)->next->prev = (add);                                                                   \
+  (add)->prev->next = (add);                                                                   \
+  if ((head) == (el)) {                                                                        \
+   (head) = (add);                                                                             \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)
+
+#define CDL_REPLACE_ELEM(head, el, add)                                                        \
+    CDL_REPLACE_ELEM2(head, el, add, prev, next)
+
+#define CDL_PREPEND_ELEM2(head, el, add, prev, next)                                           \
+do {                                                                                           \
+  if (el) {                                                                                    \
+    assert((head) != NULL);                                                                    \
+    assert((add) != NULL);                                                                     \
+    (add)->next = (el);                                                                        \
+    (add)->prev = (el)->prev;                                                                  \
+    (el)->prev = (add);                                                                        \
+    (add)->prev->next = (add);                                                                 \
+    if ((head) == (el)) {                                                                      \
+      (head) = (add);                                                                          \
+    }                                                                                          \
+  } else {                                                                                     \
+    CDL_APPEND2(head, add, prev, next);                                                        \
+  }                                                                                            \
+} while (0)
+
+#define CDL_PREPEND_ELEM(head, el, add)                                                        \
+    CDL_PREPEND_ELEM2(head, el, add, prev, next)
+
+#define CDL_APPEND_ELEM2(head, el, add, prev, next)                                            \
+do {                                                                                           \
+ if (el) {                                                                                     \
+  assert((head) != NULL);                                                                      \
+  assert((add) != NULL);                                                                       \
+  (add)->next = (el)->next;                                                                    \
+  (add)->prev = (el);                                                                          \
+  (el)->next = (add);                                                                          \
+  (add)->next->prev = (add);                                                                   \
+ } else {                                                                                      \
+  CDL_PREPEND2(head, add, prev, next);                                                         \
+ }                                                                                             \
+} while (0)
+
+#define CDL_APPEND_ELEM(head, el, add)                                                         \
+    CDL_APPEND_ELEM2(head, el, add, prev, next)
+
+#endif /* UTLIST_H */
diff --git a/src/circular_buffer.h b/src/circular_buffer.h
new file mode 100755
index 0000000..a7d5d80
--- /dev/null
+++ b/src/circular_buffer.h
@@ -0,0 +1,54 @@
+//
+//  CircularBuffer.h
+//
+//  Created by 罗亮富(Roen)zxllf23@163.com on 14-1-14.
+//  Copyright (c) 2014å¹´ All rights reserved.
+//
+//  Note: Edited by Kurotych Anatolii
+
+#ifndef YYDJ_Roen_CircularBuffer_h
+#define YYDJ_Roen_CircularBuffer_h
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+/*
+ A circular buffer(circular queue, cyclic buffer or ring buffer), is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams. visit https://en.wikipedia.org/wiki/Circular_buffer to see more information.
+ */
+
+typedef struct s_circularBuffer* CircularBuffer;
+
+// Construct CircularBuffer with ‘size' in byte. You must call CircularBufferFree() in balance for destruction.
+extern CircularBuffer CircularBufferCreate(size_t size);
+
+// Destruct CircularBuffer
+extern void CircularBufferFree(CircularBuffer cBuf);
+
+// Reset the CircularBuffer
+extern void CircularBufferReset(CircularBuffer cBuf);
+
+//get the capacity of CircularBuffer
+extern size_t CircularBufferGetCapacity(CircularBuffer cBuf);
+
+//get occupied data size of CircularBuffer
+extern size_t CircularBufferGetDataSize(CircularBuffer cBuf);
+
+// Push data to the tail of a circular buffer from 'src' with 'length' size in byte.
+extern void CircularBufferPush(CircularBuffer cBuf,void *src, size_t length);
+
+// Pop data from a circular buffer to 'dataOut'  with wished 'length' size in byte,return the actual data size in byte popped out,which is less or equal to the input 'length parameter.
+extern size_t CircularBufferPop(CircularBuffer cBuf, size_t length, void *dataOut);
+
+// Read data from a circular buffer to 'dataOut'  with wished 'length' size in byte,return the actual data size in byte popped out,which is less or equal to the input 'length parameter.
+extern size_t CircularBufferRead(CircularBuffer cBuf, size_t length, void *dataOut);
+
+#ifdef __unix__
+// Write data from a cicrular buffer to file descriptor. This is non blocking operation ( use flags MSG_DONTWAIT | MSG_NOSIGNAL )
+// if >= 0 return write data count
+// <= 0 reserved for errors
+extern int CircularBufferWriteInSocket(CircularBuffer cBuf, int sockfd);
+#endif
+
+//for test purpose, print the circular buffer's data content by printf(...); the 'hex' parameters indicates that if the data should be printed in asscii string or hex data format.
+extern void CircularBufferPrint(CircularBuffer cBuf, bool hex);
+#endif
diff --git a/src/common/int-util.h b/src/common/int-util.h
new file mode 100755
index 0000000..7cec571
--- /dev/null
+++ b/src/common/int-util.h
@@ -0,0 +1,249 @@
+// Copyright (c) 2014-2017, The Monero Project
+// 
+// All rights reserved.
+// 
+// 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 copyright holder 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+
+#if defined(__ANDROID__)
+#include <byteswap.h>
+#endif
+
+#if defined(__sun) && defined(__SVR4)
+#include <endian.h>
+#endif
+
+#if defined(_MSC_VER)
+#include <stdlib.h>
+
+static inline uint32_t rol32(uint32_t x, int r) {
+  static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers");
+  return _rotl(x, r);
+}
+
+static inline uint64_t rol64(uint64_t x, int r) {
+  return _rotl64(x, r);
+}
+
+#else
+
+static inline uint32_t rol32(uint32_t x, int r) {
+  return (x << (r & 31)) | (x >> (-r & 31));
+}
+
+static inline uint64_t rol64(uint64_t x, int r) {
+  return (x << (r & 63)) | (x >> (-r & 63));
+}
+
+#endif
+
+static inline uint64_t hi_dword(uint64_t val) {
+  return val >> 32;
+}
+
+static inline uint64_t lo_dword(uint64_t val) {
+  return val & 0xFFFFFFFF;
+}
+
+static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) {
+  // multiplier   = ab = a * 2^32 + b
+  // multiplicand = cd = c * 2^32 + d
+  // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
+  uint64_t a = hi_dword(multiplier);
+  uint64_t b = lo_dword(multiplier);
+  uint64_t c = hi_dword(multiplicand);
+  uint64_t d = lo_dword(multiplicand);
+
+  uint64_t ac = a * c;
+  uint64_t ad = a * d;
+  uint64_t bc = b * c;
+  uint64_t bd = b * d;
+
+  uint64_t adbc = ad + bc;
+  uint64_t adbc_carry = adbc < ad ? 1 : 0;
+
+  // multiplier * multiplicand = product_hi * 2^64 + product_lo
+  uint64_t product_lo = bd + (adbc << 32);
+  uint64_t product_lo_carry = product_lo < bd ? 1 : 0;
+  *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry;
+  assert(ac <= *product_hi);
+
+  return product_lo;
+}
+
+static inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) {
+  dividend |= ((uint64_t)*remainder) << 32;
+  *remainder = dividend % divisor;
+  return dividend / divisor;
+}
+
+// Long division with 2^32 base
+static inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
+  uint64_t dividend_dwords[4];
+  uint32_t remainder = 0;
+
+  dividend_dwords[3] = hi_dword(dividend_hi);
+  dividend_dwords[2] = lo_dword(dividend_hi);
+  dividend_dwords[1] = hi_dword(dividend_lo);
+  dividend_dwords[0] = lo_dword(dividend_lo);
+
+  *quotient_hi  = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32;
+  *quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder);
+  *quotient_lo  = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32;
+  *quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder);
+
+  return remainder;
+}
+
+#define IDENT32(x) ((uint32_t) (x))
+#define IDENT64(x) ((uint64_t) (x))
+
+#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \
+  (((uint32_t) (x) & 0x0000ff00) <<  8) | \
+  (((uint32_t) (x) & 0x00ff0000) >>  8) | \
+  (((uint32_t) (x) & 0xff000000) >> 24))
+#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \
+  (((uint64_t) (x) & 0x000000000000ff00) << 40) | \
+  (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \
+  (((uint64_t) (x) & 0x00000000ff000000) <<  8) | \
+  (((uint64_t) (x) & 0x000000ff00000000) >>  8) | \
+  (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \
+  (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \
+  (((uint64_t) (x) & 0xff00000000000000) >> 56))
+
+static inline uint32_t ident32(uint32_t x) { return x; }
+static inline uint64_t ident64(uint64_t x) { return x; }
+
+#ifndef __OpenBSD__
+#  if defined(__ANDROID__) && defined(__swap32) && !defined(swap32)
+#      define swap32 __swap32
+#  elif !defined(swap32)
+static inline uint32_t swap32(uint32_t x) {
+  x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8);
+  return (x << 16) | (x >> 16);
+}
+#  endif
+#  if defined(__ANDROID__) && defined(__swap64) && !defined(swap64)
+#      define swap64 __swap64
+#  elif !defined(swap64)
+static inline uint64_t swap64(uint64_t x) {
+  x = ((x & 0x00ff00ff00ff00ff) <<  8) | ((x & 0xff00ff00ff00ff00) >>  8);
+  x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16);
+  return (x << 32) | (x >> 32);
+}
+#  endif
+#endif /* __OpenBSD__ */
+
+#if defined(__GNUC__)
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { }
+#undef UNUSED
+
+static inline void mem_inplace_swap32(void *mem, size_t n) {
+  size_t i;
+  for (i = 0; i < n; i++) {
+    ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]);
+  }
+}
+static inline void mem_inplace_swap64(void *mem, size_t n) {
+  size_t i;
+  for (i = 0; i < n; i++) {
+    ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]);
+  }
+}
+
+static inline void memcpy_ident32(void *dst, const void *src, size_t n) {
+  memcpy(dst, src, 4 * n);
+}
+static inline void memcpy_ident64(void *dst, const void *src, size_t n) {
+  memcpy(dst, src, 8 * n);
+}
+
+static inline void memcpy_swap32(void *dst, const void *src, size_t n) {
+  size_t i;
+  for (i = 0; i < n; i++) {
+    ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]);
+  }
+}
+static inline void memcpy_swap64(void *dst, const void *src, size_t n) {
+  size_t i;
+  for (i = 0; i < n; i++) {
+    ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]);
+  }
+}
+
+#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
+static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled");
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define SWAP32LE IDENT32
+#define SWAP32BE SWAP32
+#define swap32le ident32
+#define swap32be swap32
+#define mem_inplace_swap32le mem_inplace_ident
+#define mem_inplace_swap32be mem_inplace_swap32
+#define memcpy_swap32le memcpy_ident32
+#define memcpy_swap32be memcpy_swap32
+#define SWAP64LE IDENT64
+#define SWAP64BE SWAP64
+#define swap64le ident64
+#define swap64be swap64
+#define mem_inplace_swap64le mem_inplace_ident
+#define mem_inplace_swap64be mem_inplace_swap64
+#define memcpy_swap64le memcpy_ident64
+#define memcpy_swap64be memcpy_swap64
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define SWAP32BE IDENT32
+#define SWAP32LE SWAP32
+#define swap32be ident32
+#define swap32le swap32
+#define mem_inplace_swap32be mem_inplace_ident
+#define mem_inplace_swap32le mem_inplace_swap32
+#define memcpy_swap32be memcpy_ident32
+#define memcpy_swap32le memcpy_swap32
+#define SWAP64BE IDENT64
+#define SWAP64LE SWAP64
+#define swap64be ident64
+#define swap64le swap64
+#define mem_inplace_swap64be mem_inplace_ident
+#define mem_inplace_swap64le mem_inplace_swap64
+#define memcpy_swap64be memcpy_ident64
+#define memcpy_swap64le memcpy_swap64
+#endif
diff --git a/src/common/memwipe.c b/src/common/memwipe.c
new file mode 100755
index 0000000..da7e9f3
--- /dev/null
+++ b/src/common/memwipe.c
@@ -0,0 +1,106 @@
+// Copyright (c) 2017, The Monero Project
+// 
+// All rights reserved.
+// 
+// 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 copyright holder 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file Copyright (c) 2009-2015 The Bitcoin Core developers
+
+#define __STDC_WANT_LIB_EXT1__ 1
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_EXPLICIT_BZERO
+#include <strings.h>
+#endif
+#include "memwipe.h"
+
+#if defined(_MSC_VER)
+#define SCARECROW \
+    __asm;
+#else
+#define SCARECROW \
+    __asm__ __volatile__("" : : "r"(ptr) : "memory");
+#endif
+
+#ifdef HAVE_MEMSET_S
+
+void *memwipe(void *ptr, size_t n)
+{
+  if (memset_s(ptr, n, 0, n))
+  {
+    abort();
+  }
+  SCARECROW // might as well...
+  return ptr;
+}
+
+#elif defined HAVE_EXPLICIT_BZERO
+
+void *memwipe(void *ptr, size_t n)
+{
+  explicit_bzero(ptr, n);
+  SCARECROW
+  return ptr;
+}
+
+#else
+
+/* The memory_cleanse implementation is taken from Bitcoin */
+
+/* Compilers have a bad habit of removing "superfluous" memset calls that
+ * are trying to zero memory. For example, when memset()ing a buffer and
+ * then free()ing it, the compiler might decide that the memset is
+ * unobservable and thus can be removed.
+ *
+ * Previously we used OpenSSL which tried to stop this by a) implementing
+ * memset in assembly on x86 and b) putting the function in its own file
+ * for other platforms.
+ *
+ * This change removes those tricks in favour of using asm directives to
+ * scare the compiler away. As best as our compiler folks can tell, this is
+ * sufficient and will continue to be so.
+ *
+ * Adam Langley <agl@google.com>
+ * Commit: ad1907fe73334d6c696c8539646c21b11178f20f
+ * BoringSSL (LICENSE: ISC)
+ */
+static void memory_cleanse(void *ptr, size_t len)
+{
+    memset(ptr, 0, len);
+
+    /* As best as we can tell, this is sufficient to break any optimisations that
+       might try to eliminate "superfluous" memsets. If there's an easy way to
+       detect memset_s, it would be better to use that. */
+    SCARECROW
+}
+
+void *memwipe(void *ptr, size_t n)
+{
+  memory_cleanse(ptr, n);
+  SCARECROW
+  return ptr;
+}
+
+#endif
diff --git a/src/common/memwipe.h b/src/common/memwipe.h
new file mode 100755
index 0000000..46cd131
--- /dev/null
+++ b/src/common/memwipe.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, The Monero Project
+// Copyrught (c) 2018, DapCash Project
+// 
+// All rights reserved.
+// 
+// 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 copyright holder 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#ifndef _MEMWIPE_H_
+#define _MEMWIPE_H_
+
+void *memwipe(void *src, size_t n);
+
+#endif
\ No newline at end of file
diff --git a/src/core.pri b/src/core.pri
new file mode 100755
index 0000000..14172b6
--- /dev/null
+++ b/src/core.pri
@@ -0,0 +1,31 @@
+unix {
+    include(unix/unix.pri)
+}
+darwin {
+    include(darwin/darwin.pri)
+}
+
+HEADERS += $$PWD/dap_common.h \
+    $$PWD/dap_config.h \
+    $$PWD/dap_math_ops.h \
+    $$PWD/uthash.h \
+    $$PWD/utlist.h \
+    $$PWD/dap_math_ops.h \
+    $$PWD/dap_file_utils.h \
+    $$PWD/circular_buffer.h \
+    $$PWD/dap_circular_buffer.h \
+    $$PWD/dap_list.h \
+    $$PWD/dap_module.h \
+    $$PWD/dap_strfuncs.h \
+    $$PWD/dap_string.h
+
+SOURCES += $$PWD/dap_common.c \
+    $$PWD/dap_config.c \
+    $$PWD/dap_file_utils.c \
+    $$PWD/dap_circular_buffer.c \
+    $$PWD/dap_list.c \
+    $$PWD/dap_module.c \
+    $$PWD/dap_strfuncs.c \
+    $$PWD/dap_string.c
+
+INCLUDEPATH += $$PWD
diff --git a/src/dap_circular_buffer.c b/src/dap_circular_buffer.c
new file mode 100755
index 0000000..703d3a3
--- /dev/null
+++ b/src/dap_circular_buffer.c
@@ -0,0 +1,346 @@
+//
+//  CircularBuffer.c
+//
+//  Created by 罗亮富(Roen zxllf23@163.com) on 14-1-14.
+//  Copyright (c) 2014å¹´ All rights reserved.
+//
+//  Note: Edited by Kurotych Anatolii
+
+
+#include "dap_circular_buffer.h"
+#include <string.h>
+#include <errno.h>
+
+#include "dap_common.h"
+
+#define LOG_TAG "circular_buffer"
+
+struct s_circularBuffer{
+
+    size_t capacity; //capacity bytes size
+    size_t dataSize; //occupied data size
+    size_t tailOffset; //head offset, the oldest byte position offset
+    size_t headOffset; //tail offset, the lastest byte position offset
+    uint8_t *buffer;
+
+};
+
+extern circular_buffer_t circular_buffer_create(size_t size)
+{
+    size_t totalSize = sizeof(struct s_circularBuffer) + size;
+    void *p = malloc(totalSize);
+    circular_buffer_t buffer = (circular_buffer_t)p;
+    buffer->buffer = p + sizeof(struct s_circularBuffer);
+    buffer->capacity = size;
+    circular_buffer_reset(buffer);
+    return buffer;
+}
+
+void circular_buffer_free(circular_buffer_t cBuf)
+{
+    circular_buffer_reset(cBuf);
+    cBuf->capacity = 0;
+    cBuf->dataSize = 0;
+    cBuf->buffer = NULL;
+    free(cBuf);
+}
+
+void circular_buffer_reset(circular_buffer_t cBuf)
+{
+    cBuf->headOffset = -1;
+    cBuf->tailOffset = -1;
+    cBuf->dataSize = 0;
+}
+
+size_t circular_buffer_get_capacity(circular_buffer_t cBuf)
+{
+    return cBuf->capacity;
+}
+
+size_t circular_buffer_get_data_size(circular_buffer_t cBuf)
+{
+    return cBuf->dataSize;
+}
+
+void circular_buffer_push(circular_buffer_t cBuf, const void *src, size_t length)
+{
+    if(length == 0)
+        return;
+
+    size_t writableLen = length;
+    const void *pSrc = src;
+
+    if(writableLen > cBuf->capacity)//in case of size overflow
+    {
+        size_t overFlowLen = writableLen - cBuf->capacity;
+        writableLen = cBuf->capacity;
+        pSrc = src + overFlowLen;
+    }
+
+
+    bool resetHead = false;
+    //in case the circle buffer won't be full after adding the data
+    if(cBuf->tailOffset+writableLen < cBuf->capacity)
+    {
+        memcpy(&cBuf->buffer[cBuf->tailOffset + 1], pSrc, writableLen);
+
+        if((cBuf->tailOffset < cBuf->headOffset) && (cBuf->tailOffset+writableLen >= cBuf->headOffset) )
+            resetHead = true;
+
+        cBuf->tailOffset += writableLen;
+    }
+    else//in case the circle buffer will be overflow after adding the data
+    {
+        size_t remainSize = cBuf->capacity - cBuf->tailOffset - 1; //the remain size
+        memcpy(&cBuf->buffer[cBuf->tailOffset+1], pSrc, remainSize);
+
+        size_t coverSize = writableLen - remainSize; //size of data to be covered from the beginning
+        memcpy(cBuf->buffer, pSrc+remainSize, coverSize);
+
+        if(cBuf->tailOffset < cBuf->headOffset)
+            resetHead = true;
+        else
+        {
+            if(coverSize>cBuf->headOffset)
+                resetHead = true;
+        }
+
+        cBuf->tailOffset = coverSize - 1;
+    }
+
+    if(cBuf->headOffset == (size_t)-1)
+        cBuf->headOffset = 0;
+
+    if(resetHead)
+    {
+        if(cBuf->tailOffset+1 < cBuf->capacity)
+            cBuf->headOffset = cBuf->tailOffset + 1;
+        else
+            cBuf->headOffset = 0;
+
+        cBuf->dataSize = cBuf->capacity;
+    }
+    else
+    {
+        if(cBuf->tailOffset >= cBuf->headOffset)
+            cBuf->dataSize = cBuf->tailOffset - cBuf->headOffset + 1;
+        else
+            cBuf->dataSize = cBuf->capacity - (cBuf->headOffset - cBuf->tailOffset - 1);
+    }
+}
+
+#ifdef __unix__
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int circular_buffer_write_In_socket(circular_buffer_t cBuf, int sockfd)
+{
+    if(cBuf->dataSize == 0) {
+        return 0;
+    }
+
+    ssize_t rdLen = -1;
+
+    if(cBuf->headOffset <= cBuf->tailOffset)
+    {
+        rdLen = send(sockfd,
+                     &cBuf->buffer[cBuf->headOffset],
+                     cBuf->dataSize, MSG_DONTWAIT | MSG_NOSIGNAL | MSG_DONTROUTE);
+        if(rdLen < 0) {
+            log_it(L_ERROR, "Can't write data in socket. %s", strerror(errno));
+            return -1;
+        }
+
+        cBuf->headOffset += rdLen;
+        if(cBuf->headOffset > cBuf->tailOffset)
+        {
+            cBuf->headOffset = -1;
+            cBuf->tailOffset = -1;
+        }
+
+        cBuf->dataSize -= rdLen;
+    }
+    else
+    {
+        if(cBuf->headOffset + cBuf->dataSize <= cBuf->capacity)
+        {
+            rdLen = send(sockfd,
+                         &cBuf->buffer[cBuf->headOffset],
+                    cBuf->dataSize, MSG_DONTWAIT | MSG_NOSIGNAL);
+
+            if(rdLen < 0) {
+                log_it(L_ERROR, "Can't write data in socket. %s", strerror(errno));
+                return -1;
+            }
+
+            cBuf->headOffset += rdLen;
+            if(cBuf->headOffset == cBuf->capacity)
+                cBuf->headOffset = 0;
+        }
+        else
+        {
+            size_t countBytesToEnd = cBuf->capacity - cBuf->headOffset;
+            rdLen = send(sockfd,
+                         &cBuf->buffer[cBuf->headOffset],
+                    countBytesToEnd, MSG_DONTWAIT | MSG_NOSIGNAL);
+          //  log_it(L_DEBUG, "Write in socket: %s", &cBuf->buffer[cBuf->headOffset]);
+            if(rdLen < 0) {
+                log_it(L_ERROR, "Can't write data in socket. %s", strerror(errno));
+                return -1;
+            }
+
+            if(rdLen < (ssize_t)countBytesToEnd) {
+                log_it(L_WARNING, "rdLen < countBytesToEnd");
+                circular_buffer_pop(cBuf, rdLen, NULL);
+                return rdLen;
+            }
+
+            cBuf->dataSize -= countBytesToEnd;
+            cBuf->headOffset = 0;
+            cBuf->tailOffset = cBuf->dataSize - 1;
+
+            ssize_t rdLen2 = send(sockfd,
+                         cBuf->buffer,
+                         cBuf->dataSize, MSG_DONTWAIT | MSG_NOSIGNAL);
+
+            if(rdLen2 < 0) {
+                log_it(L_ERROR, "Can't write data in socket. %s", strerror(errno));
+                return rdLen;
+            }
+
+            cBuf->headOffset = rdLen2;
+            if(cBuf->headOffset > cBuf->tailOffset)
+            {
+                cBuf->headOffset = -1;
+                cBuf->tailOffset = -1;
+                cBuf->dataSize = 0;
+            }
+            return countBytesToEnd + rdLen2;
+        }
+    }
+
+    return rdLen;
+
+}
+
+#endif
+
+size_t inter_circularBuffer_read(circular_buffer_t cBuf, size_t length, void *dataOut, bool resetHead)
+{
+    if(cBuf->dataSize == 0 || length == 0)
+        return 0;
+
+    size_t rdLen = length;
+
+    if(cBuf->dataSize < rdLen)
+        rdLen = cBuf->dataSize;
+
+
+    if(cBuf->headOffset <= cBuf->tailOffset)
+    {
+        if(dataOut)
+            memcpy(dataOut, &cBuf->buffer[cBuf->headOffset], rdLen);
+
+        if(resetHead)
+        {
+            cBuf->headOffset += rdLen;
+            if(cBuf->headOffset > cBuf->tailOffset)
+            {
+                cBuf->headOffset = -1;
+                cBuf->tailOffset = -1;
+            }
+        }
+    }
+    else
+    {
+        if(cBuf->headOffset+rdLen <= cBuf->capacity)
+        {
+            if(dataOut)
+                memcpy(dataOut, &cBuf->buffer[cBuf->headOffset], rdLen);
+
+            if(resetHead)
+            {
+                cBuf->headOffset += rdLen;
+                if(cBuf->headOffset == cBuf->capacity)
+                    cBuf->headOffset = 0;
+            }
+        }
+        else
+        {
+            size_t frg1Len = cBuf->capacity - cBuf->headOffset;
+            if(dataOut)
+                memcpy(dataOut, &cBuf->buffer[cBuf->headOffset], frg1Len);
+
+            size_t frg2len = rdLen - frg1Len;
+            if(dataOut)
+                memcpy(dataOut+frg1Len, cBuf->buffer, frg2len);
+
+            if(resetHead)
+            {
+                cBuf->headOffset = frg2len;
+                if(cBuf->headOffset > cBuf->tailOffset)
+                {
+                    cBuf->headOffset = -1;
+                    cBuf->tailOffset = -1;
+                }
+            }
+        }
+    }
+
+    if(resetHead)
+        cBuf->dataSize -= rdLen;
+
+    return rdLen;
+}
+
+
+size_t circular_buffer_pop(circular_buffer_t cBuf, size_t length, void *dataOut)
+{
+    return inter_circularBuffer_read(cBuf,length,dataOut,true);
+}
+
+size_t circular_buffer_read(circular_buffer_t cBuf, size_t length, void *dataOut)
+{
+    return inter_circularBuffer_read(cBuf,length,dataOut,false);
+}
+
+
+//print circular buffer's content into str,
+void circular_buffer_print(circular_buffer_t cBuf, bool hex)
+{
+    uint8_t *b = cBuf->buffer;
+    size_t cSize = circular_buffer_get_capacity(cBuf);
+    char *str = malloc(2*cSize+1);
+
+    char c;
+
+    for(size_t i=0; i<cSize; i++)
+    {
+        if(circular_buffer_get_data_size(cBuf) == 0)
+        {
+            c = '_';
+        }
+        else if (cBuf->tailOffset < cBuf->headOffset)
+        {
+            if(i>cBuf->tailOffset && i<cBuf->headOffset)
+                c = '_';
+            else
+              c = b[i];
+        }
+        else
+        {
+            if(i>cBuf->tailOffset || i<cBuf->headOffset)
+                c = '_';
+            else
+                c = b[i];
+        }
+        if(hex)
+            sprintf(str+i*2, "%02X|",c);
+        else
+            sprintf(str+i*2, "%c|",c);
+    }
+
+    printf("CircularBuffer: %s <size %zu dataSize:%zu>\n",str,circular_buffer_get_capacity(cBuf),circular_buffer_get_data_size(cBuf));
+
+    free(str);
+}
diff --git a/src/dap_common.c b/src/dap_common.c
new file mode 100755
index 0000000..45effaf
--- /dev/null
+++ b/src/dap_common.c
@@ -0,0 +1,678 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <kahovski@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2018
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef DAP_OS_ANDROID
+#include <android/log.h>
+#endif
+
+#ifndef _WIN32
+#include <pthread.h>
+#include <syslog.h>
+
+// Quick and dirty, I'm not sure but afair somewhere it was in UNIX systems too
+#define min(a,b) (((a)<(b))?(a):(b))
+#define max(a,b) (((a)>(b))?(a):(b))
+
+#else
+#include <stdlib.h>
+#include <windows.h>
+#include <process.h>
+typedef HANDLE pthread_mutex_t;
+#define popen _popen
+#define pclose _pclose
+#define pipe(pfds) _pipe(pfds, 4096, 0x8000)
+#define PTHREAD_MUTEX_INITIALIZER 0
+int pthread_mutex_lock(HANDLE **obj)
+{
+    return (( *obj = (HANDLE) CreateMutex(0, 1, 0) ) == NULL) ? 0 : 1;
+}
+int pthread_mutex_unlock(HANDLE *obj) {
+    return (ReleaseMutex(obj) == 0) ? 0 : 1;
+}
+#endif
+#include <time.h> /* 'nanosleep' */
+#include <unistd.h> /* 'pipe', 'read', 'write' */
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_string.h"
+#include "dap_list.h"
+#define LAST_ERROR_MAX 255
+
+#define LOG_TAG "dap_common"
+
+static char s_last_error[LAST_ERROR_MAX] = {0};
+static enum dap_log_level dap_log_level = L_DEBUG;
+static FILE * s_log_file = NULL;
+
+static char log_tag_fmt_str[10];
+
+/**
+ * @brief set_log_level Sets the logging level
+ * @param[in] ll logging level
+ */
+void dap_log_level_set(enum dap_log_level ll) {
+    dap_log_level = ll;
+}
+
+enum dap_log_level dap_log_level_get(void) {
+    return dap_log_level ;
+}
+
+/**
+ * @brief dap_set_log_tag_width Sets the length of the label
+ * @param[in] width Length not more than 99
+ */
+void dap_set_log_tag_width(size_t width) {
+    if (width > 99) {
+        fprintf(stderr,"Can't set width %zd", width);
+        return;
+    }
+
+    // construct new log_tag_fmt_str
+    strcpy(log_tag_fmt_str, "[%");
+    strcat(log_tag_fmt_str, dap_itoa((int)width));
+    strcat(log_tag_fmt_str, "s]\t");
+}
+
+/**
+ * @brief dap_common_init initialise
+ * @param[in] a_log_file
+ * @return
+ */
+int dap_common_init(const char * a_log_file)
+{
+    srand((unsigned int)time(NULL));
+    // init default log tag 8 width
+    strcpy(log_tag_fmt_str, "[%8s]\t");
+
+    if (a_log_file) {
+        s_log_file = fopen(a_log_file , "a");
+        if(s_log_file == NULL) {
+            fprintf(stderr,"Can't open log file %s to append\n", a_log_file);
+            return -1;
+        }
+    }
+
+    // Set max items in log list
+    dap_log_set_max_item(10);
+
+    return 0;
+}
+
+/**
+ * @brief dap_common_deinit Deinitialise
+ */
+void dap_common_deinit()
+{
+    if(s_log_file) fclose(s_log_file);
+}
+
+// list of logs
+static dap_list_t *s_list_logs = NULL;
+// for separate access to logs
+static pthread_mutex_t s_list_logs_mutex = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int s_max_items = 1000;
+
+/*
+ * Set max items in log list
+ */
+void dap_log_set_max_item(unsigned int a_max)
+{
+    if(a_max>0)
+        s_max_items = a_max;
+}
+
+/*
+ * Get logs from list
+ */
+char *dap_log_get_item(time_t a_start_time, int a_limit)
+{
+    int l_count = 0;
+    pthread_mutex_lock(&s_list_logs_mutex);
+    dap_list_t *l_list = s_list_logs;
+
+    l_list = dap_list_last(l_list);
+    // find first item
+    while(l_list) {
+        dap_list_logs_item_t *l_item = (dap_list_logs_item_t*) l_list->data;
+        if(a_start_time > l_item->t) {
+            l_list = dap_list_next(l_list);
+            break;
+        }
+        l_count++;
+        l_list = dap_list_previous(l_list);
+    }
+    // no new logs
+    if(!l_count){
+        pthread_mutex_unlock(&s_list_logs_mutex);
+        return NULL;
+    }
+    // if need all list
+    if(!l_list)
+        l_list = s_list_logs;
+
+    // create return string
+    dap_string_t *l_string = dap_string_new("");
+    l_count = 0;
+    while(l_list && a_limit > l_count) {
+        dap_list_logs_item_t *l_item = (dap_list_logs_item_t*) l_list->data;
+        //dap_string_append_printf(l_string, "%lld;%s\n", (int64_t) l_item->t, l_item->str);
+        l_list = dap_list_next(l_list);
+        l_count++;
+        if(l_list && a_limit > l_count)
+        	dap_string_append_printf(l_string, "%s\n", l_item->str);
+        else// last item
+        	dap_string_append_printf(l_string, "%s", l_item->str);
+    }
+    pthread_mutex_unlock(&s_list_logs_mutex);
+
+    char *l_ret_str = dap_string_free(l_string, false);
+    return l_ret_str;
+}
+
+// save log to list
+static void log_add_to_list(time_t a_t, const char *a_time_str, const char * a_log_tag, enum dap_log_level a_ll,
+        const char * a_format, va_list a_ap)
+{
+    pthread_mutex_lock(&s_list_logs_mutex);
+    dap_string_t *l_string = dap_string_new("");
+    dap_string_append_printf(l_string, "[%s]\t", a_time_str);
+    if(a_ll == L_DEBUG) {
+        l_string = dap_string_append(l_string, "[DBG]\t");
+    } else if(a_ll == L_INFO) {
+        l_string = dap_string_append(l_string, "[INF]\t");
+    } else if(a_ll == L_NOTICE) {
+        l_string = dap_string_append(l_string, "[ * ]\t");
+    } else if(a_ll == L_WARNING) {
+        l_string = dap_string_append(l_string, "[WRN]\t");
+    } else if(a_ll == L_ERROR) {
+        l_string = dap_string_append(l_string, "[ERR]\t");
+    } else if(a_ll == L_CRITICAL) {
+        l_string = dap_string_append(l_string, "[!!!]\t");
+    }
+
+    if(a_log_tag != NULL) {
+        dap_string_append_printf(l_string, log_tag_fmt_str, a_log_tag);
+    }
+    dap_string_append_vprintf(l_string, a_format, a_ap);
+
+    dap_list_logs_item_t *l_item = DAP_NEW(dap_list_logs_item_t);
+    l_item->t = a_t;
+    l_item->str = dap_string_free(l_string, false);
+    s_list_logs = dap_list_append(s_list_logs, l_item);
+
+    // remove old items
+    unsigned int l_count = dap_list_length(s_list_logs);
+    if(l_count > s_max_items) {
+        // remove items from the beginning
+        for(unsigned int i = 0; i < l_count - s_max_items; i++) {
+            s_list_logs = dap_list_remove(s_list_logs, s_list_logs->data);
+        }
+    }
+    pthread_mutex_unlock(&s_list_logs_mutex);
+}
+
+/**
+ * @brief _log_it Writes information to the log
+ * @param[in] log_tag Tag
+ * @param[in] ll Log level
+ * @param[in] format
+ */
+void _log_it(const char * log_tag,enum dap_log_level ll, const char * format,...)
+{
+    if(ll<dap_log_level)
+        return;
+
+    va_list ap;
+
+    va_start(ap,format);
+    _vlog_it(log_tag,ll, format,ap);
+    va_end(ap);
+}
+
+void _vlog_it(const char * log_tag,enum dap_log_level ll, const char * format,va_list ap)
+{
+    va_list ap2,ap3;
+
+    static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
+
+    pthread_mutex_lock(&mutex);
+#ifdef DAP_OS_ANDROID
+    char buf[4096];
+    vsnprintf(buf,sizeof(buf),format,ap);
+    switch (ll) {
+    case L_INFO:
+        __android_log_write(ANDROID_LOG_INFO,DAP_BRAND,buf);
+        break;
+    case L_WARNING:
+        __android_log_write(ANDROID_LOG_WARN,DAP_BRAND,buf);
+        break;
+    case L_ERROR:
+        __android_log_write(ANDROID_LOG_ERROR,DAP_BRAND,buf);
+        break;
+    case L_CRITICAL:
+        __android_log_write(ANDROID_LOG_FATAL,DAP_BRAND,buf);
+        abort();
+        break;
+    case L_DEBUG:
+    default:
+        __android_log_write(ANDROID_LOG_DEBUG,DAP_BRAND,buf);
+    }
+#endif
+
+
+    va_copy(ap2,ap);
+    va_copy(ap3,ap);
+    time_t t=time(NULL);
+    struct tm* tmp=localtime(&t);
+    static char s_time[1024]={0};
+    strftime(s_time,sizeof(s_time),"%x-%X",tmp);
+
+    if (s_log_file ) fprintf(s_log_file,"[%s] ",s_time);
+    printf("[%s] ",s_time);
+
+    if(ll==L_DEBUG){
+        if (s_log_file ) fprintf(s_log_file,"[DBG] ");
+        printf(	"\x1b[37;2m[DBG] ");
+    }else if(ll==L_INFO){
+        if (s_log_file ) fprintf(s_log_file,"[INF] ");
+        printf("\x1b[32;2m[INF] ");
+    }else if(ll==L_NOTICE){
+        if (s_log_file ) fprintf(s_log_file,"[ * ] ");
+        printf("\x1b[32m[ * ] ");
+    }else if(ll==L_WARNING){
+        if (s_log_file ) fprintf(s_log_file,"[WRN] ");
+        printf("\x1b[31;2m[WRN] ");
+    }else if(ll==L_ERROR){
+        if (s_log_file ) fprintf(s_log_file,"[ERR] ");
+        printf("\x1b[31m[ERR] ");
+    }else if(ll==L_CRITICAL){
+        if (s_log_file ) fprintf(s_log_file,"[!!!] ");
+        printf("\x1b[1;5;31m[!!!] ");
+    }
+    if(log_tag != NULL) {
+        if (s_log_file ) fprintf(s_log_file,log_tag_fmt_str,log_tag);
+        printf(log_tag_fmt_str,log_tag);
+    }
+    if (s_log_file ) vfprintf(s_log_file,format,ap);
+    vprintf(format,ap2);
+    if (s_log_file ) fprintf(s_log_file,"\n");
+    printf("\x1b[0m\n");
+    va_end(ap2);
+
+    // save log to list
+    log_add_to_list(t, s_time, log_tag, ll, format, ap3);
+    va_end(ap3);
+
+    if (s_log_file ) fflush(s_log_file);
+    fflush(stdout);
+    pthread_mutex_unlock(&mutex);
+}
+
+/**
+ * @brief log_error Error log
+ * @return
+ */
+const char * log_error()
+{
+    return s_last_error;
+}
+
+
+#define INT_DIGITS 19		/* enough for 64 bit integer */
+
+/**
+ * @brief itoa  The function converts an integer num to a string equivalent and places the result in a string
+ * @param[in] i number
+ * @return
+ */
+char *dap_itoa(int i)
+{
+    /* Room for INT_DIGITS digits, - and '\0' */
+    static char buf[INT_DIGITS + 2];
+    char *p = buf + INT_DIGITS + 1;	/* points to terminating '\0' */
+    if (i >= 0) {
+        do {
+            *--p = '0' + (i % 10);
+            i /= 10;
+        } while (i != 0);
+        return p;
+    }
+    else {			/* i < 0 */
+        do {
+            *--p = '0' - (i % 10);
+            i /= 10;
+        } while (i != 0);
+        *--p = '-';
+    }
+    return p;
+}
+
+
+/**
+ * @brief time_to_rfc822 Convert time_t to string with RFC822 formatted date and time
+ * @param[out] out Output buffer
+ * @param[out] out_size_mac Maximum size of output buffer
+ * @param[in] t UNIX time
+ * @return Length of resulting string if ok or lesser than zero if not
+ */
+int dap_time_to_str_rfc822(char * out, size_t out_size_max, time_t t)
+{
+    struct tm *tmp;
+    tmp=localtime(&t);
+    if(tmp== NULL){
+        log_it(L_ERROR,"Can't convert data from unix fromat to structured one");
+        return -2;
+    }else{
+        int ret;
+        ret=strftime(out, out_size_max,"%a, %d %b %y %T %z",tmp);
+        //free(tmp);
+        if(ret>0){
+            return ret;
+        }else{
+            log_it(L_ERROR,"Can't print formatted time in string");
+            return -1;
+        }
+    }
+}
+
+static int breaker_set[2] = { -1, -1 };
+static int initialized = 0;
+static struct timespec break_latency = {0, 1 * 1000 * 1000 };
+
+int get_select_breaker()
+{
+    if (!initialized)
+    {
+        if (pipe(breaker_set) < 0) return -1;
+        else initialized = 1;
+    }
+
+    return breaker_set[0];
+}
+int send_select_break()
+{
+    if (!initialized) return -1;
+    char buffer[1];
+    if (write(breaker_set[1], "\0", 1) <= 0) return -1;
+    nanosleep(&break_latency, NULL);
+    if (read(breaker_set[0], buffer, 1) <= 0 || buffer[0] != '\0') return -1;
+    return 0;
+}
+
+#ifdef ANDROID1
+static u_long myNextRandom = 1;
+
+double atof(const char *nptr)
+{
+    return (strtod(nptr, NULL));
+}
+
+int rand(void)
+{
+    return (int)((myNextRandom = (1103515245 * myNextRandom) + 12345) % ((u_long)RAND_MAX + 1));
+}
+
+void srand(u_int seed)
+{
+    myNextRandom = seed;
+}
+
+#endif
+
+/**
+ * @brief exec_with_ret Executes a command with result return
+ * @param[in] a_cmd Command
+ * @return Result
+ */
+char * exec_with_ret(const char * a_cmd)
+{
+    FILE * fp;
+    size_t buf_len = 0;
+    char buf[4096] = {0};
+    fp= popen(a_cmd, "r");
+    if (!fp) {
+        goto FIN;
+    }
+    memset(buf,0,sizeof(buf));
+    fgets(buf,sizeof(buf)-1,fp);
+    pclose(fp);
+    buf_len=strlen(buf);
+    if(buf[buf_len-1] =='\n')buf[buf_len-1] ='\0';
+FIN:
+    return strdup(buf);
+}
+
+/**
+ * @brief exec_with_ret_multistring performs a command with a result return in the form of a multistring
+ * @param[in] a_cmd Coomand
+ * @return Return
+ */
+char * exec_with_ret_multistring(const char * a_cmd)
+{
+    FILE * fp;
+    size_t buf_len = 0;
+    char buf[4096] = {0};
+    fp= popen(a_cmd, "r");
+    if (!fp) {
+        goto FIN;
+    }
+    memset(buf,0,sizeof(buf));
+    char retbuf[4096] = {0};
+    while(fgets(buf,sizeof(buf)-1,fp)) {
+        strcat(retbuf, buf);
+    }
+    pclose(fp);
+    buf_len=strlen(retbuf);
+    if(retbuf[buf_len-1] =='\n')retbuf[buf_len-1] ='\0';
+FIN:
+    return strdup(retbuf);
+}
+
+static const char l_possible_chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+/**
+ * @brief random_string_fill Filling a string with random characters
+ * @param[out] str A pointer to a char array
+ * @param[in] length The length of the array or string
+ */
+void dap_random_string_fill(char *str, size_t length) {
+    for(size_t i = 0; i < length; i++)
+        str[i] = l_possible_chars[
+                rand() % (sizeof(l_possible_chars) - 1)];
+}
+
+/**
+ * @brief random_string_create Generates a random string
+ * @param[in] a_length lenght
+ * @return a pointer to an array
+ */
+char * dap_random_string_create_alloc(size_t a_length)
+{
+    char * ret = DAP_NEW_SIZE(char, a_length+1);
+    size_t i;
+    for(i=0; i<a_length; ++i) {
+        int index = rand() % (sizeof(l_possible_chars)-1);
+        ret[i] = l_possible_chars[index];
+    }
+    return ret;
+}
+
+
+#define MAX_PRINT_WIDTH 100
+
+static void _printrepchar(char c, size_t count) {
+    assert(count < MAX_PRINT_WIDTH &&
+           "Too many characters");
+    static char buff[MAX_PRINT_WIDTH];
+    memset(buff, (int)c, count);
+    printf("%s\n", buff);
+}
+
+
+/**
+ * @brief The function displays a dump
+ * @param[in] data The data dump you want to display
+ * @param[in] size The size of the data whose dump you want to display
+ *
+ * The function displays a dump, for example an array, in hex format
+*/
+void dap_dump_hex(const void* data, size_t size) {
+    char ascii[17];
+    size_t i, j;
+    ascii[16] = '\0';
+    for (i = 0; i < size; ++i) {
+        printf("%02X ", ((const unsigned char*)data)[i]);
+        if (((const unsigned char*)data)[i] >= ' ' && ((const unsigned char*)data)[i] <= '~') {
+            ascii[i % 16] = ((const char*)data)[i];
+        } else {
+            ascii[i % 16] = '.';
+        }
+        if ((i+1) % 8 == 0 || i+1 == size) {
+            printf(" ");
+            if ((i+1) % 16 == 0) {
+                printf("|  %s \n", ascii);
+            } else if (i+1 == size) {
+                ascii[(i+1) % 16] = '\0';
+                if ((i+1) % 16 <= 8) {
+                    printf(" ");
+                }
+                for (j = (i+1) % 16; j < 16; ++j) {
+                    printf("   ");
+                }
+                printf("|  %s \n", ascii);
+            }
+        }
+    }
+    _printrepchar('-', 70);
+}
+
+void *memzero(void *a_buf, size_t n)
+{
+    memset(a_buf,0,n);
+    return a_buf;
+}
+
+/**
+ * Convert binary data to binhex encoded data.
+ *
+ * out output buffer, must be twice the number of bytes to encode.
+ * len is the size of the data in the in[] buffer to encode.
+ * return the number of bytes encoded, or -1 on error.
+ */
+size_t dap_bin2hex(char *a_out, const void *a_in, size_t a_len)
+{
+    size_t ct = a_len;
+    static char hex[] = "0123456789ABCDEF";
+    const uint8_t *l_in = (const uint8_t *)a_in;
+    if(!a_in || !a_out )
+        return 0;
+    // hexadecimal lookup table
+    while(ct-- > 0){
+        *a_out++ = hex[*l_in >> 4];
+        *a_out++ = hex[*l_in++ & 0x0F];
+    }
+    return a_len;
+}
+
+/**
+ * Convert binhex encoded data to binary data
+ *
+ * len is the size of the data in the in[] buffer to decode, and must be even.
+ * out outputbuffer must be at least half of "len" in size.
+ * The buffers in[] and out[] can be the same to allow in-place decoding.
+ * return the number of bytes encoded, or 0 on error.
+ */
+size_t dap_hex2bin(uint8_t *a_out, const char *a_in, size_t a_len)
+{
+    // '0'-'9' = 0x30-0x39
+    // 'a'-'f' = 0x61-0x66
+    // 'A'-'F' = 0x41-0x46
+    size_t ct = a_len;
+    if(!a_in || !a_out || (a_len & 1))
+        return 0;
+    while(ct > 0) {
+        char ch1 = ((*a_in >= 'a') ? (*a_in++ - 'a' + 10) : ((*a_in >= 'A') ? (*a_in++ - 'A' + 10) : (*a_in++ - '0'))) << 4;
+        char ch2 = ((*a_in >= 'a') ? (*a_in++ - 'a' + 10) : ((*a_in >= 'A') ? (*a_in++ - 'A' + 10) : (*a_in++ - '0'))); // ((*in >= 'A') ? (*in++ - 'A' + 10) : (*in++ - '0'));
+        *a_out++ =(uint8_t) ch1 + (uint8_t) ch2;
+        ct -= 2;
+    }
+    return a_len;
+}
+
+/**
+ * Convert string to digit
+ */
+void dap_digit_from_string(const char *num_str, uint8_t *raw, size_t raw_len)
+{
+    if(!num_str)
+        return;
+    uint64_t val;
+    if(!strncasecmp(num_str, "0x", 2)) {
+        val = strtoull(num_str + 2, NULL, 16);
+    }else {
+        val = strtoull(num_str, NULL, 10);
+    }
+    // for LITTLE_ENDIAN (Intel), do nothing, otherwise swap bytes
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    val = le64toh(val);
+#endif
+    memset(raw, 0, raw_len);
+    memcpy(raw, &val, min(raw_len, sizeof(uint64_t)));
+}
+
+
+#ifdef __MINGW32__
+/*!
+ * \brief Execute shell command silently
+ * \param a_cmd command line
+ * \return 0 if success, -1 otherwise
+ */
+int exec_silent(const char * a_cmd) {
+    PROCESS_INFORMATION p_info;
+    STARTUPINFOA s_info;
+    memzero(&s_info, sizeof(s_info));
+    memzero(&p_info, sizeof(p_info));
+
+    s_info.cb = sizeof(s_info);
+    char cmdline[512] = {'\0'};
+    strcat(cmdline, "C:\\Windows\\System32\\cmd.exe /c ");
+    strcat(cmdline, a_cmd);
+
+    if (CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0x08000000, NULL, NULL, &s_info, &p_info)) {
+        WaitForSingleObject(p_info.hProcess, 0xffffffff);
+        CloseHandle(p_info.hProcess);
+        CloseHandle(p_info.hThread);
+        return 0;
+    }
+    else {
+        return -1;
+    }
+}
+#endif
diff --git a/src/dap_config.c b/src/dap_config.c
new file mode 100755
index 0000000..e30c286
--- /dev/null
+++ b/src/dap_config.c
@@ -0,0 +1,575 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include "dap_file_utils.h"
+#include "uthash.h"
+#include "dap_common.h"
+#include "dap_config.h"
+
+#define LOG_TAG "dap_config"
+
+dap_config_t * g_config = NULL;
+
+/**
+ * @brief The dap_config_item struct
+ */
+typedef struct dap_config_item{
+    char name[64];
+    struct dap_config_item * childs;
+    struct dap_config_item * item_next;
+    union{
+        char *data_str;
+        uint8_t data_uint8;
+        bool data_bool;
+        double data_double;
+        int32_t data_int32;
+        struct {
+            char **data_str_array;
+            uint16_t array_length;
+        };
+    };
+    bool is_array;
+    UT_hash_handle hh;
+} dap_config_item_t;
+
+
+typedef struct dap_config_internal
+{
+    dap_config_item_t * item_root;
+} dap_config_internal_t;
+#define DAP_CONFIG_INTERNAL(a) ( (dap_config_internal_t* ) a->_internal )
+
+#define MAX_CONFIG_PATH 256
+static char s_configs_path[MAX_CONFIG_PATH] = "/opt/dap/etc";
+
+
+/**
+ * @brief dap_config_init Initialization settings
+ * @param[in] a_configs_path If NULL path is set to default
+ * @return
+ */
+int dap_config_init(const char * a_configs_path)
+{
+    if( a_configs_path ) {
+#ifdef _WIN32
+        // Check up under Windows, in Linux is not required
+        if(!dap_valid_ascii_symbols(a_configs_path)) {
+            log_it(L_ERROR, "Supported only ASCII symbols for directory path");
+            return -1;
+        }
+#endif
+        if(dap_dir_test(a_configs_path) || !dap_mkdir_with_parents(a_configs_path)) {
+            strncpy(s_configs_path, a_configs_path,sizeof(s_configs_path));
+            return 0;
+        }
+    }
+    return -1;
+}
+
+const char * dap_config_path()
+{
+    return s_configs_path;
+}
+
+
+/**
+ * @brief dap_config_deinit Deinitialize settings
+ */
+void dap_config_deinit()
+{
+
+}
+
+
+/**
+ * @brief get_array_length Function parse string and return array length
+ * @param[in] value
+ * @details internal function parse string and return array length
+ * @return
+ */
+static uint16_t get_array_length(const char* str) {
+    uint16_t array_length = 1; // by default if not find ','
+    while (*str) {
+        if (*str == ',')
+            array_length++;
+        str++;
+    }
+    return array_length;
+}
+/**
+ * @brief dap_config_open Open the configuration settings
+ * @param[in] a_name Configuration name
+ * @return dap_config_t Configuration
+ */
+dap_config_t * dap_config_open(const char * a_name)
+{
+    dap_config_t * ret = NULL;
+    if ( a_name ){
+        log_it(L_DEBUG,"Looking for config name %s...",a_name);
+        size_t l_config_path_size_max = strlen(a_name)+6+strlen(s_configs_path);
+        char *l_config_path = DAP_NEW_SIZE(char,l_config_path_size_max);
+        snprintf(l_config_path,l_config_path_size_max, "%s/%s.cfg",s_configs_path,a_name);
+        FILE * f = fopen(l_config_path,"r");
+        if ( f ){
+            log_it(L_DEBUG,"Opened config %s",a_name);
+            ret = DAP_NEW_Z(dap_config_t);
+            dap_config_internal_t * l_config_internal = DAP_NEW_Z(dap_config_internal_t);
+            ret->_internal = l_config_internal;
+
+            char buf[1024];
+            size_t l_global_offset=0;
+            size_t l_buf_size=0;
+            size_t l_buf_pos_line_start=0;
+            size_t l_buf_pos_line_end=0;
+            dap_config_item_t * l_section_current = NULL ;
+            bool l_is_space_now = false;
+            while ( feof(f)==0){ // Break on lines
+                size_t i;
+                l_global_offset +=  (l_buf_size = fread(buf,1,sizeof(buf),f) );
+                for (i=0; i< l_buf_size; i++){
+                    if( (buf[i] == '\r') || (buf[i] == '\n' ) ){
+                        if( ! l_is_space_now){
+                            l_buf_pos_line_end = i;
+                            l_is_space_now = true;
+                            //if(l_buf_pos_line_end)
+                            //    l_buf_pos_line_end--;
+                            if(l_buf_pos_line_end != l_buf_pos_line_start ){ // Line detected
+                                char *l_line = NULL;
+                                size_t l_line_length = 0;
+                                size_t j;
+
+                                // Trimming spaces and skip the line if commented
+                                for ( j = l_buf_pos_line_start; j < l_buf_pos_line_end; j++ ){
+                                    if ( buf[j] == '#' )
+                                        break;
+                                    if (buf[j] != ' ' ){
+                                        l_line_length = (l_buf_pos_line_end - j);
+                                        break;
+                                    }
+                                }
+                                if( l_line_length ){
+                                    l_line = DAP_NEW_SIZE(char,l_line_length+1);
+                                    memcpy(l_line,buf+j,l_line_length);
+                                    l_line[l_line_length] = 0;
+
+                                    // Process trimmed line
+                                    if( (l_line[0] == '[' ) && (l_line[l_line_length-1] == ']' ) ){ // Section detected
+                                        //log_it(L_DEBUG, "Raw line '%s'",l_line);
+                                        char * l_section_name = strdup(l_line+1);
+                                        size_t l_section_name_length = (l_line_length - 2);
+                                        l_section_name[l_section_name_length]='\0';
+                                        // log_it(L_DEBUG,"Config section '%s'",l_section_name);
+
+                                        dap_config_item_t * l_item_section = DAP_NEW_Z(dap_config_item_t);
+                                        strncpy(l_item_section->name,l_section_name,sizeof(l_item_section->name)-1);
+                                        l_item_section->item_next = l_config_internal->item_root;
+                                        l_config_internal->item_root = l_item_section;
+                                        free(l_section_name);
+
+                                        l_section_current = l_item_section;
+                                    }else{ // key-value line
+                                        //log_it(L_DEBUG,"Read line '%s'",l_line);
+                                        char l_param_name[sizeof(l_section_current->name)];
+                                        size_t l_param_name_size=0;
+                                        size_t l_param_value_size=0;
+                                        char l_param_value[1024];
+                                        l_param_name[0] = 0;
+                                        l_param_value[0] = 0;
+                                        for ( j = 0; j < l_line_length; j++ ){ // Parse param name
+                                            if ( ( l_line[j] == ' ' )|| ( l_line[j] == '=' ) ||( l_line[j] == '\t' ) ){ // Param name
+                                                l_param_name_size = j;
+                                                if (l_param_name_size > (sizeof(l_param_name) -1) ){
+                                                    l_param_name_size = (sizeof(l_param_name) - 1 );
+                                                    log_it(L_WARNING,"Too long param name in config, %u is more than %u maximum",
+                                                           j,sizeof(l_param_name) -1);
+                                                }
+                                                strncpy(l_param_name,l_line,j);
+                                                l_param_name[j] = 0;
+                                                break;
+                                            }
+
+                                        }
+
+                                        for (; j < l_line_length; j++ ){ // Find beginning of param value
+                                            if ( ( l_line[j] != '\t' ) && ( l_line[j] != ' ' ) && ( l_line[j] != '=' ) ){
+                                                break;
+                                            }
+                                        }
+                                        l_param_value_size = l_line_length - j;
+                                        if (l_param_value_size ){
+                                            if (l_param_value_size > (sizeof(l_param_value) -1) ){
+                                                l_param_value_size = (sizeof(l_param_value) - 1 );
+                                                log_it(L_WARNING,"Too long param value in config, %u is more than %u maximum",
+                                                       l_line_length - j,sizeof(l_param_value) -1);
+                                            }
+                                            strncpy(l_param_value,l_line +j, l_param_value_size);
+                                            l_param_value[l_param_value_size] = '\0';
+                                            for(int j=(int)l_param_value_size-1; j>=0; j--){
+                                                if( (l_param_value[j] ==' ') || (l_param_value[j] =='\t') ){
+                                                    l_param_value[j] = '\0';
+                                                }else{
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    //    log_it(L_DEBUG,"  Param '%s' = '%s'", l_param_name, l_param_value);
+                                        if (l_section_current){
+
+                                            if (l_param_value[0] == '[') {
+                                                if(l_param_value[1] == ']') {
+                                                    log_it(L_WARNING, "Empty array!");
+                                                    continue;
+                                                }
+
+                                                // delete '[' and ']'
+                                                char* values = l_param_value + 1;
+                                                values[l_param_value_size-2] = 0;
+
+                                                dap_config_item_t * l_item = DAP_NEW_Z(dap_config_item_t);
+
+                                                strncpy(l_item->name,l_param_name,sizeof(l_item->name));
+                                                l_item->item_next = l_section_current->childs;
+                                                l_item->is_array = true;
+                                                l_section_current->childs = l_item;
+                                                l_item->array_length = get_array_length(l_param_value);
+                                                l_item->data_str_array = (char**) malloc (sizeof(char*) * l_item->array_length);
+                                                // parsing items in array
+                                                int j = 0;
+                                                char * l_tmp = NULL;
+                                                char *token = strtok_r(values, ",",&l_tmp);
+                                                while(token) {
+
+                                                    // trim token whitespace
+                                                    if (isspace(token[0]))
+                                                        token = token + 1;
+                                                    if (isspace(token[strlen(token) - 1])
+                                                            || token[strlen(token) - 1] == ']' /* last item in array */)
+                                                        token[strlen(token) - 1] = 0;
+
+                                                    l_item->data_str_array[j] = strdup(token);
+
+                                                    token = strtok_r(NULL, ",",&l_tmp);
+                                                    j++;
+                                                }
+
+                                            } else {
+                                                dap_config_item_t * l_item = DAP_NEW_Z(dap_config_item_t);
+
+                                                strncpy(l_item->name,l_param_name,sizeof(l_item->name));
+                                                l_item->item_next = l_section_current->childs;
+                                                l_item->data_str = strdup (l_param_value);
+
+                                                l_section_current->childs = l_item;
+                                            }
+                                        }else{
+                                            log_it(L_ERROR,"Can't add param to a tree without current section");
+                                        }
+
+                                    }
+                                    DAP_DELETE(l_line);
+                                }
+                            }
+                        }
+                        continue;
+                    }else{
+                        if (l_is_space_now){
+                            l_is_space_now = false;
+                            l_buf_pos_line_start = i;
+                        }
+                    }
+                }
+            }
+            fclose(f);
+        }else{
+            log_it(L_ERROR,"Can't open config file '%s' (%s)",l_config_path,strerror(errno));
+        }
+        DAP_DELETE(l_config_path);
+    }else{
+        log_it(L_ERROR,"Config name is NULL");
+    }
+    return ret;
+}
+
+/**
+ * @brief dap_config_close Closing the configuration
+ * @param[in] a_config Configuration
+ */
+void dap_config_close(dap_config_t * a_config)
+{
+    dap_config_item_t * l_item = DAP_CONFIG_INTERNAL(a_config)->item_root ;
+    while(l_item) {
+        dap_config_item_t * l_item_child = l_item->childs;
+        DAP_CONFIG_INTERNAL(a_config)->item_root = l_item->item_next;
+
+        while(l_item_child) {
+            l_item->childs = l_item_child->item_next;
+            if(l_item_child->is_array) {
+                for(int i = 0; i< l_item_child->array_length; i++)
+                    free(l_item_child->data_str_array[i]);
+                free(l_item_child->data_str_array);
+            } else if (l_item_child->data_str) {
+                DAP_DELETE(l_item_child->data_str);
+            }
+            DAP_DELETE(l_item_child);
+            l_item_child = l_item->childs;
+        }
+
+        if(l_item->data_str) {
+            DAP_DELETE(l_item->data_str);
+        }
+        DAP_DELETE(l_item);
+        l_item = DAP_CONFIG_INTERNAL(a_config)->item_root;
+    }
+
+    free(a_config->_internal);
+    free(a_config);
+
+}
+
+/**
+ * @brief dap_config_get_item_int32 Getting a configuration item as a int32
+ * @param[in] a_config
+ * @param[in] a_section_path
+ * @param[in] a_item_name
+ * @return
+ */
+int32_t dap_config_get_item_int32(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    return atoi(dap_config_get_item_str(a_config,a_section_path,a_item_name));
+}
+
+/**
+ * @brief dap_config_get_item_int64
+ * @param a_config
+ * @param a_section_path
+ * @param a_item_name
+ * @return
+ */
+int64_t dap_config_get_item_int64(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    return atoll(dap_config_get_item_str(a_config,a_section_path,a_item_name));
+}
+
+/**
+ * @brief dap_config_get_item_uint64
+ * @param a_config
+ * @param a_section_path
+ * @param a_item_name
+ * @return
+ */
+uint64_t dap_config_get_item_uint64(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    return (uint64_t) atoll(dap_config_get_item_str(a_config,a_section_path,a_item_name));
+}
+
+/**
+ * @brief dap_config_get_item_uint16
+ * @param a_config
+ * @param a_section_path
+ * @param a_item_name
+ * @return
+ */
+uint16_t dap_config_get_item_uint16(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    return (uint16_t) atoi(dap_config_get_item_str(a_config,a_section_path,a_item_name));
+}
+
+
+/**
+ * @brief dap_config_get_item_int32_default Getting a configuration item as a int32
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name setting
+ * @param[in] a_default
+ * @return
+ */
+int32_t dap_config_get_item_int32_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, int32_t a_default)
+{
+    const char * l_str_ret = dap_config_get_item_str(a_config,a_section_path,a_item_name);
+    return l_str_ret?atoi(l_str_ret):a_default;
+}
+
+/**
+ * @brief dap_config_get_item_uint16_default
+ * @param a_config
+ * @param a_section_path
+ * @param a_item_name
+ * @param a_default
+ * @return
+ */
+uint16_t dap_config_get_item_uint16_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, uint16_t a_default)
+{
+    const char * l_str_ret = dap_config_get_item_str(a_config,a_section_path,a_item_name);
+    return l_str_ret? (uint16_t) atoi(l_str_ret):a_default;
+}
+
+/**
+ * @brief dap_config_get_item_int64_default
+ * @param a_config
+ * @param a_section_path
+ * @param a_item_name
+ * @param a_default
+ * @return
+ */
+int64_t dap_config_get_item_int64_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, int64_t a_default)
+{
+    const char * l_str_ret = dap_config_get_item_str(a_config,a_section_path,a_item_name);
+    return l_str_ret? (int64_t) atoll(l_str_ret):a_default;
+}
+
+/**
+ * @brief dap_config_get_item_int64_default
+ * @param a_config
+ * @param a_section_path
+ * @param a_item_name
+ * @param a_default
+ * @return
+ */
+uint64_t dap_config_get_item_uint64_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, uint64_t a_default)
+{
+    const char * l_str_ret = dap_config_get_item_str(a_config,a_section_path,a_item_name);
+    return l_str_ret? (uint64_t) atoll(l_str_ret):a_default;
+}
+
+
+/**
+ * @brief dap_config_get_item Get the configuration as a item
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name setting
+ * @return
+ */
+static dap_config_item_t * dap_config_get_item(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    dap_config_item_t * l_item_section = DAP_CONFIG_INTERNAL(a_config)->item_root ;
+    while(l_item_section){
+        if (strcmp(l_item_section->name,a_section_path)==0){
+            dap_config_item_t * l_item = l_item_section->childs;
+            while (l_item){
+                if (strcmp(l_item->name,a_item_name)==0){
+                    return l_item;
+                }
+                l_item = l_item->item_next;
+            }
+        }
+        l_item_section = l_item_section->item_next;
+    }
+    return NULL;
+}
+
+
+/**
+ * @brief dap_config_get_item_str Getting a configuration item as a string
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name setting
+ * @return
+ */
+const char * dap_config_get_item_str(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    dap_config_item_t * item = dap_config_get_item(a_config, a_section_path, a_item_name);
+    if (item == NULL)
+        return NULL;
+    return item->data_str;
+}
+
+
+/**
+ * @brief dap_config_get_array_str Getting an array of configuration items as a string
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name setting
+ * @return
+ */
+char** dap_config_get_array_str(dap_config_t * a_config, const char * a_section_path,
+                                const char * a_item_name, uint16_t * array_length) {
+    dap_config_item_t * item = dap_config_get_item(a_config, a_section_path, a_item_name);
+    if (item == NULL)
+        return NULL;
+    if (array_length != NULL)
+        *array_length = item->array_length;
+    return item->data_str_array;
+}
+
+
+/**
+ * @brief dap_config_get_item_str_default Getting an array of configuration items as a string
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name setting
+ * @param[in] a_value_default Default
+ * @return
+ */
+const char * dap_config_get_item_str_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, const char * a_value_default)
+{
+    dap_config_item_t * l_item_section = DAP_CONFIG_INTERNAL(a_config)->item_root ;
+    while(l_item_section){
+        if (strcmp(l_item_section->name,a_section_path)==0){
+            dap_config_item_t * l_item = l_item_section->childs;
+            while (l_item){
+                if (strcmp(l_item->name,a_item_name)==0){
+                    return l_item->data_str;
+                }
+                l_item = l_item->item_next;
+            }
+        }
+        l_item_section = l_item_section->item_next;
+    }
+    return a_value_default;
+}
+
+/**
+ * @brief dap_config_get_item_bool Getting a configuration item as a boolean
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name Setting
+ * @return
+ */
+bool dap_config_get_item_bool(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    return strcmp(dap_config_get_item_str(a_config,a_section_path,a_item_name),"true") == 0;
+}
+
+
+/**
+ * @brief dap_config_get_item_bool_default Getting a configuration item as a boolean
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name Setting
+ * @param[in] a_default Default
+ * @return
+ */
+bool dap_config_get_item_bool_default(dap_config_t * a_config, const char * a_section_path,
+                                      const char * a_item_name, bool a_default)
+{
+    return strcmp(dap_config_get_item_str_default(a_config,a_section_path,a_item_name,
+                                                  a_default?"true":"false"),"true") == 0;
+}
+
+/**
+ * @brief dap_config_get_item_double Getting a configuration item as a floating-point value
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name Setting
+ * @return
+ */
+double dap_config_get_item_double(dap_config_t * a_config, const char * a_section_path, const char * a_item_name)
+{
+    return atof(dap_config_get_item_str(a_config,a_section_path,a_item_name));
+}
+
+/**
+ * @brief dap_config_get_item_double Getting a configuration item as a floating-point value
+ * @param[in] a_config Configuration
+ * @param[in] a_section_path Path
+ * @param[in] a_item_name Setting
+ * @param[in] a_default Defailt
+ * @return
+ */
+double dap_config_get_item_double_default(dap_config_t * a_config, const char * a_section_path, const char * a_item_name, double a_default)
+{
+    const char * l_str_ret = dap_config_get_item_str(a_config,a_section_path,a_item_name);
+    return l_str_ret?atof(l_str_ret):a_default;
+}
+
diff --git a/src/dap_file_utils.c b/src/dap_file_utils.c
new file mode 100755
index 0000000..93e6f4c
--- /dev/null
+++ b/src/dap_file_utils.c
@@ -0,0 +1,346 @@
+/*
+ * Authors:
+ * Aleksandr Lysikov <alexander.lysikov@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2018
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#if (OS_TARGET == OS_MACOS)
+    #include <stdio.h>
+#else
+    #include <malloc.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#endif
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_file_utils.h"
+
+/**
+ * Check the directory path for unsupported symbols
+ *
+ * @string
+ * @return true, if the directory path contains only ASCII symbols
+ */
+bool dap_valid_ascii_symbols(const char *a_string)
+{
+    if(!a_string)
+        return true;
+    for(size_t i = 0; i < strlen(a_string); i++) {
+        if((uint8_t) a_string[i] > 0x7f)
+            return false;
+    }
+    return true;
+}
+
+/**
+ * Check the directory for exists
+ *
+ * @dir_path directory pathname
+ * @return true, if the file is a directory
+ */
+bool dap_dir_test(const char * a_dir_path)
+{
+    if(!a_dir_path)
+        return false;
+#ifdef _WIN32
+    int attr = GetFileAttributesA(a_dir_path);
+    if(attr != -1 && (attr & FILE_ATTRIBUTE_DIRECTORY))
+        return true;
+#else
+    struct stat st;
+    if (!stat(a_dir_path, &st)) {
+        if (S_ISDIR(st.st_mode))
+        return true;
+    }
+#endif
+    return false;
+}
+
+/**
+ * Create a new directory with intermediate sub-directories
+ *
+ * @dir_path new directory pathname
+ * @return 0, if the directory was created or already exist, else -1
+ */
+int dap_mkdir_with_parents(const char *a_dir_path)
+{
+
+    char *path, *p;
+    // validation of a pointer
+    if(a_dir_path == NULL || a_dir_path[0] == '\0') {
+        errno = EINVAL;
+        return -1;
+    }
+    path = strdup(a_dir_path);
+    // skip the root component if it is present, i.e. the "/" in Unix or "C:\" in Windows
+#ifdef _WIN32
+    if(((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
+            && (path[1] == ':') && DAP_IS_DIR_SEPARATOR(path[2])) {
+        p = path + 3;
+    }
+#else
+        if (DAP_IS_DIR_SEPARATOR(path[0])) {
+            p = path + 1;
+        }
+#endif
+        else
+        p = path;
+
+    do {
+        while(*p && !DAP_IS_DIR_SEPARATOR(*p))
+            p++;
+
+        if(!*p)
+            p = NULL;
+        else
+            *p = '\0';
+
+        if(!dap_dir_test(path)) {
+#ifdef _WIN32
+            int result = mkdir(path);
+#else
+            int result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+#endif
+            if(result == -1) {
+                free(path);
+                errno = ENOTDIR;
+                return -1;
+            }
+        }
+        if(p) {
+            *p++ = DAP_DIR_SEPARATOR;
+            while(*p && DAP_IS_DIR_SEPARATOR(*p))
+                p++;
+        }
+    } while(p);
+
+    free(path);
+    return 0;
+}
+
+/**
+ * dap_path_get_basename:
+ * @a_file_name: the name of the file
+ *
+ * Gets the last component of the filename.
+ *
+ * If @a_file_name ends with a directory separator it gets the component
+ * before the last slash. If @a_file_name consists only of directory
+ * separators (and on Windows, possibly a drive letter), a single
+ * separator is returned. If @a_file_name is empty, it gets ".".
+ *
+ * Returns: a newly allocated string containing the last
+ *    component of the filename
+ */
+char* dap_path_get_basename(const char *a_file_name)
+{
+    ssize_t l_base;
+    ssize_t l_last_nonslash;
+    const char *l_retval;
+
+    dap_return_val_if_fail(a_file_name != NULL, NULL);
+
+    if(a_file_name[0] == '\0')
+        return dap_strdup(".");
+
+    l_last_nonslash = strlen(a_file_name) - 1;
+
+    while(l_last_nonslash >= 0 && DAP_IS_DIR_SEPARATOR(a_file_name[l_last_nonslash]))
+        l_last_nonslash--;
+
+    if(l_last_nonslash == -1)
+        // string only containing slashes
+        return dap_strdup(DAP_DIR_SEPARATOR_S);
+
+#ifdef _WIN32
+    if (l_last_nonslash == 1 &&
+            dap_ascii_isalpha(a_file_name[0]) &&
+            a_file_name[1] == ':')
+    // string only containing slashes and a drive
+    return dap_strdup (DAP_DIR_SEPARATOR_S);
+#endif
+    l_base = l_last_nonslash;
+
+    while(l_base >= 0 && !DAP_IS_DIR_SEPARATOR(a_file_name[l_base]))
+        l_base--;
+
+#ifdef _WIN32
+    if (l_base == -1 &&
+            dap_ascii_isalpha(a_file_name[0]) &&
+            a_file_name[1] == ':')
+    l_base = 1;
+#endif
+
+    //size_t l_len = l_last_nonslash - l_base;
+    l_retval = a_file_name + l_base + 1;
+
+    return dap_strdup(l_retval);
+}
+
+/**
+ * dap_path_is_absolute:
+ * @a_file_name: a file name
+ *
+ * Returns true if the given @a_file_name is an absolute file name.
+ * Note that this is a somewhat vague concept on Windows.
+ *
+ * On POSIX systems, an absolute file name is well-defined. It always
+ * starts from the single root directory. For example "/usr/local".
+ *
+ * On Windows, the concepts of current drive and drive-specific
+ * current directory introduce vagueness. This function interprets as
+ * an absolute file name one that either begins with a directory
+ * separator such as "\Users\tml" or begins with the root on a drive,
+ * for example "C:\Windows". The first case also includes UNC paths
+ * such as "\\myserver\docs\foo". In all cases, either slashes or
+ * backslashes are accepted.
+ *
+ * Returns: true if @a_file_name is absolute
+ */
+bool dap_path_is_absolute(const char *a_file_name)
+{
+    dap_return_val_if_fail(a_file_name != NULL, false);
+
+    if(DAP_IS_DIR_SEPARATOR(a_file_name[0]))
+        return true;
+
+#ifdef _WIN32
+    /* Recognize drive letter on native Windows */
+    if (dap_ascii_isalpha(a_file_name[0]) &&
+            a_file_name[1] == ':' && DAP_IS_DIR_SEPARATOR (a_file_name[2]))
+    return true;
+#endif
+
+    return false;
+}
+
+/**
+ * dap_path_get_dirname:
+ * @a_file_name: the name of the file
+ *
+ * Gets the directory components of a file name.
+ *
+ * If the file name has no directory components "." is returned.
+ * The returned string should be freed when no longer needed.
+ *
+ * Returns: the directory components of the file
+ */
+char* dap_path_get_dirname(const char *a_file_name)
+{
+    char *l_base;
+    size_t l_len;
+
+    dap_return_val_if_fail(a_file_name != NULL, NULL);
+
+    l_base = strrchr(a_file_name, DAP_DIR_SEPARATOR);
+
+#ifdef _WIN32
+    {
+        char *l_q;
+        l_q = strrchr (a_file_name, '/');
+        if (l_base == NULL || (l_q != NULL && l_q > l_base))
+        l_base = l_q;
+    }
+#endif
+
+    if(!l_base)
+    {
+#ifdef _WIN32
+        if (dap_ascii_isalpha(a_file_name[0]) && a_file_name[1] == ':')
+        {
+            char l_drive_colon_dot[4];
+
+            l_drive_colon_dot[0] = a_file_name[0];
+            l_drive_colon_dot[1] = ':';
+            l_drive_colon_dot[2] = '.';
+            l_drive_colon_dot[3] = '\0';
+
+            return dap_strdup (l_drive_colon_dot);
+        }
+#endif
+        return dap_strdup(".");
+    }
+
+    while(l_base > a_file_name && DAP_IS_DIR_SEPARATOR(*l_base))
+        l_base--;
+
+#ifdef _WIN32
+    /* base points to the char before the last slash.
+     *
+     * In case file_name is the root of a drive (X:\) or a child of the
+     * root of a drive (X:\foo), include the slash.
+     *
+     * In case file_name is the root share of an UNC path
+     * (\\server\share), add a slash, returning \\server\share\ .
+     *
+     * In case file_name is a direct child of a share in an UNC path
+     * (\\server\share\foo), include the slash after the share name,
+     * returning \\server\share\ .
+     */
+    if (l_base == a_file_name + 1 &&
+            dap_ascii_isalpha(a_file_name[0]) &&
+            a_file_name[1] == ':')
+    l_base++;
+    else if (DAP_IS_DIR_SEPARATOR (a_file_name[0]) &&
+            DAP_IS_DIR_SEPARATOR (a_file_name[1]) &&
+            a_file_name[2] &&
+            !DAP_IS_DIR_SEPARATOR (a_file_name[2]) &&
+            l_base >= a_file_name + 2)
+    {
+        const char *l_p = a_file_name + 2;
+        while (*l_p && !DAP_IS_DIR_SEPARATOR (*l_p))
+        l_p++;
+        if (l_p == l_base + 1)
+        {
+            l_len = (uint32_t) strlen (a_file_name) + 1;
+            l_base = DAP_NEW_SIZE (char, l_len + 1);
+            strcpy (l_base, a_file_name);
+            l_base[l_len-1] = DAP_DIR_SEPARATOR;
+            l_base[l_len] = 0;
+            return l_base;
+        }
+        if (DAP_IS_DIR_SEPARATOR (*l_p))
+        {
+            l_p++;
+            while (*l_p && !DAP_IS_DIR_SEPARATOR (*l_p))
+            l_p++;
+            if (l_p == l_base + 1)
+            l_base++;
+        }
+    }
+#endif
+
+    l_len = (uint32_t) 1 + l_base - a_file_name;
+    l_base = DAP_NEW_SIZE(char, l_len + 1);
+    memmove(l_base, a_file_name, l_len);
+    l_base[l_len] = 0;
+
+    return l_base;
+}
diff --git a/src/dap_list.c b/src/dap_list.c
new file mode 100755
index 0000000..1d043ea
--- /dev/null
+++ b/src/dap_list.c
@@ -0,0 +1,1005 @@
+/*
+ * Doubly-Linked Lists — linked lists that can be iterated over in both directions
+ */
+
+#include <stddef.h>
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_list.h"
+
+
+#define LOG_TAG "dap_list"
+
+/**
+ * dap_list_alloc:
+ * Returns: a pointer to the newly-allocated DapList element
+ **/
+dap_list_t *dap_list_alloc(void)
+{
+    dap_list_t *list = DAP_NEW(dap_list_t);
+    return list;
+}
+
+dap_list_t *dap_list_alloc0(void)
+{
+    dap_list_t *list = DAP_NEW_Z(dap_list_t);
+    return list;
+}
+
+/**
+ * dap_list_free:
+ * Frees all of the memory used by a DapList.
+ * The freed elements are returned to the slice allocator.
+ * If list elements contain dynamically-allocated memory, you should
+ * either use dap_list_free_full() or free them manually first.
+ */
+void dap_list_free(dap_list_t *list)
+{
+    while(list)
+    {
+        dap_list_t *next = list->next;
+        DAP_DELETE(list);
+        list = next;
+    }
+}
+
+/**
+ * dap_list_free_1:
+ *
+ * Frees one DapList element.
+ * It is usually used after dap_list_remove_link().
+ */
+void dap_list_free1(dap_list_t *list)
+{
+    DAP_DELETE(list);
+}
+
+/**
+ * dap_list_free_full:
+ * @list: a pointer to a DapList
+ * @free_func: the function to be called to free each element's data
+ *
+ * Convenience method, which frees all the memory used by a DapList,
+ * and calls @free_func on every element's data.
+ */
+void dap_list_free_full(dap_list_t *list, dap_callback_destroyed_t free_func)
+{
+    dap_list_foreach(list, (dap_callback_t) free_func, NULL);
+    dap_list_free(list);
+}
+
+/**
+ * dap_list_append:
+ * @list: a pointer to a DapList
+ * @data: the data for the new element
+ *
+ * Adds a new element on to the end of the list.
+ *
+ * Note that the return value is the new start of the list,
+ * if @list was empty; make sure you store the new value.
+ *
+ * dap_list_append() has to traverse the entire list to find the end,
+ * which is inefficient when adding multiple elements. A common idiom
+ * to avoid the inefficiency is to use dap_list_prepend() and reverse
+ * the list with dap_list_reverse() when all elements have been added.
+ *
+ * |[<!-- language="C" -->
+ * // Notice that these are initialized to the empty list.
+ * DapList *string_list = NULL, *number_list = NULL;
+ *
+ * // This is a list of strings.
+ * string_list = dap_list_append (string_list, "first");
+ * string_list = dap_list_append (string_list, "second");
+ * 
+ * // This is a list of integers.
+ * number_list = dap_list_append (number_list, INT_TO_POINTER (27));
+ * number_list = dap_list_append (number_list, INT_TO_POINTER (14));
+ * ]|
+ *
+ * Returns: either @list or the new start of the DapList if @list was %NULL
+ */
+dap_list_t * dap_list_append(dap_list_t *list, void* data)
+{
+    dap_list_t *new_list;
+    dap_list_t *last;
+
+    new_list = dap_list_alloc();
+    new_list->data = data;
+    new_list->next = NULL;
+
+    if(list)
+    {
+        last = dap_list_last(list);
+        /* assert (last != NULL); */
+        last->next = new_list;
+        new_list->prev = last;
+
+        return list;
+    }
+    else
+    {
+        new_list->prev = NULL;
+        return new_list;
+    }
+}
+
+/**
+ * dap_list_prepend:
+ * @list: a pointer to a DapList, this must point to the top of the list
+ * @data: the data for the new element
+ *
+ * Prepends a new element on to the start of the list.
+ *
+ * Note that the return value is the new start of the list,
+ * which will have changed, so make sure you store the new value. 
+ *
+ * |[<!-- language="C" -->
+ * // Notice that it is initialized to the empty list.
+ * DapList *list = NULL;
+ *
+ * list = dap_list_prepend (list, "last");
+ * list = dap_list_prepend (list, "first");
+ * ]|
+ *
+ * Do not use this function to prepend a new element to a different
+ * element than the start of the list. Use dap_list_insert_before() instead.
+ *
+ * Returns: a pointer to the newly prepended element, which is the new 
+ *     start of the DapList
+ */
+dap_list_t *dap_list_prepend(dap_list_t *list, void* data)
+{
+    dap_list_t *new_list;
+
+    new_list = dap_list_alloc();
+    new_list->data = data;
+    new_list->next = list;
+
+    if(list)
+    {
+        new_list->prev = list->prev;
+        if(list->prev)
+            list->prev->next = new_list;
+        list->prev = new_list;
+    }
+    else
+        new_list->prev = NULL;
+
+    return new_list;
+}
+
+/**
+ * dap_list_insert:
+ * @list: a pointer to a DapList, this must point to the top of the list
+ * @data: the data for the new element
+ * @position: the position to insert the element. If this is 
+ *     negative, or is larger than the number of elements in the 
+ *     list, the new element is added on to the end of the list.
+ * 
+ * Inserts a new element into the list at the given position.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_insert(dap_list_t *list, void* data, int position)
+{
+    dap_list_t *new_list;
+    dap_list_t *tmp_list;
+
+    if(position < 0)
+        return dap_list_append(list, data);
+    else if(position == 0)
+        return dap_list_prepend(list, data);
+
+    tmp_list = dap_list_nth(list,(unsigned int) position);
+    if(!tmp_list)
+        return dap_list_append(list, data);
+
+    new_list = dap_list_alloc();
+    new_list->data = data;
+    new_list->prev = tmp_list->prev;
+    tmp_list->prev->next = new_list;
+    new_list->next = tmp_list;
+    tmp_list->prev = new_list;
+
+    return list;
+}
+
+/**
+ * dap_list_insert_before:
+ * @list: a pointer to a DapList, this must point to the top of the list
+ * @sibling: the list element before which the new element 
+ *     is inserted or %NULL to insert at the end of the list
+ * @data: the data for the new element
+ *
+ * Inserts a new element into the list before the given position.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_insert_before(dap_list_t *list, dap_list_t *sibling, void* data)
+{
+    if(!list)
+    {
+        list = dap_list_alloc();
+        list->data = data;
+        dap_return_val_if_fail(sibling == NULL, list);
+        return list;
+    }
+    else if(sibling)
+    {
+        dap_list_t *node;
+
+        node = dap_list_alloc();
+        node->data = data;
+        node->prev = sibling->prev;
+        node->next = sibling;
+        sibling->prev = node;
+        if(node->prev)
+        {
+            node->prev->next = node;
+            return list;
+        }
+        else
+        {
+            dap_return_val_if_fail(sibling == list, node);
+            return node;
+        }
+    }
+    else
+    {
+        dap_list_t *last;
+
+        last = list;
+        while(last->next)
+            last = last->next;
+
+        last->next = dap_list_alloc();
+        last->next->data = data;
+        last->next->prev = last;
+        last->next->next = NULL;
+
+        return list;
+    }
+}
+
+/**
+ * dap_list_concat:
+ * @list1: a DapList, this must point to the top of the list
+ * @list2: the DapList to add to the end of the first DapList,
+ *     this must point  to the top of the list
+ *
+ * Adds the second DapList onto the end of the first DapList.
+ * Note that the elements of the second DapList are not copied.
+ * They are used directly.
+ *
+ * This function is for example used to move an element in the list.
+ * The following example moves an element to the top of the list:
+ * |[<!-- language="C" -->
+ * list = dap_list_remove_link (list, llink);
+ * list = dap_list_concat (llink, list);
+ * ]|
+ *
+ * Returns: the start of the new DapList, which equals @list1 if not %NULL
+ */
+dap_list_t *dap_list_concat(dap_list_t *list1, dap_list_t *list2)
+{
+    dap_list_t *tmp_list;
+
+    if(list2)
+    {
+        tmp_list = dap_list_last(list1);
+        if(tmp_list)
+            tmp_list->next = list2;
+        else
+            list1 = list2;
+        list2->prev = tmp_list;
+    }
+
+    return list1;
+}
+
+static inline dap_list_t * _dap_list_remove_link(dap_list_t *list, dap_list_t *link)
+{
+    if(link == NULL)
+        return list;
+
+    if(link->prev)
+    {
+        if(link->prev->next == link)
+            link->prev->next = link->next;
+        else
+            log_it(L_ERROR, "corrupted double-linked list detected");
+    }
+    if(link->next)
+    {
+        if(link->next->prev == link)
+            link->next->prev = link->prev;
+        else
+            log_it(L_ERROR, "corrupted double-linked list detected");
+    }
+
+    if(link == list)
+        list = list->next;
+
+    link->next = NULL;
+    link->prev = NULL;
+
+    return list;
+}
+
+/**
+ * dap_list_remove:
+ * @list: a DapList, this must point to the top of the list
+ * @data: the data of the element to remove
+ *
+ * Removes an element from a DapList.
+ * If two elements contain the same data, only the first is removed.
+ * If none of the elements contain the data, the DapList is unchanged.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_remove(dap_list_t *list, const void * data)
+{
+    dap_list_t *tmp;
+
+    tmp = list;
+    while(tmp)
+    {
+        if(tmp->data != data)
+            tmp = tmp->next;
+        else
+        {
+            list = _dap_list_remove_link(list, tmp);
+            dap_list_free1(tmp);
+
+            break;
+        }
+    }
+    return list;
+}
+
+/**
+ * dap_list_remove_all:
+ * @list: a DapList, this must point to the top of the list
+ * @data: data to remove
+ *
+ * Removes all list nodes with data equal to @data.
+ * Returns the new head of the list. Contrast with
+ * dap_list_remove() which removes only the first node
+ * matching the given data.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_remove_all(dap_list_t *list, const void * data)
+{
+    dap_list_t *tmp = list;
+
+    while(tmp)
+    {
+        if(tmp->data != data)
+            tmp = tmp->next;
+        else
+        {
+            dap_list_t *next = tmp->next;
+
+            if(tmp->prev)
+                tmp->prev->next = next;
+            else
+                list = next;
+            if(next)
+                next->prev = tmp->prev;
+
+            dap_list_free1(tmp);
+            tmp = next;
+        }
+    }
+    return list;
+}
+
+/**
+ * dap_list_remove_link:
+ * @list: a DapList, this must point to the top of the list
+ * @llink: an element in the DapList
+ *
+ * Removes an element from a DapList, without freeing the element.
+ * The removed element's prev and next links are set to %NULL, so 
+ * that it becomes a self-contained list with one element.
+ *
+ * This function is for example used to move an element in the list
+ * (see the example for dap_list_concat()) or to remove an element in
+ * the list before freeing its data:
+ * |[<!-- language="C" --> 
+ * list = dap_list_remove_link (list, llink);
+ * free_some_data_that_may_access_the_list_again (llink->data);
+ * dap_list_free (llink);
+ * ]|
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_remove_link(dap_list_t *list, dap_list_t *llink)
+{
+    return _dap_list_remove_link(list, llink);
+}
+
+/**
+ * dap_list_delete_link:
+ * @list: a DapList, this must point to the top of the list
+ * @link_: node to delete from @list
+ *
+ * Removes the node link_ from the list and frees it. 
+ * Compare this to dap_list_remove_link() which removes the node
+ * without freeing it.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_delete_link(dap_list_t *list, dap_list_t *link_)
+{
+    list = _dap_list_remove_link(list, link_);
+    dap_list_free1(link_);
+
+    return list;
+}
+
+/**
+ * dap_list_copy:
+ * @list: a DapList, this must point to the top of the list
+ *
+ * Copies a DapList.
+ *
+ * Note that this is a "shallow" copy. If the list elements 
+ * consist of pointers to data, the pointers are copied but 
+ * the actual data is not. See dap_list_copy_deep() if you need
+ * to copy the data as well.
+ *
+ * Returns: the start of the new list that holds the same data as @list
+ */
+dap_list_t *dap_list_copy(dap_list_t *list)
+{
+    return dap_list_copy_deep(list, NULL, NULL);
+}
+
+/**
+ * dap_list_copy_deep:
+ * @list: a DapList, this must point to the top of the list
+ * @func: a copy function used to copy every element in the list
+ * @user_data: user data passed to the copy function @func, or %NULL
+ *
+ * Makes a full (deep) copy of a DapList.
+ *
+ * In contrast with dap_list_copy(), this function uses @func to make
+ * a copy of each list element, in addition to copying the list
+ * container itself.
+ *
+ * @func, as a #DapCopyFunc, takes two arguments, the data to be copied
+ * and a @user_data pointer. It's safe to pass %NULL as user_data,
+ * if the copy function takes only one argument.
+ *
+ * For instance,
+ * |[<!-- language="C" -->   
+ * another_list = dap_list_copy_deep (list, (DapCopyFunc) dap_object_ref, NULL);
+ * ]|
+ *
+ * And, to entirely free the new list, you could do:
+ * |[<!-- language="C" --> 
+ * dap_list_free_full (another_list, dap_object_unref);
+ * ]|
+ *
+ * Returns: the start of the new list that holds a full copy of @list, 
+ *     use dap_list_free_full() to free it
+ */
+dap_list_t *dap_list_copy_deep(dap_list_t *list, dap_callback_copy_t func, void* user_data)
+{
+    dap_list_t *new_list = NULL;
+
+    if(list)
+    {
+        dap_list_t *last;
+
+        new_list = dap_list_alloc();
+        if(func)
+            new_list->data = func(list->data, user_data);
+        else
+            new_list->data = list->data;
+        new_list->prev = NULL;
+        last = new_list;
+        list = list->next;
+        while(list)
+        {
+            last->next = dap_list_alloc();
+            last->next->prev = last;
+            last = last->next;
+            if(func)
+                last->data = func(list->data, user_data);
+            else
+                last->data = list->data;
+            list = list->next;
+        }
+        last->next = NULL;
+    }
+
+    return new_list;
+}
+
+/**
+ * dap_list_reverse:
+ * @list: a DapList, this must point to the top of the list
+ *
+ * Reverses a DapList.
+ * It simply switches the next and prev pointers of each element.
+ *
+ * Returns: the start of the reversed DapList
+ */
+dap_list_t * dap_list_reverse(dap_list_t *list)
+{
+    dap_list_t *last;
+
+    last = NULL;
+    while(list)
+    {
+        last = list;
+        list = last->next;
+        last->next = last->prev;
+        last->prev = list;
+    }
+
+    return last;
+}
+
+/**
+ * dap_list_nth:
+ * @list: a DapList, this must point to the top of the list
+ * @n: the position of the element, counting from 0
+ *
+ * Gets the element at the given position in a DapList.
+ *
+ * Returns: the element, or %NULL if the position is off 
+ *     the end of the DapList
+ */
+dap_list_t *dap_list_nth(dap_list_t *list, unsigned int n)
+{
+    while((n-- > 0) && list)
+        list = list->next;
+
+    return list;
+}
+
+/**
+ * dap_list_nth_prev:
+ * @list: a DapList
+ * @n: the position of the element, counting from 0
+ *
+ * Gets the element @n places before @list.
+ *
+ * Returns: the element, or %NULL if the position is 
+ *     off the end of the DapList
+ */
+dap_list_t *dap_list_nth_prev(dap_list_t *list, unsigned int n)
+{
+    while((n-- > 0) && list)
+        list = list->prev;
+
+    return list;
+}
+
+/**
+ * dap_list_nth_data:
+ * @list: a DapList, this must point to the top of the list
+ * @n: the position of the element
+ *
+ * Gets the data of the element at the given position.
+ *
+ * Returns: the element's data, or %NULL if the position 
+ *     is off the end of the DapList
+ */
+void* dap_list_nth_data(dap_list_t *list, unsigned int n)
+{
+    while((n-- > 0) && list)
+        list = list->next;
+
+    return list ? list->data : NULL ;
+}
+
+/**
+ * dap_list_find:
+ * @list: a DapList, this must point to the top of the list
+ * @data: the element data to find
+ *
+ * Finds the element in a DapList which contains the given data.
+ *
+ * Returns: the found DapList element, or %NULL if it is not found
+ */
+dap_list_t *dap_list_find(dap_list_t *list, const void * data)
+{
+    while(list)
+    {
+        if(list->data == data)
+            break;
+        list = list->next;
+    }
+
+    return list;
+}
+
+/**
+ * dap_list_find_custom:
+ * @list: a DapList, this must point to the top of the list
+ * @data: user data passed to the function
+ * @func: the function to call for each element. 
+ *     It should return 0 when the desired element is found
+ *
+ * Finds an element in a DapList, using a supplied function to
+ * find the desired element. It iterates over the list, calling 
+ * the given function which should return 0 when the desired 
+ * element is found. The function takes two const pointer arguments,
+ * the DapList element's data as the first argument and the
+ * given user data.
+ *
+ * Returns: the found DapList element, or %NULL if it is not found
+ */
+dap_list_t *dap_list_find_custom(dap_list_t *list, const void * data, dap_callback_compare_t func)
+{
+    dap_return_val_if_fail(func != NULL, list);
+
+    while(list)
+    {
+        if(!func(list->data, data))
+            return list;
+        list = list->next;
+    }
+
+    return NULL ;
+}
+
+/**
+ * dap_list_position:
+ * @list: a DapList, this must point to the top of the list
+ * @llink: an element in the DapList
+ *
+ * Gets the position of the given element 
+ * in the DapList (starting from 0).
+ *
+ * Returns: the position of the element in the DapList,
+ *     or -1 if the element is not found
+ */
+int dap_list_position(dap_list_t *list, dap_list_t *llink)
+{
+    int i;
+
+    i = 0;
+    while(list)
+    {
+        if(list == llink)
+            return i;
+        i++;
+        list = list->next;
+    }
+
+    return -1;
+}
+
+/**
+ * dap_list_index:
+ * @list: a DapList, this must point to the top of the list
+ * @data: the data to find
+ *
+ * Gets the position of the element containing 
+ * the given data (starting from 0).
+ *
+ * Returns: the index of the element containing the data, 
+ *     or -1 if the data is not found
+ */
+int dap_list_index(dap_list_t *list, const void * data)
+{
+    int i;
+
+    i = 0;
+    while(list)
+    {
+        if(list->data == data)
+            return i;
+        i++;
+        list = list->next;
+    }
+
+    return -1;
+}
+
+/**
+ * dap_list_last:
+ * @list: any DapList element
+ *
+ * Gets the last element in a DapList.
+ *
+ * Returns: the last element in the DapList,
+ *     or %NULL if the DapList has no elements
+ */
+dap_list_t * dap_list_last(dap_list_t *list)
+{
+    if(list)
+    {
+        while(list->next)
+            list = list->next;
+    }
+
+    return list;
+}
+
+/**
+ * dap_list_first:
+ * @list: any DapList element
+ *
+ * Gets the first element in a DapList.
+ *
+ * Returns: the first element in the DapList,
+ *     or %NULL if the DapList has no elements
+ */
+dap_list_t *dap_list_first(dap_list_t *list)
+{
+    if(list)
+    {
+        while(list->prev)
+            list = list->prev;
+    }
+
+    return list;
+}
+
+/**
+ * dap_list_length:
+ * @list: a DapList, this must point to the top of the list
+ *
+ * Gets the number of elements in a DapList.
+ *
+ * This function iterates over the whole list to count its elements.
+ *
+ * Returns: the number of elements in the DapList
+ */
+unsigned int dap_list_length(dap_list_t *list)
+{
+    unsigned int length;
+
+    length = 0;
+    while(list)
+    {
+        length++;
+        list = list->next;
+    }
+
+    return length;
+}
+
+/**
+ * dap_list_foreach:
+ * @list: a DapList, this must point to the top of the list
+ * @func: the function to call with each element's data
+ * @user_data: user data to pass to the function
+ *
+ * Calls a function for each element of a DapList.
+ */
+void dap_list_foreach(dap_list_t *list, dap_callback_t func, void* user_data)
+{
+    while(list)
+    {
+        dap_list_t *next = list->next;
+        (*func)(list->data, user_data);
+        list = next;
+    }
+}
+
+static dap_list_t* dap_list_insert_sorted_real(dap_list_t *list, void* data, dap_callback_t func, void* user_data)
+{
+    dap_list_t *tmp_list = list;
+    dap_list_t *new_list;
+    int cmp;
+
+    dap_return_val_if_fail(func != NULL, list);
+
+    if(!list)
+    {
+        new_list = dap_list_alloc0();
+        new_list->data = data;
+        return new_list;
+    }
+
+    cmp = ((dap_callback_compare_data_t) func)(data, tmp_list->data, user_data);
+
+    while((tmp_list->next) && (cmp > 0))
+    {
+        tmp_list = tmp_list->next;
+
+        cmp = ((dap_callback_compare_data_t) func)(data, tmp_list->data, user_data);
+    }
+
+    new_list = dap_list_alloc0();
+    new_list->data = data;
+
+    if((!tmp_list->next) && (cmp > 0))
+            {
+        tmp_list->next = new_list;
+        new_list->prev = tmp_list;
+        return list;
+    }
+
+    if(tmp_list->prev)
+    {
+        tmp_list->prev->next = new_list;
+        new_list->prev = tmp_list->prev;
+    }
+    new_list->next = tmp_list;
+    tmp_list->prev = new_list;
+
+    if(tmp_list == list)
+        return new_list;
+    else
+        return list;
+}
+
+/**
+ * dap_list_insert_sorted:
+ * @list: a pointer to a DapList, this must point to the top of the
+ *     already sorted list
+ * @data: the data for the new element
+ * @func: the function to compare elements in the list. It should
+ *     return a number > 0 if the first parameter comes after the
+ *     second parameter in the sort order.
+ *
+ * Inserts a new element into the list, using the given comparison
+ * function to determine its position.
+ *
+ * If you are adding many new elements to a list, and the number of
+ * new elements is much larger than the length of the list, use
+ * dap_list_prepend() to add the new items and sort the list afterwards
+ * with dap_list_sort().
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_insert_sorted(dap_list_t *list, void* data, dap_callback_compare_t func)
+{
+    return dap_list_insert_sorted_real(list, data, (dap_callback_t) func, NULL);
+}
+
+/**
+ * dap_list_insert_sorted_with_data:
+ * @list: a pointer to a DapList, this must point to the top of the
+ *     already sorted list
+ * @data: the data for the new element
+ * @func: the function to compare elements in the list. It should
+ *     return a number > 0 if the first parameter  comes after the
+ *     second parameter in the sort order.
+ * @user_data: user data to pass to comparison function
+ *
+ * Inserts a new element into the list, using the given comparison 
+ * function to determine its position.
+ *
+ * If you are adding many new elements to a list, and the number of
+ * new elements is much larger than the length of the list, use
+ * dap_list_prepend() to add the new items and sort the list afterwards
+ * with dap_list_sort().
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t * dap_list_insert_sorted_with_data(dap_list_t *list, void* data, dap_callback_compare_data_t func, void* user_data)
+{
+    return dap_list_insert_sorted_real(list, data, (dap_callback_t) func, user_data);
+}
+
+static dap_list_t *dap_list_sort_merge(dap_list_t *l1, dap_list_t *l2, dap_callback_t compare_func, void* user_data)
+{
+    dap_list_t list, *l, *lprev;
+    int cmp;
+
+    l = &list;
+    lprev = NULL;
+
+    while(l1 && l2)
+    {
+        cmp = ((dap_callback_compare_data_t) compare_func)(l1->data, l2->data, user_data);
+
+        if(cmp <= 0)
+                {
+            l->next = l1;
+            l1 = l1->next;
+        }
+        else
+        {
+            l->next = l2;
+            l2 = l2->next;
+        }
+        l = l->next;
+        l->prev = lprev;
+        lprev = l;
+    }
+    l->next = l1 ? l1 : l2;
+    l->next->prev = l;
+
+    return list.next;
+}
+
+static dap_list_t *dap_list_sort_real(dap_list_t *list, dap_callback_t compare_func, void* user_data)
+{
+    dap_list_t *l1, *l2;
+
+    if(!list)
+        return NULL ;
+    if(!list->next)
+        return list;
+
+    l1 = list;
+    l2 = list->next;
+
+    while((l2 = l2->next) != NULL )
+    {
+        if((l2 = l2->next) == NULL)
+            break;
+        l1 = l1->next;
+    }
+    l2 = l1->next;
+    l1->next = NULL;
+
+    return dap_list_sort_merge(dap_list_sort_real(list, compare_func, user_data),
+            dap_list_sort_real(l2, compare_func, user_data),
+            compare_func,
+            user_data);
+}
+
+/**
+ * dap_list_sort:
+ * @list: a DapList, this must point to the top of the list
+ * @compare_func: the comparison function used to sort the DapList.
+ *     This function is passed the data from 2 elements of the DapList
+ *     and should return 0 if they are equal, a negative value if the 
+ *     first element comes before the second, or a positive value if 
+ *     the first element comes after the second.
+ *
+ * Sorts a DapList using the given comparison function. The algorithm
+ * used is a stable sort.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+/**
+ * DapCompareFunc:
+ * @a: a value
+ * @b: a value to compare with
+ *
+ * Specifies the type of a comparison function used to compare two
+ * values.  The function should return a negative integer if the first
+ * value comes before the second, 0 if they are equal, or a positive
+ * integer if the first value comes after the second.
+ *
+ * Returns: negative value if @a < @b; zero if @a = @b; positive
+ *          value if @a > @b
+ */
+dap_list_t *dap_list_sort(dap_list_t *list, dap_callback_compare_t compare_func)
+{
+    return dap_list_sort_real(list, (dap_callback_t) compare_func, NULL);
+}
+
+/**
+ * dap_list_sort_with_data:
+ * @list: a DapList, this must point to the top of the list
+ * @compare_func: comparison function
+ * @user_data: user data to pass to comparison function
+ *
+ * Like dap_list_sort(), but the comparison function accepts
+ * a user data argument.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+/**
+ * DapCompareFunc:
+ * @a: a value
+ * @b: a value to compare with
+ * @user_data: user data
+ *
+ * Specifies the type of a comparison function used to compare two
+ * values.  The function should return a negative integer if the first
+ * value comes before the second, 0 if they are equal, or a positive
+ * integer if the first value comes after the second.
+ *
+ * Returns: negative value if @a < @b; zero if @a = @b; positive
+ *          value if @a > @b
+ */
+dap_list_t *dap_list_sort_with_data(dap_list_t *list, dap_callback_compare_data_t compare_func, void* user_data)
+{
+    return dap_list_sort_real(list, (dap_callback_t) compare_func, user_data);
+}
diff --git a/src/dap_module.c b/src/dap_module.c
new file mode 100755
index 0000000..84bfbdb
--- /dev/null
+++ b/src/dap_module.c
@@ -0,0 +1,29 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2018
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "dap_common.h"
+#include "dap_module.h"
+
+#define LOG_TAG "module"
+
+
diff --git a/src/dap_strfuncs.c b/src/dap_strfuncs.c
new file mode 100755
index 0000000..6bafe20
--- /dev/null
+++ b/src/dap_strfuncs.c
@@ -0,0 +1,713 @@
+/* DAP String Functions */
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS
+#include <windows.h>
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+
+#ifdef _WIN32
+char *strndup(char *str, int len) {
+    char *buf = (char*)malloc(len + 1);
+    memcpy(buf, str, len);
+    buf[len] = 0;
+    return buf;
+}
+#endif
+
+/**
+ * dap_strlen:
+ * @a_str: (nullable): the string
+ *
+ * If @a_str is %NULL it returns 0
+ *
+ * Returns: length of the string
+ */
+size_t dap_strlen(const char *a_str)
+{
+    size_t l_length = 0;
+
+    if(a_str) {
+        l_length = strlen(a_str);
+    }
+    return l_length;
+}
+
+/**
+ * dap_strcmp:
+ * @a_str1: (nullable): the string
+ * @a_str2: (nullable): the string
+ *
+ * Compare a_str1 and a_str2
+ */
+int dap_strcmp(const char *a_str1, const char *a_str2)
+{
+    if(a_str1 && a_str2) {
+        return strcmp(a_str1, a_str2);
+    }
+    return -1;
+}
+
+/**
+ * dap_strcmp:
+ * @a_str1: (nullable): the string
+ * @a_str2: (nullable): the string
+ *
+ * Compare a_n characters of a_str1 and a_str2
+ */
+int dap_strncmp(const char *a_str1, const char *a_str2, size_t a_n)
+{
+    if(a_str1 && a_str2) {
+        return strncmp(a_str1, a_str2, a_n);
+    }
+    return -1;
+}
+
+/**
+ * dap_strdup:
+ * @a_str: (nullable): the string to duplicate
+ *
+ * Duplicates a string. If @a_str is %NULL it returns %NULL.
+ * The returned string should be freed
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated copy of @a_str
+ */
+char* dap_strdup(const char *a_str)
+{
+    char *l_new_str;
+    size_t l_length;
+
+    if(a_str)
+    {
+        l_length = (size_t) (strlen(a_str) + 1);
+        l_new_str = DAP_NEW_SIZE(char, l_length);
+        memcpy(l_new_str, a_str, l_length);
+    }
+    else
+        l_new_str = NULL;
+
+    return l_new_str;
+}
+
+/**
+ * dap_strdup_vprintf:
+ * @a_format: a standard printf() format string, but notice
+ *     [string precision pitfalls][string-precision]
+ * @a_args: the list of parameters to insert into the format string
+ *
+ * Similar to the standard C vsprintf() function but safer, since it
+ * calculates the maximum space required and allocates memory to hold
+ * the result. The returned string should be freed with DAP_DELETE()
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated string holding the result
+ */
+char* dap_strdup_vprintf(const char *a_format, va_list a_args)
+{
+    char *l_string = NULL;
+    int len = vasprintf(&l_string, a_format, a_args);
+    if(len < 0)
+        l_string = NULL;
+    return l_string;
+}
+
+/**
+ * dap_strdup_printf:
+ * @a_format: a standard printf() format string
+ *
+ * Similar to the standard C sprintf() function but safer, since it
+ * calculates the maximum space required and allocates memory to hold
+ * the result. The returned string should be freed with DAP_DELETE()
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated string holding the result
+ */
+char* dap_strdup_printf(const char *a_format, ...)
+{
+    char *l_buffer;
+    va_list l_args;
+
+    va_start(l_args, a_format);
+    l_buffer = dap_strdup_vprintf(a_format, l_args);
+    va_end(l_args);
+
+    return l_buffer;
+}
+
+/*
+ // alternative version
+ char* dap_strdup_printf2(const char *a_format, ...)
+ {
+ size_t l_buffer_size = 0;
+ char *l_buffer = NULL;
+ va_list l_args;
+
+ va_start(l_args, a_format);
+ l_buffer_size += vsnprintf(l_buffer, 0, a_format, l_args);
+ va_end(l_args);
+
+ if(!l_buffer_size)
+ return NULL;
+ l_buffer = DAP_NEW_SIZE(char, l_buffer_size + 1);
+
+ va_start(l_args, a_format);
+ vsnprintf(l_buffer, l_buffer_size + 1, a_format, l_args);
+ va_end(l_args);
+
+ return l_buffer;
+ }*/
+
+/**
+ * dap_stpcpy:
+ * @a_dest: destination buffer.
+ * @a_src: source string.
+ *
+ * Copies a null-terminated string into the dest buffer, include the
+ * trailing null, and return a pointer to the trailing null byte.
+ * This is useful for concatenating multiple strings together
+ * without having to repeatedly scan for the end.
+ *
+ * Returns: a pointer to trailing null byte.
+ **/
+char* dap_stpcpy(char *a_dest, const char *a_src)
+{
+    char *l_d = a_dest;
+    const char *l_s = a_src;
+
+    dap_return_val_if_fail(a_dest != NULL, NULL);
+    dap_return_val_if_fail(a_src != NULL, NULL);
+    do
+        *l_d++ = *l_s;
+    while(*l_s++ != '\0');
+
+    return l_d - 1;
+}
+
+/**
+ * dap_strstr_len:
+ * @a_haystack: a string
+ * @a_haystack_len: the maximum length of @a_haystack. Note that -1 is
+ *     a valid length, if @a_haystack is null-terminated, meaning it will
+ *     search through the whole string.
+ * @a_needle: the string to search for
+ *
+ * Searches the string @a_haystack for the first occurrence
+ * of the string @a_needle, limiting the length of the search
+ * to @a_haystack_len.
+ *
+ * Returns: a pointer to the found occurrence, or
+ *    %NULL if not found.
+ */
+char* dap_strstr_len(const char *a_haystack, ssize_t a_haystack_len, const char *a_needle)
+{
+    dap_return_val_if_fail(a_haystack != NULL, NULL);
+    dap_return_val_if_fail(a_needle != NULL, NULL);
+
+    if(a_haystack_len < 0)
+        return strstr(a_haystack, a_needle);
+    else
+    {
+        const char *l_p = a_haystack;
+        ssize_t l_needle_len = (ssize_t) strlen(a_needle);
+        const char *l_end;
+        ssize_t l_i;
+
+        if(l_needle_len == 0)
+            return (char *) a_haystack;
+
+        if(a_haystack_len < l_needle_len)
+            return NULL;
+
+        l_end = a_haystack + a_haystack_len - l_needle_len;
+
+        while(l_p <= l_end && *l_p)
+        {
+            for(l_i = 0; l_i < l_needle_len; l_i++)
+                if(l_p[l_i] != a_needle[l_i])
+                    goto next;
+
+            return (char *) l_p;
+
+            next:
+            l_p++;
+        }
+
+        return NULL;
+    }
+}
+
+/**
+ * dap_strjoinv:
+ * @a_separator: (allow-none): a string to insert between each of the
+ *     strings, or %NULL
+ * @a_str_array: a %NULL-terminated array of strings to join
+ *
+ * Joins a number of strings together to form one long string, with the
+ * optional @separator inserted between each of them. The returned string
+ * should be freed.
+ *
+ * If @str_array has no items, the return value will be an
+ * empty string. If @a_str_array contains a single item, @a_separator will not
+ * appear in the resulting string.
+ *
+ * Returns: a newly-allocated string containing all of the strings joined
+ *     together, with @a_separator between them
+ */
+char* dap_strjoinv(const char *a_separator, char **a_str_array)
+{
+    char *l_string;
+    char *l_ptr;
+
+    dap_return_val_if_fail(a_str_array != NULL, NULL);
+
+    if(a_separator == NULL)
+        a_separator = "";
+
+    if(*a_str_array)
+    {
+        int l_i;
+        size_t l_len;
+        size_t l_separator_len;
+
+        l_separator_len = strlen(a_separator);
+        /* First part, getting length */
+        l_len = 1 + strlen(a_str_array[0]);
+        for(l_i = 1; a_str_array[l_i] != NULL; l_i++)
+            l_len += strlen(a_str_array[l_i]);
+        l_len += l_separator_len * (l_i - 1);
+
+        /* Second part, building string */
+        l_string = DAP_NEW_SIZE(char, l_len);
+        l_ptr = dap_stpcpy(l_string, *a_str_array);
+        for(l_i = 1; a_str_array[l_i] != NULL; l_i++)
+                {
+            l_ptr = dap_stpcpy(l_ptr, a_separator);
+            l_ptr = dap_stpcpy(l_ptr, a_str_array[l_i]);
+        }
+    }
+    else
+        l_string = dap_strdup("");
+
+    return l_string;
+}
+
+/**
+ * dap_strjoin:
+ * @a_separator: (allow-none): a string to insert between each of the
+ *     strings, or %NULL
+ * @...: a %NULL-terminated list of strings to join
+ *
+ * Joins a number of strings together to form one long string, with the
+ * optional @a_separator inserted between each of them. The returned string
+ * should be freed.
+ *
+ * Returns: a newly-allocated string containing all of the strings joined
+ *     together, with @a_separator between them
+ */
+char* dap_strjoin(const char *a_separator, ...)
+{
+    char *string, *l_s;
+    va_list l_args;
+    size_t l_len;
+    size_t l_separator_len;
+    char *l_ptr;
+
+    if(a_separator == NULL)
+        a_separator = "";
+
+    l_separator_len = strlen(a_separator);
+
+    va_start(l_args, a_separator);
+
+    l_s = va_arg(l_args, char*);
+
+    if(l_s)
+    {
+        /* First part, getting length */
+        l_len = 1 + strlen(l_s);
+
+        l_s = va_arg(l_args, char*);
+        while(l_s)
+        {
+            l_len += l_separator_len + strlen(l_s);
+            l_s = va_arg(l_args, char*);
+        }
+        va_end(l_args);
+
+        /* Second part, building string */
+        string = DAP_NEW_SIZE(char, l_len);
+
+        va_start(l_args, a_separator);
+
+        l_s = va_arg(l_args, char*);
+        l_ptr = dap_stpcpy(string, l_s);
+
+        l_s = va_arg(l_args, char*);
+        while(l_s)
+        {
+            l_ptr = dap_stpcpy(l_ptr, a_separator);
+            l_ptr = dap_stpcpy(l_ptr, l_s);
+            l_s = va_arg(l_args, char*);
+        }
+    }
+    else
+        string = dap_strdup("");
+
+    va_end(l_args);
+
+    return string;
+}
+
+typedef struct _dap_slist dap_slist;
+
+struct _dap_slist
+{
+    void* data;
+    dap_slist *next;
+};
+
+static dap_slist* dap_slist_prepend(dap_slist *a_list, void* a_data)
+{
+    dap_slist *l_new_list;
+
+    l_new_list = DAP_NEW_Z(dap_slist);
+    l_new_list->data = a_data;
+    l_new_list->next = a_list;
+
+    return l_new_list;
+}
+
+static void dap_slist_free(dap_slist *a_list)
+{
+    if(a_list)
+    {
+        dap_slist *l_cur_node;
+        dap_slist *l_last_node = a_list;
+        while(l_last_node)
+        {
+            l_cur_node = l_last_node;
+            l_last_node = l_last_node->next;
+            DAP_DELETE(l_cur_node);
+        }
+    }
+}
+
+/**
+ * dap_strsplit:
+ * @a_string: a string to split
+ * @a_delimiter: a string which specifies the places at which to split
+ *     the string. The delimiter is not included in any of the resulting
+ *     strings, unless @a_max_tokens is reached.
+ * @a_max_tokens: the maximum number of pieces to split @a_string into.
+ *     If this is less than 1, the string is split completely.
+ *
+ * Splits a string into a maximum of @a_max_tokens pieces, using the given
+ * @a_delimiter. If @a_max_tokens is reached, the remainder of @a_string is
+ * appended to the last token.
+ *
+ * As an example, the result of dap_strsplit (":a:bc::d:", ":", -1) is a
+ * %NULL-terminated vector containing the six strings "", "a", "bc", "", "d"
+ * and "".
+ *
+ * As a special case, the result of splitting the empty string "" is an empty
+ * vector, not a vector containing a single string. The reason for this
+ * special case is that being able to represent a empty vector is typically
+ * more useful than consistent handling of empty elements. If you do need
+ * to represent empty elements, you'll need to check for the empty string
+ * before calling dap_strsplit().
+ *
+ * Returns: a newly-allocated %NULL-terminated array of strings.
+ * Use dap_strfreev() to free it.
+ */
+char** dap_strsplit(const char *a_string, const char *a_delimiter, int a_max_tokens)
+{
+    dap_slist *l_string_list = NULL, *l_slist;
+    char **l_str_array, *l_s;
+    uint32_t l_n = 1;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+    dap_return_val_if_fail(a_delimiter != NULL, NULL);
+
+    if(a_max_tokens < 1)
+        a_max_tokens = INT_MAX;
+
+    l_s = strstr(a_string, a_delimiter);
+    if(l_s)
+    {
+        uint32_t delimiter_len = (uint32_t) strlen(a_delimiter);
+
+        do
+        {
+            uint32_t len;
+            char *new_string;
+
+            len = (uint32_t) (l_s - a_string);
+            new_string = DAP_NEW_SIZE(char, len + 1);
+            strncpy(new_string, a_string, len);
+            new_string[len] = 0;
+            l_string_list = dap_slist_prepend(l_string_list, new_string);
+            l_n++;
+            a_string = l_s + delimiter_len;
+            l_s = strstr(a_string, a_delimiter);
+        }
+        while(--a_max_tokens && l_s);
+    }
+    l_string_list = dap_slist_prepend(l_string_list, dap_strdup(a_string));
+
+    l_str_array = DAP_NEW_SIZE(char*, (l_n + 1) * sizeof(char*));
+
+    l_str_array[l_n--] = NULL;
+    for(l_slist = l_string_list; l_slist; l_slist = l_slist->next)
+        l_str_array[l_n--] = l_slist->data;
+
+    dap_slist_free(l_string_list);
+
+    return l_str_array;
+}
+
+size_t dap_str_countv(char **a_str_array)
+{
+    size_t l_i = 0;
+    if(a_str_array)
+    {
+        for(l_i = 0; a_str_array[l_i] != NULL; l_i++)
+            ;
+    }
+    return l_i;
+}
+
+/**
+ * dap_strdupv:
+ * @a_str_array: (nullable): a %NULL-terminated array of strings
+ *
+ * Copies %NULL-terminated array of strings. The copy is a deep copy;
+ * the new array should be freed by first freeing each string, then
+ * the array itself. g_strfreev() does this for you. If called
+ * on a %NULL value, g_strdupv() simply returns %NULL.
+ *
+ * Returns: (nullable): a new %NULL-terminated array of strings.
+ */
+char** dap_strdupv(const char **a_str_array)
+{
+    if(a_str_array)
+    {
+        int l_i;
+        char **l_retval;
+
+        l_i = 0;
+        while(a_str_array[l_i])
+            ++l_i;
+
+        l_retval = DAP_NEW_SIZE(char*, (l_i + 1) * sizeof(char*));
+
+        l_i = 0;
+        while(a_str_array[l_i])
+        {
+            l_retval[l_i] = dap_strdup(a_str_array[l_i]);
+            ++l_i;
+        }
+        l_retval[l_i] = NULL;
+
+        return l_retval;
+    }
+    else
+        return NULL;
+}
+
+/**
+ * dap_strfreev:
+ * @a_str_array: (nullable): a %NULL-terminated array of strings to free
+ *
+ * Frees a %NULL-terminated array of strings, as well as each
+ * string it contains.
+ *
+ * If @a_str_array is %NULL, this function simply returns.
+ */
+void dap_strfreev(char **a_str_array)
+{
+    if(a_str_array)
+    {
+        int l_i;
+        for(l_i = 0; a_str_array[l_i] != NULL; l_i++)
+            DAP_DELETE(a_str_array[l_i]);
+
+        DAP_DELETE(a_str_array);
+    }
+}
+
+/**
+ * dap_strchug:
+ * @a_string: a string to remove the leading whitespace from
+ *
+ * Removes leading whitespace from a string, by moving the rest
+ * of the characters forward.
+ *
+ * This function doesn't allocate or reallocate any memory;
+ * it modifies @a_string in place. Therefore, it cannot be used on
+ * statically allocated strings.
+ *
+ * The pointer to @a_string is returned to allow the nesting of functions.
+ * Returns: @a_string
+ */
+char* dap_strchug(char *a_string)
+{
+    unsigned char *l_start;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    for(l_start = (unsigned char*) a_string; *l_start && dap_ascii_isspace(*l_start); l_start++)
+        ;
+
+    memmove(a_string, l_start, strlen((char *) l_start) + 1);
+
+    return a_string;
+}
+
+/**
+ * dap_strchomp:
+ * @a_string: a string to remove the trailing whitespace from
+ *
+ * Removes trailing whitespace from a string.
+ *
+ * This function doesn't allocate or reallocate any memory;
+ * it modifies @a_string in place. Therefore, it cannot be used
+ * on statically allocated strings.
+ *
+ * The pointer to @a_string is returned to allow the nesting of functions.
+ *
+ * Returns: @a_string
+ */
+char* dap_strchomp(char *a_string)
+{
+    size_t l_len;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    l_len = (size_t) strlen(a_string);
+    while(l_len--)
+    {
+        if(dap_ascii_isspace((unsigned char ) a_string[l_len]))
+            a_string[l_len] = '\0';
+        else
+            break;
+    }
+    return a_string;
+}
+
+/**
+ * dap_strup:
+ * @a_str: a string
+ * @a_len: length of @a_str in bytes, or -1 if @a_str is nul-terminated
+ *
+ * Converts all lower case ASCII letters to upper case ASCII letters.
+ *
+ * Returns: a newly allocated string, with all the lower case
+ *     characters in @a_str converted to upper case
+ */
+char* dap_strup(const char *a_str, ssize_t a_len)
+{
+    char *l_result, *l_s;
+
+    dap_return_val_if_fail(a_str != NULL, NULL);
+
+    if(a_len < 0)
+        a_len = strlen(a_str);
+
+    l_result = strndup(a_str, a_len);
+    for(l_s = l_result; *l_s; l_s++)
+        *l_s = toupper(*l_s);
+
+    return l_result;
+}
+
+/**
+ * dap_strdown:
+ * @a_str: a string
+ * @a_len: length of @a_str in bytes, or -1 if @a_str is nul-terminated
+ *
+ * Converts all upper case ASCII letters to lower case ASCII letters.
+ *
+ * Returns: a newly-allocated string, with all the upper case
+ *     characters in @a_str converted to lower case
+ */
+char* dap_strdown(const char *a_str, ssize_t a_len)
+{
+    char *l_result, *l_s;
+
+    dap_return_val_if_fail(a_str != NULL, NULL);
+
+    if(a_len < 0)
+        a_len = strlen(a_str);
+
+    l_result = strndup(a_str, a_len);
+    for(l_s = l_result; *l_s; l_s++)
+        *l_s = tolower(*l_s);
+
+    return l_result;
+}
+
+/**
+ * dap_strreverse:
+ * @a_string: the string to reverse
+ *
+ * Reverses all of the bytes in a string. For example,
+ * `dap_strreverse("abcdef")` will result in "fedcba".
+ *
+ * Note that g_strreverse() doesn't work on UTF-8 strings
+ * containing multibyte characters.
+ *
+ * Returns: the same pointer passed in as @a_string
+ */
+char* dap_strreverse(char *a_string)
+{
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    if(*a_string)
+    {
+        register char *l_h, *l_t;
+
+        l_h = a_string;
+        l_t = a_string + strlen(a_string) - 1;
+
+        while(l_h < l_t)
+        {
+            register char l_c;
+
+            l_c = *l_h;
+            *l_h = *l_t;
+            l_h++;
+            *l_t = l_c;
+            l_t--;
+        }
+    }
+
+    return a_string;
+}
+
+/**
+ * dap_usleep:
+ * @a_microseconds: number of microseconds to pause
+ *
+ * Pauses the current thread for the given number of microseconds.
+ */
+void dap_usleep(time_t a_microseconds)
+{
+#ifdef _WIN32
+    Sleep (a_microseconds / 1000);
+#else
+    struct timespec l_request, l_remaining;
+    l_request.tv_sec = a_microseconds / DAP_USEC_PER_SEC;
+    l_request.tv_nsec = 1000 * (a_microseconds % DAP_USEC_PER_SEC);
+    while(nanosleep(&l_request, &l_remaining) == -1 && errno == EINTR)
+        l_request = l_remaining;
+#endif
+}
+
diff --git a/src/dap_string.c b/src/dap_string.c
new file mode 100755
index 0000000..a707232
--- /dev/null
+++ b/src/dap_string.c
@@ -0,0 +1,917 @@
+// dap_string_t is an object that handles the memory management of a C string for you.
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_string.h"
+
+#define MY_MAXSIZE ((size_t)-1)
+
+static inline size_t nearest_power(size_t a_base, size_t a_num)
+{
+    if(a_num > MY_MAXSIZE / 2) {
+        return MY_MAXSIZE;
+    }
+    else {
+        size_t l_n = a_base;
+
+        while(l_n < a_num)
+            l_n <<= 1;
+
+        return l_n;
+    }
+}
+
+static void dap_string_maybe_expand(dap_string_t *a_string, size_t a_len)
+{
+    if(a_string->len + a_len >= a_string->allocated_len) {
+        a_string->allocated_len = nearest_power(1, a_string->len + a_len + 1);
+
+        /*char *l_str = DAP_NEW_Z_SIZE(char, a_string->allocated_len);
+        if(a_string->str)
+            strcpy(l_str, a_string->str);
+        DAP_DELETE(a_string->str);
+        a_string->str = l_str;*/
+        a_string->str = DAP_REALLOC(a_string->str, a_string->allocated_len);
+    }
+}
+
+/**
+ * dap_string_sized_new:
+ * @a_a_dfl_size: the default size of the space allocated to
+ *     hold the string
+ *
+ * Creates a new #dap_string_t, with enough space for @a_a_dfl_size
+ * bytes. This is useful if you are going to add a lot of
+ * text to the string and don't want it to be reallocated
+ * too often.
+ *
+ * Returns: the new #dap_string_t
+ */
+dap_string_t * dap_string_sized_new(size_t a_dfl_size)
+{
+    dap_string_t *l_string = DAP_NEW(dap_string_t);
+
+    l_string->allocated_len = 0;
+    l_string->len = 0;
+    l_string->str = NULL;
+
+    dap_string_maybe_expand(l_string, max(a_dfl_size, 2));
+    l_string->str[0] = 0;
+
+    return l_string;
+}
+
+/**
+ * dap_string_new:
+ * @a_a_init: (nullable): the initial text to copy into the string, or %NULL to
+ * start with an empty string
+ *
+ * Creates a new #dap_string_t, initialized with the given string.
+ *
+ * Returns: the new #dap_string_t
+ */
+dap_string_t* dap_string_new(const char *a_init)
+{
+    dap_string_t *l_string;
+
+    if(a_init == NULL || *a_init == '\0')
+        l_string = dap_string_sized_new(2);
+    else
+    {
+        int len;
+
+        len = strlen(a_init);
+        l_string = dap_string_sized_new(len + 2);
+
+        dap_string_append_len(l_string, a_init, len);
+    }
+
+    return l_string;
+}
+
+/**
+ * dap_string_new_len:
+ * @a_init: initial contents of the string
+ * @a_len: length of @a_init to use
+ *
+ * Creates a new #dap_string_t with @a_len bytes of the @a_init buffer.
+ * Because a length is provided, @a_init need not be nul-terminated,
+ * and can contain embedded nul bytes.
+ *
+ * Since this function does not stop at nul bytes, it is the caller's
+ * responsibility to ensure that @a_init has at least @a_len addressable
+ * bytes.
+ *
+ * Returns: a new #dap_string_t
+ */
+dap_string_t* dap_string_new_len(const char *a_init, ssize_t a_len)
+{
+    dap_string_t *l_string;
+
+    if(a_len < 0)
+        return dap_string_new(a_init);
+    else
+    {
+        l_string = dap_string_sized_new(a_len);
+
+        if(a_init)
+            dap_string_append_len(l_string, a_init, a_len);
+
+        return l_string;
+    }
+}
+
+/**
+ * dap_string_free:
+ * @a_string: (transfer full): a #dap_string_t
+ * @a_free_segment: if %true, the actual character data is freed as well
+ *
+ * Frees the memory allocated for the #dap_string_t.
+ * If @a_free_segment is %true it also frees the character data.  If
+ * it's %false, the caller gains ownership of the buffer and must
+ * free it after use with dap_free().
+ *
+ * Returns: (nullable): the character data of @a_string
+ *          (i.e. %NULL if @a_free_segment is %true)
+ */
+char* dap_string_free(dap_string_t *a_string, bool a_free_segment)
+{
+    char *l_segment;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    if(a_free_segment)
+    {
+        DAP_DELETE(a_string->str);
+        l_segment = NULL;
+    }
+    else
+        l_segment = a_string->str;
+
+    DAP_DELETE(a_string);
+
+    return l_segment;
+}
+
+/**
+ * dap_string_equal:
+ * @a_v: a #dap_string_t
+ * @a_v2: another #dap_string_t
+ *
+ * Compares two strings for equality, returning %true if they are equal.
+ * For use with #GHashTable.
+ *
+ * Returns: %true if the strings are the same length and contain the
+ *     same bytes
+ */
+bool dap_string_equal(const dap_string_t *a_v, const dap_string_t *a_v2)
+{
+    char *p, *q;
+    dap_string_t *l_string1 = (dap_string_t *) a_v;
+    dap_string_t *l_string2 = (dap_string_t *) a_v2;
+    size_t i = l_string1->len;
+
+    if(i != l_string2->len)
+        return false;
+
+    p = l_string1->str;
+    q = l_string2->str;
+    while(i)
+    {
+        if(*p != *q)
+            return false;
+        p++;
+        q++;
+        i--;
+    }
+    return true;
+}
+
+/**
+ * dap_string_hash:
+ * @a_str: a string to hash
+ *
+ * Creates a hash code for @a_str
+ *
+ * Returns: hash code for @a_str
+ */
+unsigned int dap_string_hash(const dap_string_t *a_str)
+{
+    const char *p = a_str->str;
+    size_t n = a_str->len;
+    unsigned int h = 0;
+
+    /* 31 bit hash function */
+    while(n--)
+    {
+        h = (h << 5) - h + *p;
+        p++;
+    }
+
+    return h;
+}
+
+/**
+ * dap_string_assign:
+ * @a_string: the destination #dap_string_t. Its current contents
+ *          are destroyed.
+ * @a_rval: the string to copy into @a_string
+ *
+ * Copies the bytes from a string into a #dap_string_t,
+ * destroying any previous contents. It is rather like
+ * the standard strcpy() function, except that you do not
+ * have to worry about having enough space to copy the string.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_assign(dap_string_t *a_string, const char *a_rval)
+{
+    dap_return_val_if_fail(a_string != NULL, NULL);
+    dap_return_val_if_fail(a_rval != NULL, a_string);
+
+    /* Make sure assigning to itself doesn't corrupt the string. */
+    if(a_string->str != a_rval)
+            {
+        /* Assigning from substring should be ok, since
+         * dap_string_truncate() does not reallocate.
+         */
+        dap_string_truncate(a_string, 0);
+        dap_string_append(a_string, a_rval);
+    }
+
+    return a_string;
+}
+
+/**
+ * dap_string_truncate:
+ * @a_string: a #dap_string_t
+ * @a_len: the new size of @a_string
+ *
+ * Cuts off the end of the dap_string_t, leaving the first @a_len bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_truncate(dap_string_t *string, size_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    string->len = min(len, string->len);
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_set_size:
+ * @a_string: a #dap_string_t
+ * @a_len: the new length
+ *
+ * Sets the length of a #dap_string_t. If the length is less than
+ * the current length, the string will be truncated. If the
+ * length is greater than the current length, the contents
+ * of the newly added area are undefined. (However, as
+ * always, string->str[string->len] will be a nul byte.)
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_set_size(dap_string_t *string, size_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    if(len >= string->allocated_len)
+        dap_string_maybe_expand(string, len - string->len);
+
+    string->len = len;
+    string->str[len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_insert_len:
+ * @a_string: a #dap_string_t
+ * @a_pos: position in @a_string where insertion should
+ *       happen, or -1 for at the end
+ * @a_val: bytes to insert
+ * @a_len: number of bytes of @a_val to insert
+ *
+ * Inserts @a_len bytes of @a_val into @a_string at @a_pos.
+ * Because @a_len is provided, @a_val may contain embedded
+ * nuls and need not be nul-terminated. If @a_pos is -1,
+ * bytes are inserted at the end of the string.
+ *
+ * Since this function does not stop at nul bytes, it is
+ * the caller's responsibility to ensure that @a_val has at
+ * least @a_len addressable bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert_len(dap_string_t *string, ssize_t pos, const char *val, ssize_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+    dap_return_val_if_fail(len == 0 || val != NULL, string);
+
+    if(len == 0)
+        return string;
+
+    if(len < 0)
+        len = strlen(val);
+
+    if(pos < 0)
+        pos = string->len;
+    else
+        dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    /* Check whether val represents a substring of string.
+     * This test probably violates chapter and verse of the C standards,
+     * since ">=" and "<=" are only valid when val really is a substring.
+     * In practice, it will work on modern archs.
+     */
+    if(val >= string->str && val <= string->str + string->len)
+            {
+        size_t offset = val - string->str;
+        size_t precount = 0;
+
+        dap_string_maybe_expand(string, len);
+        val = string->str + offset;
+        /* At this point, val is valid again.  */
+
+        /* Open up space where we are going to insert.  */
+        if((size_t) pos < string->len)
+            memmove(string->str + pos + len, string->str + pos, string->len - pos);
+
+        /* Move the source part before the gap, if any.  */
+        if(offset < (size_t) pos) {
+            precount = min(len, pos - (ssize_t )offset);
+            memcpy(string->str + pos, val, precount);
+        }
+
+        /* Move the source part after the gap, if any.  */
+        if((size_t) len > precount)
+            memcpy(string->str + pos + precount,
+                    val + /* Already moved: */precount + /* Space opened up: */len,
+                    len - precount);
+    }
+    else
+    {
+        dap_string_maybe_expand(string, len);
+
+        /* If we aren't appending at the end, move a hunk
+         * of the old string to the end, opening up space
+         */
+        if((size_t) pos < string->len)
+            memmove(string->str + pos + len, string->str + pos, string->len - pos);
+
+        /* insert the new string */
+        if(len == 1)
+            string->str[pos] = *val;
+        else
+            memcpy(string->str + pos, val, len);
+    }
+
+    string->len += len;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_append:
+ * @a_string: a #dap_string_t
+ * @a_val: the string to append onto the end of @a_string
+ *
+ * Adds a string onto the end of a #dap_string_t, expanding
+ * it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_append(dap_string_t *string, const char *val)
+{
+    return dap_string_insert_len(string, -1, val, -1);
+}
+
+/**
+ * dap_string_append_len:
+ * @a_string: a #dap_string_t
+ * @a_val: bytes to append
+ * @a_len: number of bytes of @a_val to use
+ *
+ * Appends @a_len bytes of @a_val to @a_string. Because @a_len is
+ * provided, @a_val may contain embedded nuls and need not
+ * be nul-terminated.
+ *
+ * Since this function does not stop at nul bytes, it is
+ * the caller's responsibility to ensure that @a_val has at
+ * least @a_len addressable bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_append_len(dap_string_t *string, const char *val, ssize_t len)
+{
+    return dap_string_insert_len(string, -1, val, len);
+}
+
+/**
+ * dap_string_append_c:
+ * @a_string: a #dap_string_t
+ * @a_c: the byte to append onto the end of @a_string
+ *
+ * Adds a byte onto the end of a #dap_string_t, expanding
+ * it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+#undef dap_string_append_c
+dap_string_t* dap_string_append_c(dap_string_t *string, char c)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_c(string, -1, c);
+}
+
+/**
+ * dap_string_append_unichar:
+ * @a_string: a #dap_string_t
+ * @a_wc: a Unicode character
+ *
+ * Converts a Unicode character into UTF-8, and appends it
+ * to the string.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_append_unichar(dap_string_t *string, uint32_t wc)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_unichar(string, -1, wc);
+}
+
+/**
+ * dap_string_prepend:
+ * @a_string: a #dap_string_t
+ * @a_val: the string to prepend on the start of @a_string
+ *
+ * Adds a string on to the start of a #dap_string_t,
+ * expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend(dap_string_t *string, const char *val)
+{
+    return dap_string_insert_len(string, 0, val, -1);
+}
+
+/**
+ * dap_string_prepend_len:
+ * @a_string: a #dap_string_t
+ * @a_val: bytes to prepend
+ * @a_len: number of bytes in @a_val to prepend
+ *
+ * Prepends @a_len bytes of @a_val to @a_string.
+ * Because @a_len is provided, @a_val may contain
+ * embedded nuls and need not be nul-terminated.
+ *
+ * Since this function does not stop at nul bytes,
+ * it is the caller's responsibility to ensure that
+ * @a_val has at least @a_len addressable bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend_len(dap_string_t *string, const char *val, ssize_t len)
+{
+    return dap_string_insert_len(string, 0, val, len);
+}
+
+/**
+ * dap_string_prepend_c:
+ * @a_string: a #dap_string_t
+ * @a_c: the byte to prepend on the start of the #dap_string_t
+ *
+ * Adds a byte onto the start of a #dap_string_t,
+ * expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend_c(dap_string_t *string, char c)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_c(string, 0, c);
+}
+
+/**
+ * dap_string_prepend_unichar:
+ * @a_string: a #dap_string_t
+ * @a_wc: a Unicode character
+ *
+ * Converts a Unicode character into UTF-8, and prepends it
+ * to the string.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend_unichar(dap_string_t *string, uint32_t wc)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_unichar(string, 0, wc);
+}
+
+/**
+ * dap_string_insert:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position to insert the copy of the string
+ * @a_val: the string to insert
+ *
+ * Inserts a copy of a string into a #dap_string_t,
+ * expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert(dap_string_t *string, ssize_t pos, const char *val)
+{
+    return dap_string_insert_len(string, pos, val, -1);
+}
+
+/**
+ * dap_string_insert_c:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position to insert the byte
+ * @a_c: the byte to insert
+ *
+ * Inserts a byte into a #dap_string_t, expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert_c(dap_string_t *string, ssize_t pos, char c)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    dap_string_maybe_expand(string, 1);
+
+    if(pos < 0)
+        pos = string->len;
+    else
+        dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    /* If not just an append, move the old stuff */
+    if((size_t) pos < string->len)
+        memmove(string->str + pos + 1, string->str + pos, string->len - pos);
+
+    string->str[pos] = c;
+
+    string->len += 1;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_insert_unichar:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position at which to insert character, or -1
+ *     to append at the end of the string
+ * @a_wc: a Unicode character
+ *
+ * Converts a Unicode character into UTF-8, and insert it
+ * into the string at the given position.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert_unichar(dap_string_t *string, ssize_t pos, uint32_t wc)
+{
+    int charlen, first, i;
+    char *dest;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    /* Code copied from dap_unichar_to_utf() */
+    if(wc < 0x80)
+            {
+        first = 0;
+        charlen = 1;
+    }
+    else if(wc < 0x800)
+            {
+        first = 0xc0;
+        charlen = 2;
+    }
+    else if(wc < 0x10000)
+            {
+        first = 0xe0;
+        charlen = 3;
+    }
+    else if(wc < 0x200000)
+            {
+        first = 0xf0;
+        charlen = 4;
+    }
+    else if(wc < 0x4000000)
+            {
+        first = 0xf8;
+        charlen = 5;
+    }
+    else
+    {
+        first = 0xfc;
+        charlen = 6;
+    }
+    /* End of copied code */
+
+    dap_string_maybe_expand(string, charlen);
+
+    if(pos < 0)
+        pos = string->len;
+    else
+        dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    /* If not just an append, move the old stuff */
+    if((size_t) pos < string->len)
+        memmove(string->str + pos + charlen, string->str + pos, string->len - pos);
+
+    dest = string->str + pos;
+    /* Code copied from dap_unichar_to_utf() */
+    for(i = charlen - 1; i > 0; --i)
+            {
+        dest[i] = (wc & 0x3f) | 0x80;
+        wc >>= 6;
+    }
+    dest[0] = wc | first;
+    /* End of copied code */
+
+    string->len += charlen;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_overwrite:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position at which to start overwriting
+ * @a_val: the string that will overwrite the @a_string starting at @a_pos
+ *
+ * Overwrites part of a string, lengthening it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_overwrite(dap_string_t *string, size_t pos, const char *val)
+{
+    dap_return_val_if_fail(val != NULL, string);
+    return dap_string_overwrite_len(string, pos, val, strlen(val));
+}
+
+/**
+ * dap_string_overwrite_len:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position at which to start overwriting
+ * @a_val: the string that will overwrite the @a_string starting at @a_pos
+ * @a_len: the number of bytes to write from @a_val
+ *
+ * Overwrites part of a string, lengthening it if necessary.
+ * This function will work with embedded nuls.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_overwrite_len(dap_string_t *string, size_t pos, const char *val, ssize_t len)
+{
+    size_t end;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    if(!len)
+        return string;
+
+    dap_return_val_if_fail(val != NULL, string);
+    dap_return_val_if_fail(pos <= string->len, string);
+
+    if(len < 0)
+        len = strlen(val);
+
+    end = pos + len;
+
+    if(end > string->len)
+        dap_string_maybe_expand(string, end - string->len);
+
+    memcpy(string->str + pos, val, len);
+
+    if(end > string->len)
+            {
+        string->str[end] = '\0';
+        string->len = end;
+    }
+
+    return string;
+}
+
+/**
+ * dap_string_erase:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position of the content to remove
+ * @a_len: the number of bytes to remove, or -1 to remove all
+ *       following bytes
+ *
+ * Removes @a_len bytes from a #dap_string_t, starting at position @a_pos.
+ * The rest of the #dap_string_t is shifted down to fill the gap.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_erase(dap_string_t *string, ssize_t pos, ssize_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+    dap_return_val_if_fail(pos >= 0, string);
+    dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    if(len < 0)
+        len = string->len - pos;
+    else
+    {
+        dap_return_val_if_fail((size_t )(pos + len) <= string->len, string);
+
+        if((size_t) (pos + len) < string->len)
+            memmove(string->str + pos, string->str + pos + len, string->len - (pos + len));
+    }
+
+    string->len -= len;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_down:
+ * @a_string: a #dap_string_t
+ *
+ * Converts a #dap_string_t to lowercase.
+ *
+ * Returns: (transfer none): the #dap_string_t
+ *
+ * Deprecated:2.2: This function uses the locale-specific
+ *     tolower() function, which is almost never the right thing.
+ *     Use dap_string_ascii_down() or dap_utf8_strdown() instead.
+ */
+dap_string_t* dap_string_down(dap_string_t *string)
+{
+    uint8_t *s;
+    long n;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    n = string->len;
+    s = (uint8_t *) string->str;
+
+    while(n)
+    {
+        if(isupper(*s))
+            *s = tolower(*s);
+        s++;
+        n--;
+    }
+
+    return string;
+}
+
+/**
+ * dap_string_up:
+ * @a_string: a #dap_string_t
+ *
+ * Converts a #dap_string_t to uppercase.
+ *
+ * Returns: (transfer none): @a_string
+ *
+ * Deprecated:2.2: This function uses the locale-specific
+ *     toupper() function, which is almost never the right thing.
+ *     Use dap_string_ascii_up() or dap_utf8_strup() instead.
+ */
+dap_string_t* dap_string_up(dap_string_t *string)
+{
+    uint8_t *s;
+    long n;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    n = string->len;
+    s = (uint8_t *) string->str;
+
+    while(n)
+    {
+        if(islower(*s))
+            *s = toupper(*s);
+        s++;
+        n--;
+    }
+
+    return string;
+}
+
+/**
+ * dap_string_append_vprintf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @a_args: the list of arguments to insert in the output
+ *
+ * Appends a formatted string onto the end of a #dap_string_t.
+ * This function is similar to dap_string_append_printf()
+ * except that the arguments to the format string are passed
+ * as a va_list.
+ */
+void dap_string_append_vprintf(dap_string_t *string, const char *format, va_list args)
+{
+    char *buf;
+    int len;
+
+    dap_return_if_fail(string != NULL);
+    dap_return_if_fail(format != NULL);
+
+    len = vasprintf(&buf, format, args);
+
+    if(len >= 0) {
+        dap_string_maybe_expand(string, len);
+        memcpy(string->str + string->len, buf, len + 1);
+        string->len += len;
+        DAP_DELETE(buf);
+    }
+}
+
+/**
+ * dap_string_vprintf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @a_args: the parameters to insert into the format string
+ *
+ * Writes a formatted string into a #dap_string_t.
+ * This function is similar to dap_string_printf() except that
+ * the arguments to the format string are passed as a va_list.
+ */
+void dap_string_vprintf(dap_string_t *string, const char *format, va_list args)
+{
+    dap_string_truncate(string, 0);
+    dap_string_append_vprintf(string, format, args);
+}
+
+/**
+ * dap_string_sprintf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the sprintf() documentation
+ * @...: the parameters to insert into the format string
+ *
+ * Writes a formatted string into a #dap_string_t.
+ * This is similar to the standard sprintf() function,
+ * except that the #dap_string_t buffer automatically expands
+ * to contain the results. The previous contents of the
+ * #dap_string_t are destroyed.
+ *
+ * Deprecated: This function has been renamed to dap_string_printf().
+ */
+
+/**
+ * dap_string_printf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @...: the parameters to insert into the format string
+ *
+ * Writes a formatted string into a #dap_string_t.
+ * This is similar to the standard sprintf() function,
+ * except that the #dap_string_t buffer automatically expands
+ * to contain the results. The previous contents of the
+ * #dap_string_t are destroyed.
+ */
+void dap_string_printf(dap_string_t *string, const char *format, ...)
+{
+    va_list args;
+
+    dap_string_truncate(string, 0);
+
+    va_start(args, format);
+    dap_string_append_vprintf(string, format, args);
+    va_end(args);
+}
+
+/**
+ * dap_string_append_printf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @...: the parameters to insert into the format string
+ *
+ * Appends a formatted string onto the end of a #dap_string_t.
+ * This function is similar to dap_string_printf() except
+ * that the text is appended to the #dap_string_t.
+ */
+void dap_string_append_printf(dap_string_t *string, const char *format, ...)
+{
+    va_list args;
+
+    va_start(args, format);
+    dap_string_append_vprintf(string, format, args);
+    va_end(args);
+}
diff --git a/src/darwin/CMakeLists.txt b/src/darwin/CMakeLists.txt
new file mode 100755
index 0000000..8f981d5
--- /dev/null
+++ b/src/darwin/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.0)
+
+project (dap_core_darwin)
+
+file(GLOB CORE_DARWIN_SRCS *.c)
+file(GLOB CORE_DARWIN_HEADERS *.h)
+
+if(DARWIN)
+    file(GLOB CORE_MACOS_SRCS macos/*.c)
+    file(GLOB CORE_MACOS_HEADERS macos/*.h)
+endif()
+
+add_library(${PROJECT_NAME} STATIC ${CORE_DARWIN_SRCS} ${CORE_DARWIN_HEADERS}
+    ${CORE_MACOS_SRCS} ${CORE_MACOS_HEADERS})
+
+target_link_libraries(${PROJECT_NAME} dap_core pthread)
+
diff --git a/src/darwin/darwin.pri b/src/darwin/darwin.pri
new file mode 100755
index 0000000..d8afafc
--- /dev/null
+++ b/src/darwin/darwin.pri
@@ -0,0 +1,5 @@
+macos {
+    include(macos/macos.pri)
+}
+
+INCLUDEPATH += $$PWD
diff --git a/src/darwin/macos/dap_network_monitor.c b/src/darwin/macos/dap_network_monitor.c
new file mode 100755
index 0000000..f66c715
--- /dev/null
+++ b/src/darwin/macos/dap_network_monitor.c
@@ -0,0 +1,329 @@
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFDictionary.h>
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFString.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dap_network_monitor.h"
+#include "dap_common.h"
+#include "pthread_barrier.h"
+
+
+#define LOG_TAG "dap_network_monitor"
+
+static SCDynamicStoreRef s_store = NULL;
+static CFRunLoopSourceRef s_rls;
+#define __bridge
+
+static void* network_monitor_worker(void *arg);
+
+static struct {
+    CFRunLoopRef rlref;
+    pthread_t thread;
+    dap_network_monitor_notification_callback_t callback;
+} _net_notification;
+
+
+void watch_for_network_changes()
+{
+    SCDynamicStoreContext context = { 0, (void *)s_store, NULL, NULL, NULL };
+
+    s_store = SCDynamicStoreCreate(NULL, CFSTR("watch_for_network_changes"), _net_notification.callback, &context);
+    if (!s_store) {
+        log_it(L_ERROR, "Could not open session with config.error = %s\n", SCErrorString(SCError()));
+        return;
+    }
+
+/*
+* establish and register dynamic store keys to watch
+* - global IPv4 configuration changes (e.g. new default route)
+* - per-service IPv4 state changes (IP service added/removed/...)
+*/
+    CFStringRef           key1 =    SCDynamicStoreKeyCreateNetworkGlobalEntity      (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+    CFStringRef           key2 =    SCDynamicStoreKeyCreateNetworkInterfaceEntity   (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
+    CFStringRef           key3 =    SCDynamicStoreKeyCreateNetworkServiceEntity     (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
+    CFStringRef           pattern1  = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
+    CFStringRef           pattern2  = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetInterface);
+    CFStringRef           pattern3  = SCDynamicStoreKeyCreateNetworkGlobalEntity    (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+    CFStringRef           pattern4  = SCDynamicStoreKeyCreateNetworkGlobalEntity    (NULL, kSCDynamicStoreDomainState, kSCEntNetInterface);
+    CFStringRef           pattern5  = SCDynamicStoreKeyCreateNetworkServiceEntity   (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
+    CFStringRef           pattern6  = SCDynamicStoreKeyCreateNetworkServiceEntity   (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetInterface);
+    CFMutableArrayRef     keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+    CFMutableArrayRef     patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+    if (!key1 || !key2 || !key3 || !keys || !pattern1 || !pattern2 || !pattern3 || !pattern4 || !pattern5 || !pattern6 || !patterns) goto error;
+
+    CFArrayAppendValue(keys, key1);
+    CFArrayAppendValue(keys, key2);
+    CFArrayAppendValue(keys, key3);
+    CFArrayAppendValue(patterns, pattern1);
+    CFArrayAppendValue(patterns, pattern2);
+    CFArrayAppendValue(patterns, pattern3);
+    CFArrayAppendValue(patterns, pattern4);
+    CFArrayAppendValue(patterns, pattern5);
+    CFArrayAppendValue(patterns, pattern6);
+
+    if (SCDynamicStoreSetNotificationKeys(s_store, keys, patterns)){
+        s_rls = SCDynamicStoreCreateRunLoopSource(NULL, s_store, 0);
+        if (s_rls) {
+            CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
+        }else{
+            log_it(L_ERROR, "SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError()));
+            CFRelease(s_store);
+        }
+    }else {
+        log_it(L_ERROR, "SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError()));
+        CFRelease(s_store);
+    }
+    goto exit;
+
+    error:
+    if (s_store)    CFRelease(s_store);
+
+    exit:
+    if (key1) CFRelease(key1);
+    if (key2) CFRelease(key2);
+    if (key3) CFRelease(key3);
+    if (pattern1) CFRelease(pattern1);
+    if (pattern2) CFRelease(pattern2);
+    if (pattern3) CFRelease(pattern3);
+    if (pattern4) CFRelease(pattern4);
+    if (pattern5) CFRelease(pattern5);
+    if (pattern6) CFRelease(pattern6);
+    if (keys) CFRelease(keys);
+    if (patterns) CFRelease(patterns);
+    return;
+}
+
+
+/**
+ * @brief dap_network_monitor_init
+ * @param callback
+ * @details starts network monitorting
+ * @return 0 if successful
+ */
+int dap_network_monitor_init(dap_network_monitor_notification_callback_t cbMonitorNatification)
+{
+    memset((void*)&_net_notification, 0, sizeof(_net_notification));
+    _net_notification.callback = cbMonitorNatification;
+
+    pthread_barrier_t barrier;
+
+    pthread_barrier_init(&barrier, NULL, 2);
+    if(pthread_create(&_net_notification.thread, NULL, network_monitor_worker, &barrier) != 0) {
+        log_it(L_ERROR, "Error create notification thread");
+        return -3;
+    }
+
+    pthread_barrier_wait(&barrier);
+
+    pthread_barrier_destroy(&barrier);
+
+    log_it(L_INFO, "dap_network_monitor was initialized");
+    return 0;
+}
+
+/**
+ * @brief dap_network_monitor_deinit
+ */
+void dap_network_monitor_deinit(void)
+{
+    CFRunLoopStop(_net_notification.rlref);
+    //log_it(L_INFO, "After stopping run loop cycle");
+    pthread_cancel(_net_notification.thread);
+    //log_it(L_INFO, "After cancelation monitor thread!");
+    pthread_join(_net_notification.thread, NULL);
+    //log_it(L_INFO, "After deinit that wonderfull monitor!");
+}
+
+
+
+static void* network_monitor_worker(void *arg)
+{
+    pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
+    watch_for_network_changes();
+    pthread_barrier_wait(barrier);
+    _net_notification.rlref = CFRunLoopGetCurrent();
+    CFRunLoopRun();
+    log_it(L_WARNING, "We are in the loop activity and won't have to see this message!");
+    return NULL;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////
+// Usefull functions for future processing changes to interfaces
+
+static OSStatus MoreSCErrorBoolean(Boolean success)
+{
+    OSStatus err;
+    int scErr;
+
+    err = noErr;
+    if ( ! success ) {
+        scErr = SCError();
+        if (scErr == kSCStatusOK) {
+            scErr = kSCStatusFailed;
+        }
+        // Return an SCF error directly as an OSStatus.
+        // That's a little cheesy.  In a real program
+        // you might want to do some mapping from SCF
+        // errors to a range within the OSStatus range.
+        err = scErr;
+    }
+    return err;
+}
+
+static OSStatus MoreSCError(const void *value)
+{
+    return MoreSCErrorBoolean(value != NULL);
+}
+
+static OSStatus CFQError(CFTypeRef cf)
+    // Maps Core Foundation error indications (such as they
+    // are) to the OSStatus domain.
+{
+    OSStatus err;
+
+    err = noErr;
+    if (cf == NULL) {
+        err = -4960;
+    }
+    return err;
+}
+
+static void CFQRelease(CFTypeRef cf)
+    // A version of CFRelease that's tolerant of NULL.
+{
+    if (cf != NULL) {
+        CFRelease(cf);
+    }
+}
+
+
+static void GetIPAddressListFromValue(const void *key,
+                                      const void *value,
+                                      void *context)
+    // This function is a callback CopyIPAddressListSCF when
+    // it calls CFDictionaryApplyFunction.  It extracts the
+    // IPv4 address list from the network service dictionary
+    // and appends it to the result dictionary (which is passed
+    // in via the context pointers).
+{
+    CFArrayRef intfAddrList;
+
+    assert( key != NULL );
+    assert( CFGetTypeID(key) == CFStringGetTypeID() );
+    assert( value != NULL );
+    assert( CFGetTypeID(value) == CFDictionaryGetTypeID() );
+    assert( context != NULL );
+    assert( CFGetTypeID(context) == CFArrayGetTypeID() );
+
+    //CFDictionaryRef _value = (CFDictionaryRef) value;
+    struct __CFDictionary * _value = (__bridge struct __CFDictionary *) value;
+    intfAddrList = (__bridge struct __CFArray *) CFDictionaryGetValue(_value,
+                            kSCPropNetIPv4Addresses);
+    if (intfAddrList != NULL) {
+        assert( CFGetTypeID(intfAddrList)
+                == CFArrayGetTypeID() );
+        struct __CFArray * _context = (__bridge struct __CFArray *) context;
+        CFArrayAppendArray(_context,
+                            intfAddrList,
+                            CFRangeMake(0, CFArrayGetCount(intfAddrList))
+                            );
+    }
+
+}
+
+static OSStatus CopyIPAddressListSCF(CFArrayRef *addrList)
+    // Returns a CFArray that contains every IPv4
+    // address on the system (as CFStrings) in no
+    // particular order.
+{
+    OSStatus            err;
+    SCDynamicStoreRef   ref;
+    CFStringRef         pattern;
+    CFArrayRef          patternList;
+    CFDictionaryRef     valueDict;
+    CFMutableArrayRef   result;
+
+    assert( addrList != NULL);
+    assert(*addrList == NULL);
+
+    ref         = NULL;
+    pattern     = NULL;
+    patternList = NULL;
+    valueDict   = NULL;
+    result      = NULL;
+
+    // Create a connection to the dynamic store, then create
+    // a search pattern that finds all IPv4 entities.
+    // The pattern is "State:/Network/Service/[^/]+/IPv4".
+    ref = SCDynamicStoreCreate( NULL,
+                                CFSTR("CopyIPAddressListSCF"),
+                                NULL,
+                                NULL);
+    err = MoreSCError(ref);
+    if (err == noErr) {
+        pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(
+                                NULL,
+                                kSCDynamicStoreDomainState,
+                                kSCCompAnyRegex,
+                                kSCEntNetIPv4);
+        err = MoreSCError(pattern);
+    }
+
+    // Now make a pattern list out of the pattern and then
+    // call SCDynamicStoreCopyMultiple.  We use that call,
+    // rather than repeated calls to SCDynamicStoreCopyValue,
+    // because it gives us a snapshot of the state.
+    if (err == noErr) {
+        patternList = CFArrayCreate(NULL,
+                                    (const void **) &pattern,
+                                    1,
+                                    &kCFTypeArrayCallBacks);
+        err = CFQError(patternList);
+    }
+    if (err == noErr) {
+        valueDict = SCDynamicStoreCopyMultiple(ref,
+                                               NULL,
+                                               patternList);
+        err = MoreSCError(valueDict);
+    }
+
+    // For each IPv4 entity that we found, extract the list
+    // of IP addresses and append it to our results array.
+    if (err == noErr) {
+        result = CFArrayCreateMutable(NULL, 0,
+                                      &kCFTypeArrayCallBacks);
+        err = CFQError(result);
+    }
+
+    // Iterate over the values, extracting the IP address
+    // arrays and appending them to the result.
+    if (err == noErr) {
+        CFDictionaryApplyFunction(valueDict,
+                                  GetIPAddressListFromValue,
+                                  result);
+    }
+    // Clean up.
+
+    CFQRelease(ref);
+    CFQRelease(pattern);
+    CFQRelease(patternList);
+    if (err != noErr && result != NULL) {
+        CFQRelease(result);
+        result = NULL;
+        printf("10\n");
+    }
+    *addrList = result;
+
+    assert( (err == noErr) == (*addrList != NULL) );
+
+    return err;
+}
diff --git a/src/darwin/macos/dap_network_monitor.h b/src/darwin/macos/dap_network_monitor.h
new file mode 100755
index 0000000..6d570f4
--- /dev/null
+++ b/src/darwin/macos/dap_network_monitor.h
@@ -0,0 +1,53 @@
+/*
+ * Authors:
+ * Anton Isaikin <anton.isaikin@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma once
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <CoreFoundation/CFArray.h>
+
+typedef void (*dap_network_monitor_notification_callback_t)
+              (SCDynamicStoreRef store, CFArrayRef changedKeys, void *info);
+/**
+ * @brief dap_network_monitor_init
+ * @param callback
+ * @details starts network monitorting
+ * @return 0 if successful
+ */
+int dap_network_monitor_init(dap_network_monitor_notification_callback_t callback);
+
+/**
+ * @brief dap_network_monitor_deinit
+ */
+void dap_network_monitor_deinit(void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/darwin/macos/macos.pri b/src/darwin/macos/macos.pri
new file mode 100755
index 0000000..1fb87f8
--- /dev/null
+++ b/src/darwin/macos/macos.pri
@@ -0,0 +1,10 @@
+HEADERS += $$PWD/dap_network_monitor.h \
+HEADERS += $$PWD/pthread_barrier.h
+
+SOURCES += $$PWD/dap_network_monitor.c
+
+INCLUDEPATH += $$PWD
+
+LIBS += -framework CoreFoundation
+LIBS += -framework SystemConfiguration
+#LIBS += -framework NetworkExtension
diff --git a/src/darwin/macos/pthread_barrier.h b/src/darwin/macos/pthread_barrier.h
new file mode 100755
index 0000000..e1f5a8d
--- /dev/null
+++ b/src/darwin/macos/pthread_barrier.h
@@ -0,0 +1,65 @@
+#ifndef PTHREAD_BARRIER_H
+#define PTHREAD_BARRIER_H
+
+#include <pthread.h>
+#include <errno.h>
+
+typedef int pthread_barrierattr_t;
+typedef struct
+{
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    int count;
+    int tripCount;
+} pthread_barrier_t;
+
+
+int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
+{
+    if(count == 0)
+    {
+        errno = EINVAL;
+        return -1;
+    }
+    if(pthread_mutex_init(&barrier->mutex, 0) < 0)
+    {
+        return -2;
+    }
+    if(pthread_cond_init(&barrier->cond, 0) < 0)
+    {
+        pthread_mutex_destroy(&barrier->mutex);
+        return -3;
+    }
+    barrier->tripCount = count;
+    barrier->count = 0;
+
+    return 0;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+    pthread_cond_destroy(&barrier->cond);
+    pthread_mutex_destroy(&barrier->mutex);
+    return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+    pthread_mutex_lock(&barrier->mutex);
+    ++(barrier->count);
+    if(barrier->count >= barrier->tripCount)
+    {
+        barrier->count = 0;
+        pthread_cond_broadcast(&barrier->cond);
+        pthread_mutex_unlock(&barrier->mutex);
+        return 1;
+    }
+    else
+    {
+        pthread_cond_wait(&barrier->cond, &(barrier->mutex));
+        pthread_mutex_unlock(&barrier->mutex);
+        return 0;
+    }
+}
+
+#endif // PTHREAD_BARRIER_H
diff --git a/src/unix/CMakeLists.txt b/src/unix/CMakeLists.txt
new file mode 100755
index 0000000..da6eb03
--- /dev/null
+++ b/src/unix/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.0)
+
+project (dap_core_unix)
+
+file(GLOB CORE_UNIX_SRCS *.c)
+file(GLOB CORE_UNIX_HEADERS *.h)
+
+if(LINUX)
+    file(GLOB CORE_LINUX_SRCS linux/*.c)
+    file(GLOB CORE_LINUX_HEADERS linux/*.h)
+endif()
+
+add_library(${PROJECT_NAME} STATIC ${CORE_UNIX_SRCS} ${CORE_UNIX_HEADERS}
+    ${CORE_LINUX_SRCS} ${CORE_LINUX_HEADERS})
+
+target_link_libraries(${PROJECT_NAME} dap_core pthread)
+
+target_include_directories(dap_core_unix INTERFACE .)
+
+if(LINUX)
+    target_include_directories(dap_core_unix INTERFACE ./linux)
+endif()
+
diff --git a/src/unix/dap_cpu_monitor.c b/src/unix/dap_cpu_monitor.c
new file mode 100755
index 0000000..7a41bc7
--- /dev/null
+++ b/src/unix/dap_cpu_monitor.c
@@ -0,0 +1,113 @@
+#include "dap_cpu_monitor.h"
+#include "dap_common.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#define LOG_TAG "dap_cpu_monitor"
+
+static FILE * _proc_stat = NULL;
+static dap_cpu_stats_t _cpu_stats = {0};
+static dap_cpu_t _cpu_old_stats[MAX_CPU_COUNT] = {0};
+static dap_cpu_t _cpu_summary_old = {0};
+
+typedef struct proc_stat_line
+{
+    /* http://man7.org/linux/man-pages/man5/proc.5.html */
+    char cpu[10];
+    size_t user;
+    size_t nice;
+    size_t system;
+    size_t idle;
+    size_t iowait;
+    size_t irq;
+    size_t softirq;
+    size_t steal;
+    size_t guest;
+    size_t guest_nice;
+    size_t total; // summary all parameters
+} proc_stat_line_t;
+
+int dap_cpu_monitor_init()
+{
+    _cpu_stats.cpu_cores_count = (unsigned) sysconf(_SC_NPROCESSORS_ONLN);
+
+    log_it(L_DEBUG, "Cpu core count: %d", _cpu_stats.cpu_cores_count);
+
+    dap_cpu_get_stats(); // init prev parameters
+
+    return 0;
+}
+
+void dap_cpu_monitor_deinit()
+{
+
+}
+
+static void _deserealize_proc_stat(char *line, proc_stat_line_t *stat)
+{
+    sscanf(line,"%s %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
+           stat->cpu, &stat->user, &stat->nice, &stat->system, &stat->idle,
+           &stat->iowait, &stat->irq, &stat->softirq, &stat->steal,
+           &stat->guest, &stat->guest_nice);
+    stat->total = stat->user + stat->system + stat->idle +
+            stat->iowait + stat->irq + stat->softirq +
+            stat->steal + stat->guest + stat->guest_nice;
+}
+
+static float _calculate_load(size_t idle_time, size_t prev_idle_time,
+                      size_t total_time, size_t prev_total_time)
+{
+    return (1 - (1.0*idle_time -prev_idle_time) /
+            (total_time - prev_total_time)) * 100.0;
+}
+
+dap_cpu_stats_t dap_cpu_get_stats()
+{
+    _proc_stat = fopen("/proc/stat", "r");
+
+    if(_proc_stat == NULL){
+        log_it(L_ERROR, "Сan't open /proc/stat file");
+        return (dap_cpu_stats_t){0};
+    }
+
+    char *line = NULL;
+    proc_stat_line_t stat = {0};
+
+    /** get summary cpu stat **/
+    size_t mem_size;
+    getline(&line, &mem_size, _proc_stat);
+    _deserealize_proc_stat(line, &stat);
+
+    _cpu_stats.cpu_summary.idle_time = stat.idle;
+    _cpu_stats.cpu_summary.total_time = stat.total;
+    /*********************************************/
+
+    for(unsigned i = 0; i < _cpu_stats.cpu_cores_count; i++) {
+        getline(&line, &mem_size, _proc_stat);
+        _deserealize_proc_stat(line, &stat);
+        _cpu_stats.cpus[i].idle_time = stat.idle;
+        _cpu_stats.cpus[i].total_time = stat.total;
+        _cpu_stats.cpus[i].ncpu = i;
+
+        _cpu_stats.cpus[i].load = _calculate_load(_cpu_stats.cpus[i].idle_time,
+                                                  _cpu_old_stats[i].idle_time,
+                                                  _cpu_stats.cpus[i].total_time,
+                                                  _cpu_old_stats[i].total_time);
+    }
+
+    _cpu_stats.cpu_summary.load = _calculate_load(_cpu_stats.cpu_summary.idle_time,
+                    _cpu_summary_old.idle_time,
+                    _cpu_stats.cpu_summary.total_time,
+                    _cpu_summary_old.total_time);
+
+    memcpy(&_cpu_summary_old, &_cpu_stats.cpu_summary, sizeof (dap_cpu_t));
+
+    memcpy(_cpu_old_stats, _cpu_stats.cpus,
+           sizeof (dap_cpu_t) * _cpu_stats.cpu_cores_count);
+
+    fclose(_proc_stat);
+
+    return _cpu_stats;
+}
diff --git a/src/unix/dap_cpu_monitor.h b/src/unix/dap_cpu_monitor.h
new file mode 100755
index 0000000..ed15e28
--- /dev/null
+++ b/src/unix/dap_cpu_monitor.h
@@ -0,0 +1,69 @@
+/*
+ * Authors:
+ * Anatolii Kurotych <akurotych@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+// For C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_CPU_COUNT 64
+
+#include <stdlib.h>
+
+typedef struct dap_cpu {
+    unsigned ncpu; // number of cpu core
+    float load; // percent of load
+    size_t total_time;
+    size_t idle_time;
+} dap_cpu_t;
+
+typedef struct dap_cpu_stats
+{
+    unsigned cpu_cores_count;
+    dap_cpu_t cpu_summary; // average statistic for all cpu
+    dap_cpu_t cpus[MAX_CPU_COUNT]; // list of cpu with stat
+} dap_cpu_stats_t;
+
+/**
+ * @brief dap_cpu_monitor_init Monitor CPU initialization
+ * @return
+ */
+int dap_cpu_monitor_init(void);
+
+/**
+ * @brief dap_cpu_monitor_deinit Monitor CPU deinitialization
+ */
+void dap_cpu_monitor_deinit(void);
+
+/**
+ * @brief dap_cpu_get_stats Getting processor information
+ * @return
+ */
+dap_cpu_stats_t dap_cpu_get_stats(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/unix/dap_process_manager.c b/src/unix/dap_process_manager.c
new file mode 100755
index 0000000..e2d8259
--- /dev/null
+++ b/src/unix/dap_process_manager.c
@@ -0,0 +1,79 @@
+#ifdef __linux__
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "dap_process_manager.h"
+#include "dap_common.h"
+
+#undef LOG_TAG
+#define LOG_TAG "dap_process_manager"
+
+/**
+ * @brief is_process_running Check whether the process is running
+ * @param[in] pid PID
+ * @return
+ */
+bool is_process_running(pid_t pid) {
+    return kill(pid, 0) == 0;
+}
+
+/**
+ * @brief save_process_pid_in_file Saves process pid into file by file_path
+ * @param[in] file_path File path
+ * @return Execution result
+ *
+ * Saves process pid into file by file_path.
+ * If file exists he will be overwritten
+ */
+bool save_process_pid_in_file(const char* file_path) {
+    FILE * fpid = fopen(file_path, "w");
+    if (fpid == NULL) {
+        log_it(L_ERROR, "Cant create/open file by path %s",file_path);
+        return false;
+    }
+    fprintf(fpid, "%d", getpid());
+    fclose(fpid);
+    return true;
+}
+
+/**
+ * @brief get_pid_from_file File must consist only PID. Return 0 if file is clear.
+ * @param[in] file_path File path
+ * @return Execution result
+ */
+pid_t get_pid_from_file(const char* file_path) {
+    FILE * fpid = fopen(file_path, "r");
+    if (fpid == NULL) {
+        log_it(L_ERROR, "Cant create/open file by path %s",file_path);
+        return false;
+    }
+
+    pid_t f_pid = 0;
+    fscanf(fpid, "%d", &f_pid);
+    fclose(fpid);
+
+    return f_pid;
+}
+
+/**
+ * @brief daemonize_process Demonizes current process and exit from program
+ * @return
+ */
+bool daemonize_process() {
+    return daemon(1,1) == 0;
+}
+
+/**
+ * @brief kill_process Sends SIGKILL to process
+ * @param[in] pid
+ * @return
+ */
+bool kill_process(pid_t pid) {
+    if (!is_process_running(pid)) {
+        return false;
+    }
+    return kill(pid, SIGKILL) == 0;
+}
+
+#endif
diff --git a/src/unix/dap_process_manager.h b/src/unix/dap_process_manager.h
new file mode 100755
index 0000000..374c12a
--- /dev/null
+++ b/src/unix/dap_process_manager.h
@@ -0,0 +1,56 @@
+/*
+ * Authors:
+ * Anatolii Kurotych <akurotych@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __linux__
+
+#include <stdbool.h>
+#include <unistd.h>
+
+/* Saves process pid into file by file_path.
+ * If file exists he will be overwritten */
+extern bool save_process_pid_in_file(const char* file_path);
+
+/* File must consist only PID. Return 0 if file is clear. */
+extern pid_t get_pid_from_file(const char* file_path);
+
+/* Return true if process running */
+extern bool is_process_running(pid_t pid);
+
+/* Demonizes current process and exit from program */
+extern bool daemonize_process(void);
+
+/* Sends SIGKILL to process */
+extern bool kill_process(pid_t pid);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/unix/dap_process_memory.c b/src/unix/dap_process_memory.c
new file mode 100755
index 0000000..dc14f0b
--- /dev/null
+++ b/src/unix/dap_process_memory.c
@@ -0,0 +1,64 @@
+#include "dap_process_memory.h"
+#include "dap_common.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define LOG_TAG "dap_process_mem"
+
+#define MAX_LINE_LENGTH 128
+
+static size_t _parse_size_line(char *line) {
+    // This assumes that a digit will be found and the line ends in " Kb".
+    size_t i = strlen(line);
+    const char *p = line;
+    while (*p < '0' || *p > '9') p++;
+    line[i - 3] = '\0';
+    i = (size_t)atol(p);
+    return i;
+}
+
+static dap_process_memory_t _get_process_memory(const char* proc_file_path)
+{
+    FILE *file = fopen(proc_file_path, "r");
+
+    if(file == NULL) {
+        log_it(L_WARNING, "Cant open proc file");
+        return (dap_process_memory_t){0,0};
+    }
+
+    char line[MAX_LINE_LENGTH];
+    dap_process_memory_t proc_mem = {0};
+
+    while (fgets(line, MAX_LINE_LENGTH, file) != NULL) {
+        if (strncmp(line, "VmSize:", 7) == 0) {
+            proc_mem.vsz = _parse_size_line(line);
+        }
+
+        if (strncmp(line, "VmRSS:", 6) == 0) {
+            proc_mem.rss = _parse_size_line(line);
+        }
+
+        if (proc_mem.rss != 0 && proc_mem.vsz != 0)
+            break;
+    }
+
+    fclose(file);
+
+    if(proc_mem.vsz == 0 || proc_mem.rss == 0)
+        log_it(L_WARNING, "Getting memory statistics failed");
+
+    return proc_mem;
+}
+
+dap_process_memory_t get_proc_mem_current(void)
+{
+    return _get_process_memory("/proc/self/status");
+}
+
+dap_process_memory_t get_proc_mem_by_pid(pid_t pid)
+{
+    char buf[126] = {0};
+    sprintf(buf, "/proc/%d/status", pid);
+    return _get_process_memory(buf);
+}
diff --git a/src/unix/dap_process_memory.h b/src/unix/dap_process_memory.h
new file mode 100755
index 0000000..debeb98
--- /dev/null
+++ b/src/unix/dap_process_memory.h
@@ -0,0 +1,56 @@
+/*
+ * Authors:
+ * Anatolii Kurotych <akurotych@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+
+
+typedef struct dap_process_memory {
+    size_t vsz; // virtual memory (kb)
+    size_t rss; // physical memory (kb)
+} dap_process_memory_t;
+
+
+/**
+ * @brief get_proc_mem_current Get information about the amount of RAM consumed for the current process
+ * @return
+ */
+dap_process_memory_t get_proc_mem_current(void);
+
+/**
+ * @brief get_proc_mem_by_pid Obtain information about the amount of RAM consumed for a particular process
+ * @param[in] pid PID
+ * @return
+ */
+dap_process_memory_t get_proc_mem_by_pid(pid_t pid);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/unix/linux/dap_network_monitor.c b/src/unix/linux/dap_network_monitor.c
new file mode 100755
index 0000000..4ca6d78
--- /dev/null
+++ b/src/unix/linux/dap_network_monitor.c
@@ -0,0 +1,245 @@
+#include <linux/netlink.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+
+#include "dap_network_monitor.h"
+#include "dap_common.h"
+
+#define LOG_TAG "dap_network_monitor"
+
+static bool _send_NLM_F_ACK_msg(int fd)
+{
+    static int sequence_number = 0;
+
+    struct nlmsghdr *nh = DAP_NEW_Z(struct nlmsghdr);
+    struct sockaddr_nl sa;
+    struct iovec iov = { &nh, nh->nlmsg_len };
+    struct msghdr msg = {&sa, sizeof(sa), &iov, 1, NULL, 0, 0};
+
+    memset(&sa, 0, sizeof(sa));
+    sa.nl_family = AF_NETLINK;
+    nh->nlmsg_pid = getpid();
+    nh->nlmsg_seq = ++sequence_number;
+    nh->nlmsg_flags |= NLM_F_ACK;
+
+    ssize_t rc = sendmsg(fd, &msg, 0);
+    if (rc == -1) {
+          log_it(L_ERROR, "sendmsg failed");
+          return false;
+    }
+
+    DAP_DELETE(nh);
+    return true;
+}
+
+static struct {
+    int socket;
+    pthread_t thread;
+    dap_network_monitor_notification_callback_t callback;
+} _net_notification;
+
+static void* network_monitor_worker(void *arg);
+
+int dap_network_monitor_init(dap_network_monitor_notification_callback_t cb)
+{
+    memset((void*)&_net_notification, 0, sizeof(_net_notification));
+
+    if ((_net_notification.socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
+        log_it(L_ERROR, "Can't open notification socket");
+        return -1;
+    }
+
+    struct sockaddr_nl addr = {0};
+    addr.nl_family = AF_NETLINK;
+    addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
+    if (bind(_net_notification.socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+        log_it(L_ERROR, "Can't bind notification socket");
+        return -2;
+    }
+
+    pthread_barrier_t barrier;
+
+    pthread_barrier_init(&barrier, NULL, 2);
+    if(pthread_create(&_net_notification.thread, NULL, network_monitor_worker, &barrier) != 0) {
+        log_it(L_ERROR, "Error create notification thread");
+        return -3;
+    }
+
+    pthread_barrier_wait(&barrier);
+
+    pthread_barrier_destroy(&barrier);
+
+    _net_notification.callback = cb;
+    log_it(L_DEBUG, "dap_network_monitor was initialized");
+    return 0;
+}
+
+void dap_network_monitor_deinit(void)
+{
+    if(_net_notification.socket == 0 || _net_notification.socket == -1) {
+        log_it(L_ERROR, "Network monitor not be inited");
+        return;
+    }
+    close(_net_notification.socket);
+    pthread_cancel(_net_notification.thread);
+    pthread_join(_net_notification.thread, NULL);
+}
+
+static void _ip_addr_msg_handler(struct nlmsghdr *nlh,
+                                 dap_network_notification_t* result)
+{
+    struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
+    struct rtattr *rth = IFA_RTA(ifa);
+    size_t rtl = IFA_PAYLOAD(nlh);
+    for (; rtl && RTA_OK(rth, rtl); rth = RTA_NEXT(rth,rtl)) {
+        char *inet_str = inet_ntoa(*((struct in_addr *)RTA_DATA(rth)));
+
+        if (rth->rta_type != IFA_LOCAL) continue;
+
+        /* fill result */
+        result->addr.ip = htonl(*((uint32_t *)RTA_DATA(rth)));
+        strcpy(result->addr.s_ip, inet_str);
+        if_indextoname(ifa->ifa_index, result->addr.interface_name);
+    }
+}
+
+static void _route_msg_handler(struct nlmsghdr *nlh,
+                               dap_network_notification_t* result,
+                               int received_bytes)
+{
+    struct  rtmsg *route_entry;  /* This struct represent a route entry
+                                        in the routing table */
+    struct  rtattr *route_attribute; /* This struct contain route
+                                                attributes (route type) */
+    int     route_attribute_len = 0;
+
+    route_attribute_len = RTM_PAYLOAD(nlh);
+
+    for ( ; NLMSG_OK(nlh, received_bytes); \
+                       nlh = NLMSG_NEXT(nlh, received_bytes))
+       {
+           /* Get the route data */
+           route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
+
+           result->route.netmask = route_entry->rtm_dst_len;
+           result->route.protocol = route_entry->rtm_protocol;
+
+           /* Get attributes of route_entry */
+           route_attribute = (struct rtattr *) RTM_RTA(route_entry);
+
+           /* Get the route atttibutes len */
+           route_attribute_len = RTM_PAYLOAD(nlh);
+           /* Loop through all attributes */
+           for ( ; RTA_OK(route_attribute, route_attribute_len); \
+               route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
+           {
+               /* Get the destination address */
+               if (route_attribute->rta_type == RTA_DST)
+               {
+                   result->route.destination_address = htonl(*(uint32_t*)RTA_DATA(route_attribute));
+
+                   inet_ntop(AF_INET, RTA_DATA(route_attribute),
+                                                result->route.s_destination_address,
+                                                sizeof(result->route.s_destination_address));
+               }
+               /* Get the gateway (Next hop) */
+               if (route_attribute->rta_type == RTA_GATEWAY)
+               {
+                   result->route.gateway_address = htonl(*(uint32_t*)RTA_DATA(route_attribute));
+;
+                   inet_ntop(AF_INET, RTA_DATA(route_attribute),
+                                                result->route.s_gateway_address,
+                                                sizeof(result->route.s_gateway_address));
+               }
+           }
+   }
+
+}
+
+static void _link_msg_handler(struct nlmsghdr *nlh,
+                               dap_network_notification_t* result,
+                              struct sockaddr_nl sa)
+{
+    (void) sa;
+    struct ifaddrmsg *ifa=NLMSG_DATA(nlh);
+    struct ifinfomsg *ifi=NLMSG_DATA(nlh);;
+
+    switch (nlh->nlmsg_type){
+        case RTM_NEWLINK:
+            if_indextoname(ifa->ifa_index,result->link.interface_name);
+            result->link.is_up = ifi->ifi_flags & IFF_UP ? true : false;
+            result->link.is_running = ifi->ifi_flags & IFF_RUNNING ? true : false;
+            //printf("netlink_link_state: Link %s is %s and %s\n",
+            //    result->link.interface_name, (ifi->ifi_flags & IFF_UP)?"Up":"Down", (ifi->ifi_flags & IFF_RUNNING)?"Running":"Not Running");
+            break;
+        case RTM_DELLINK:
+            if_indextoname(ifa->ifa_index,result->link.interface_name);
+            //printf("msg_handler: RTM_DELLINK : %s\n",result->link.interface_name);
+            break;
+    }
+}
+
+static void clear_results(dap_network_notification_t* cb_result)
+{
+    bzero(cb_result, sizeof (dap_network_notification_t));
+    cb_result->route.destination_address = (uint64_t) -1;
+    cb_result->route.gateway_address = (uint64_t) -1;
+}
+
+static void* network_monitor_worker(void *arg)
+{
+    pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
+    log_it(L_DEBUG, "Network monitor worker started");
+    if (_net_notification.socket == -1) {
+        log_it(L_ERROR, "Net socket not running. Can't start worker");
+        return  NULL;
+    }
+
+    dap_network_notification_t callback_result;
+    int len;
+    char buf[4096];
+    struct iovec iov = { buf, sizeof(buf) };
+    struct sockaddr_nl sa;
+    struct nlmsghdr *nlh;
+    struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
+
+    pthread_barrier_wait(barrier);
+    while ((len = recvmsg(_net_notification.socket, &msg, 0)) > 0){
+        _send_NLM_F_ACK_msg(_net_notification.socket);
+
+        for (nlh = (struct nlmsghdr *) buf; (NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len)) {
+            if (nlh->nlmsg_type == NLMSG_ERROR){
+                /* Do some error handling. */
+                log_it(L_DEBUG, "There an error! nlmsg_type %d", nlh->nlmsg_type);
+                break;
+            }
+
+            clear_results(&callback_result);
+
+            callback_result.type = nlh->nlmsg_type;
+            if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR) {
+                _ip_addr_msg_handler(nlh, &callback_result);
+            } else if(nlh->nlmsg_type == RTM_NEWROUTE || nlh->nlmsg_type == RTM_DELROUTE) {
+                _route_msg_handler(nlh, &callback_result, len);
+            } else if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK){
+                _link_msg_handler(nlh, &callback_result, sa);
+            }
+            else{
+                log_it(L_DEBUG, "Not supported msg type %d", nlh->nlmsg_type);
+                continue;
+            }
+
+            if (_net_notification.callback) {
+                _net_notification.callback(callback_result);
+            } else {
+                log_it(L_ERROR, "callback is NULL");
+            }
+        }
+    }
+    return NULL;
+}
diff --git a/src/unix/linux/dap_network_monitor.h b/src/unix/linux/dap_network_monitor.h
new file mode 100755
index 0000000..1f40931
--- /dev/null
+++ b/src/unix/linux/dap_network_monitor.h
@@ -0,0 +1,91 @@
+/*
+ * Authors:
+ * Anatolii Kurotych <akurotych@gmail.com>
+ * DeM Labs Inc.   https://demlabs.net
+ * DeM Labs Open source community https://github.com/demlabsinc
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    DAP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <net/if.h>
+#include <linux/rtnetlink.h>
+
+#define MAX_IP_STR_LEN 15
+#define DAP_ADRESS_UNDEFINED (uint64_t)-1
+
+typedef enum {
+    // like in rtnetlink defines
+    IP_ADDR_ADD = RTM_NEWADDR,
+    IP_ADDR_REMOVE,
+    IP_ROUTE_ADD = RTM_NEWROUTE,
+    IP_ROUTE_REMOVE,
+    IP_LINK_NEW = RTM_NEWLINK,
+    IP_LINK_DEL
+} dap_network_monitor_notification_type_t;
+
+typedef struct {
+    dap_network_monitor_notification_type_t type;
+    union {
+        struct {
+            char interface_name[IF_NAMESIZE];
+            char s_ip[MAX_IP_STR_LEN + 1];
+            uint32_t ip; // inet_ntoa(*((struct in_addr *)&ipaddr)) for cast to char*
+        } addr; // for IP_ADDR_ADD, IP_ADDR_REMOVE
+        struct {
+            uint64_t destination_address; // 64 bit for checking -1 like not filled variable
+            char s_destination_address[MAX_IP_STR_LEN + 1];
+            uint64_t gateway_address;
+            char s_gateway_address[MAX_IP_STR_LEN + 1];
+            uint8_t protocol;
+            uint8_t netmask;
+        } route; // for IP_ROUTE_ADD, IP_ROUTE_REMOVE
+        struct {
+            char interface_name[IF_NAMESIZE];
+            bool is_up;
+            bool is_running;
+        } link; // for RTM_NEWLINK, RTM_DELLINK
+    };
+} dap_network_notification_t;
+
+typedef void (*dap_network_monitor_notification_callback_t)
+              (const dap_network_notification_t notification);
+
+/**
+ * @brief dap_network_monitor_init
+ * @param callback
+ * @details starts network monitorting
+ * @return 0 if successful
+ */
+int dap_network_monitor_init(dap_network_monitor_notification_callback_t callback);
+
+/**
+ * @brief dap_network_monitor_deinit
+ */
+void dap_network_monitor_deinit(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/unix/linux/linux.pri b/src/unix/linux/linux.pri
new file mode 100755
index 0000000..05fc40b
--- /dev/null
+++ b/src/unix/linux/linux.pri
@@ -0,0 +1,5 @@
+HEADERS += $$PWD/dap_network_monitor.h \
+
+SOURCES += $$PWD/dap_network_monitor.c \
+
+INCLUDEPATH += $$PWD
diff --git a/src/unix/unix.pri b/src/unix/unix.pri
new file mode 100755
index 0000000..299e0ae
--- /dev/null
+++ b/src/unix/unix.pri
@@ -0,0 +1,17 @@
+linux-* {
+    include(linux/linux.pri)
+}
+
+!android {
+
+HEADERS += $$PWD/dap_cpu_monitor.h \
+           $$PWD/dap_process_manager.h \
+           $$PWD/dap_process_memory.h \
+
+SOURCES += $$PWD/dap_cpu_monitor.c \
+           $$PWD/dap_process_manager.c \
+           $$PWD/dap_process_memory.c \
+
+INCLUDEPATH += $$PWD
+
+}
diff --git a/test/dap_circular_test.c b/test/dap_circular_test.c
new file mode 100755
index 0000000..2e3f108
--- /dev/null
+++ b/test/dap_circular_test.c
@@ -0,0 +1,214 @@
+#include "dap_circular_test.h"
+#include "dap_circular_buffer.h"
+
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+
+static char *chars_string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+#define MAX_RESULT_BUF_LEN 8096
+void dap_circular_test_simple()
+{
+    const int buf_size = 8;
+    circular_buffer_t cb = circular_buffer_create(buf_size);
+
+    circular_buffer_push(cb, chars_string, buf_size);
+
+    int fd[2];
+    socketpair(PF_LOCAL, SOCK_STREAM, 0, fd);
+
+    int ret = circular_buffer_write_In_socket(cb, fd[0]);
+    dap_assert(ret == buf_size, "Check ret write in socket");
+
+    ret = circular_buffer_write_In_socket(cb, fd[0]);
+    dap_assert(ret == 0, "Check ret write in socket");
+    char result_buff[MAX_RESULT_BUF_LEN] = {0};
+    ssize_t res = read(fd[1], result_buff, 44);
+
+    dap_assert(res == buf_size, "Check buf size");
+
+    dap_assert(dap_strn_equals(result_buff, chars_string, buf_size),
+               "Check result buf");
+    dap_assert(circular_buffer_get_data_size(cb) == 0, "Check data size");
+
+    close(fd[0]);
+    close(fd[1]);
+    circular_buffer_free(cb);
+    dap_pass_msg("Test simple");
+}
+
+void dap_circular_test_double_write()
+{
+    const int buf_size = 8;
+    const char* expected_string = "0123456701";
+    int expected_string_len = strlen(expected_string);
+    circular_buffer_t cb = circular_buffer_create(buf_size);
+
+    circular_buffer_push(cb, chars_string, buf_size);
+
+    int fd[2];
+    socketpair(PF_LOCAL, SOCK_STREAM, 0, fd);
+
+    int ret = circular_buffer_write_In_socket(cb, fd[0]);
+
+    circular_buffer_push(cb, chars_string, 2);
+
+    ret = circular_buffer_write_In_socket(cb, fd[0]);
+    dap_assert(ret == 2, "Check ret write in socket");
+
+    char result_buff[MAX_RESULT_BUF_LEN] = {0};
+    ssize_t res = read(fd[1], result_buff, 44);
+
+    dap_assert(res == expected_string_len, "Check buf size");
+
+    dap_assert(dap_str_equals(result_buff, expected_string),
+               "Check result buf");
+    dap_assert(circular_buffer_get_data_size(cb) == 0, "Check data size");
+    dap_pass_msg("Double write");
+
+    circular_buffer_free(cb);
+    close(fd[0]);
+    close(fd[1]);
+}
+
+void dap_circular_test_defrag_write()
+{
+    const int buf_size = 8;
+    const char* expected_string = "56701201";
+    int expected_string_len = strlen(expected_string);
+    circular_buffer_t cb = circular_buffer_create(buf_size);
+
+    circular_buffer_push(cb, chars_string, buf_size);
+    circular_buffer_pop(cb, 5, NULL);
+    circular_buffer_push(cb, chars_string, 3);
+    // expected string here 567012
+
+
+    int fd[2];
+    socketpair(PF_LOCAL, SOCK_STREAM, 0, fd);
+
+    // write 567012
+    int ret = circular_buffer_write_In_socket(cb, fd[0]);
+    dap_assert(ret == 6, "Check ret write in socket");
+
+    // push 01
+    circular_buffer_push(cb, chars_string, 2);
+
+    // write 01
+    ret = circular_buffer_write_In_socket(cb, fd[0]);
+    dap_assert(ret == 2, "Check ret write in socket");
+
+    char result_buff[MAX_RESULT_BUF_LEN] = {0};
+    ssize_t res = read(fd[1], result_buff, MAX_RESULT_BUF_LEN);
+
+    dap_assert(res == expected_string_len, "Check buf size");
+
+    dap_assert(dap_str_equals(result_buff, expected_string),
+               "Check result buf");
+
+    dap_pass_msg("Double write");
+    dap_assert(circular_buffer_get_data_size(cb) == 0, "Check data size");
+    circular_buffer_free(cb);
+    close(fd[0]);
+    close(fd[1]);
+}
+
+void dap_circular_test_write_bad_socket()
+{
+    const int buf_size = 8;
+    circular_buffer_t cb = circular_buffer_create(buf_size);
+
+    circular_buffer_push(cb, chars_string, buf_size);
+
+    int fd[2];
+    socketpair(PF_LOCAL, SOCK_STREAM, 0, fd);
+    int fd2[2];
+    socketpair(PF_LOCAL, SOCK_STREAM, 0, fd2);
+
+    close(fd[0]);
+    int ret = circular_buffer_write_In_socket(cb, fd[0]);
+    dap_assert(ret == -1, "Check ret write in socket");
+
+    ret = circular_buffer_write_In_socket(cb, fd2[0]);
+    dap_assert(ret == 8, "Check ret write in socket");
+    char result_buff[MAX_RESULT_BUF_LEN] = {0};
+    ssize_t res = read(fd2[1], result_buff, MAX_RESULT_BUF_LEN);
+
+    dap_assert(res == buf_size, "Check buf size");
+
+    dap_assert(dap_strn_equals(result_buff, chars_string, buf_size),
+               "Check result buf");
+
+    ret = circular_buffer_write_In_socket(cb, fd2[0]);
+    dap_assert(ret == 0, "Check zero write");
+    dap_assert(circular_buffer_get_data_size(cb) == 0, "Check data size");
+    close(fd[1]);
+    close(fd2[0]);
+    close(fd2[1]);
+    circular_buffer_free(cb);
+    dap_pass_msg("Test simple");
+}
+
+void dap_circular_load_test()
+{
+    srand(time(NULL));
+
+    int iterations = 230;
+
+    const char *digits = "123456789";
+
+    const int buf_size = strlen(digits);
+    circular_buffer_t cb = circular_buffer_create(buf_size);
+
+    int fd[2];
+    socketpair(PF_LOCAL, SOCK_STREAM, 0, fd);
+
+    int count_writed_bytes = 0;
+
+    // defrag buffer
+    circular_buffer_push(cb, (void*)digits, strlen(digits));
+    circular_buffer_pop(cb, strlen(digits) - 1, NULL);
+    circular_buffer_push(cb, (void*)digits, 3);
+    count_writed_bytes = 4;
+
+    char expectedBuffer[MAX_RESULT_BUF_LEN];
+    memset(expectedBuffer, 0, MAX_RESULT_BUF_LEN);
+    circular_buffer_read(cb, count_writed_bytes, expectedBuffer);
+
+    int count_write_bytes = 4;
+    do {
+        int r = circular_buffer_write_In_socket(cb, fd[0]);
+        dap_assert_PIF(r == count_write_bytes, "Check write bytes");
+        dap_assert_PIF(circular_buffer_get_data_size(cb) == 0, "buf size must be 0!");
+
+        count_write_bytes = rand() % strlen(digits);
+        circular_buffer_push(cb, (void*)digits, count_write_bytes);
+        strncat(expectedBuffer, digits, count_write_bytes);
+        count_writed_bytes += count_write_bytes;
+    } while (--iterations);
+    count_writed_bytes -= count_write_bytes; // last bytes will not be writed
+
+    char result_buff[MAX_RESULT_BUF_LEN] = {0};
+    ssize_t res = read(fd[1], result_buff, MAX_RESULT_BUF_LEN);
+    dap_assert(res == count_writed_bytes, "Check count writed and readed from socket bytes");
+
+    dap_assert(memcmp(expectedBuffer, result_buff, res) == 0, "Check expected and result buffer");
+
+    circular_buffer_free(cb);
+    close(fd[0]);
+    close(fd[1]);
+}
+void dap_circular_test_run()
+{
+    dap_print_module_name("dap_circular");
+
+    dap_circular_test_simple();
+    dap_circular_test_double_write();
+    dap_circular_test_defrag_write();
+#ifdef __linux__
+    dap_circular_test_write_bad_socket();
+    dap_circular_load_test();
+#endif
+}
diff --git a/test/dap_circular_test.h b/test/dap_circular_test.h
new file mode 100755
index 0000000..51a73ae
--- /dev/null
+++ b/test/dap_circular_test.h
@@ -0,0 +1,5 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_common.h"
+
+extern void dap_circular_test_run(void);
diff --git a/test/dap_common_test.c b/test/dap_common_test.c
new file mode 100755
index 0000000..5301d2f
--- /dev/null
+++ b/test/dap_common_test.c
@@ -0,0 +1,14 @@
+#include "dap_common_test.h"
+
+void test_put_int() {
+    const int INT_VAL = 10;
+    const char * EXPECTED_RESULT = "10";
+    char * result_arr = dap_itoa(INT_VAL);
+    dap_assert(strcmp(result_arr, EXPECTED_RESULT) == 0,
+               "Check string result from itoa");
+}
+
+void dap_common_test_run() {
+    dap_print_module_name("dap_common");
+    test_put_int();
+}
diff --git a/test/dap_common_test.h b/test/dap_common_test.h
new file mode 100755
index 0000000..3934603
--- /dev/null
+++ b/test/dap_common_test.h
@@ -0,0 +1,5 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_common.h"
+
+extern void dap_common_test_run(void);
diff --git a/test/dap_config_test.c b/test/dap_config_test.c
new file mode 100755
index 0000000..38744b0
--- /dev/null
+++ b/test/dap_config_test.c
@@ -0,0 +1,184 @@
+#include "dap_config_test.h"
+
+static const char * testconfigName = "test_dap_config.cfg";
+static const char * config_data = "[db_options]\n"
+                                  "db_type=mongoDb\n"
+                                  "[server_options]\n"
+                                  "timeout=1,0\n"
+                                  "vpn_enable=true\n"
+                                  "proxy_enable=false\n"
+                                  "TTL_session_key=600\n"
+                                  "str_arr=[vasya, petya, grisha, petushok@microsoft.com]\n"
+                                  "int_arr=[1, 3, 5]\n";
+
+static const size_t STR_ARR_LEN = 4;
+static const char * str_add_test_case[] = {
+    "vasya",
+    "petya",
+    "grisha",
+    "petushok@microsoft.com"
+};
+static const size_t INT_ARR_LEN = 3;
+static const int32_t int_arr_test_cases[] = {1, 3, 5};
+
+
+static FILE * config_file;
+static dap_config_t * config;
+
+void create_test_config_file() {
+    config_file = fopen(testconfigName, "w+");
+    dap_assert(config_file != NULL, "Create config file");
+
+    fwrite(config_data, sizeof(char),
+           strlen(config_data), config_file);
+
+    fclose(config_file);
+}
+
+void init_test_case() {
+    create_test_config_file();
+
+    // init dir path for configs files
+    dap_config_init(".");
+
+    config = dap_config_open("test_dap_config");
+}
+
+void cleanup_test_case() {
+    dap_assert(remove("test_dap_config.cfg") == 0,
+           "Remove config file");
+    dap_config_close(config);
+    dap_config_deinit();
+}
+
+void test_config_open_fail() {
+    dap_assert(dap_config_open("RandomNeverExistName") == NULL,
+           "Try open not exists config file");
+}
+
+void test_get_int() {
+    int32_t resultTTL = dap_config_get_item_int32(config,
+                                                  "server_options",
+                                                  "TTL_session_key");
+    dap_assert(resultTTL == 600, "Get int from config");
+
+}
+
+void test_get_int_default()
+{
+    int32_t resultTTLDefault = dap_config_get_item_int32_default(config,
+                                                                 "server_options",
+                                                                 "TTL_session_key",
+                                                                 650);
+    dap_assert(resultTTLDefault == 600, "The correct valid int value is obtained from the default function");
+
+    int32_t resultTTLDefault1 = dap_config_get_item_int32_default(config,
+                                                                 "server_options",
+                                                                 "TTL_session_key2",
+                                                                 650);
+    dap_assert(resultTTLDefault1 == 650, "The correct default value of int from the default function is obtained");
+}
+
+void test_get_double() {
+    double timeout = dap_config_get_item_double(config,
+                                                "server_options",
+                                                "timeout");
+    dap_assert(timeout == 1.0, "Get double from config");
+}
+
+void test_get_double_default()
+{
+    double timeoutDefault = dap_config_get_item_double_default(config,
+                                                                "server_options",
+                                                                "timeout",
+                                                                1.0);
+    dap_assert(timeoutDefault == 1.0, "The correct valid double value is obtained from the default function");
+
+    double timeoutDefault2 = dap_config_get_item_double_default(config,
+                                                                "server_options",
+                                                                "ghsdgfyhj",
+                                                                1.5);
+    dap_assert(timeoutDefault2 == 1.5, "The correct default value of double from the default function is obtained");
+}
+
+void test_get_bool() {
+    bool rBool = dap_config_get_item_bool(config, "server_options", "vpn_enable");
+    dap_assert(rBool == true, "Get bool from config");
+    rBool = dap_config_get_item_bool(config, "server_options", "proxy_enable");
+    dap_assert(rBool == false, "Get bool from config");
+}
+
+void test_get_bool_default()
+{
+    bool rBool = dap_config_get_item_bool_default(config, "server_options", "proxy_enable", true);
+    dap_assert(rBool == false, "received true true bool value from a function default");
+    rBool = dap_config_get_item_bool_default(config, "server_options", "proxy_enable2", false);
+    dap_assert(rBool == false, "the correct default value of bool is obtained from the default function");
+}
+
+void test_array_str()
+{
+    uint16_t arraySize;
+    char ** result_arr = dap_config_get_array_str(config, "server_options", "str_arr", &arraySize);
+
+    dap_assert(result_arr != NULL, "Get array str from config");
+    dap_assert(arraySize == STR_ARR_LEN, "Check array length");
+
+    for(uint32_t i = 0; i < arraySize; i++) {
+        assert(strcmp(result_arr[i], str_add_test_case[i]) == 0 && "test_array_str failed");
+    }
+}
+
+void test_array_int() {
+    uint16_t arraySize;
+    char ** result_arr = dap_config_get_array_str(config, "server_options", "int_arr", &arraySize);
+
+    dap_assert(result_arr != NULL, "Get array int");
+    dap_assert(arraySize == INT_ARR_LEN, "Check array int length");
+
+    dap_test_msg("Testing array int values.");
+    for(uint32_t i = 0; i < arraySize; i++) {
+        dap_assert_PIF(atoi(result_arr[i]) == int_arr_test_cases[i], "Check array int");
+    }
+}
+
+void test_get_item_str()
+{
+    const char* dct = dap_config_get_item_str(config, "db_options", "db_type");
+    const char* E1 = "mongoDb";
+    dap_assert(memcmp(dct,E1,7) == 0, "The function returns const char*");
+}
+
+void test_get_item_str_default()
+{
+    const char* E1 = "mongoDb";
+    const char* E2 = "EDb";
+
+    const char* dct2 = dap_config_get_item_str_default(config, "db_options", "db_type", "EDb");
+    dap_assert(memcmp(dct2,E1,7) == 0, "The function returns the true value of const char *");
+
+    const char* dct3 = dap_config_get_item_str_default(config, "db_options", "db_type2", "EDb");
+    dap_assert(memcmp(dct3,E2,3) == 0, "The function returns the default const char *");
+}
+
+
+void dap_config_tests_run()
+{
+    dap_print_module_name("dap_config");
+
+    init_test_case();
+
+    test_config_open_fail();
+    test_get_int();
+    test_get_int_default();
+    test_get_bool();
+    test_get_bool_default();
+    test_array_str();
+    test_array_int();
+    test_get_item_str();
+    test_get_item_str_default();
+    test_get_double();
+    test_get_double_default();
+
+    cleanup_test_case();
+}
diff --git a/test/dap_config_test.h b/test/dap_config_test.h
new file mode 100755
index 0000000..0fd0c13
--- /dev/null
+++ b/test/dap_config_test.h
@@ -0,0 +1,5 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_config.h"
+
+extern void dap_config_tests_run(void);
diff --git a/test/dap_strfuncs_test.c b/test/dap_strfuncs_test.c
new file mode 100755
index 0000000..010da4e
--- /dev/null
+++ b/test/dap_strfuncs_test.c
@@ -0,0 +1,145 @@
+#include "dap_common.h"
+#include "dap_strfuncs_test.h"
+#include "dap_list.h"
+#include "dap_string.h"
+
+void dap_str_dup_test(void)
+{
+    int l_a = rand(), l_b = rand();
+    const char *l_s = "test string";
+    char l_str0[1024];
+    sprintf(l_str0, "a=%d b=%d s=%s", l_a, l_b, l_s);
+    char *l_str1 = dap_strdup_printf("a=%d b=%d s=%s", l_a, l_b, l_s);
+    size_t str_size0 = strlen(l_str0);
+    size_t str_size1 = strlen(l_str1);
+
+    char *l_str2 = DAP_NEW_SIZE(char, str_size1 + 1);
+    dap_stpcpy(l_str2, l_str1);
+
+    size_t str_size2 = strlen(l_str2);
+    dap_assert_PIF(str_size0 == str_size1, "Strings sizes must be equal");
+    dap_assert_PIF(str_size1 == str_size2, "Strings sizes must be equal");
+
+    dap_assert(!strcmp(l_str0, l_str1), "Test dap_strdup_printf()");
+    dap_assert(!strcmp(l_str1, l_str2), "Test dap_stpcpy()");
+    DAP_DELETE(l_str1);
+    DAP_DELETE(l_str2);
+}
+
+void dap_str_modify_test(void)
+{
+    const char *l_s_in = "Test String";
+    const char *l_s_up_check = "TEST STRING";
+    const char *l_s_down_check = "test string";
+    char *l_s_out;
+
+    l_s_out = dap_strup(l_s_in, -1);
+    dap_assert(!strcmp(l_s_out, l_s_up_check), "Test dap_strup()");
+    DAP_DELETE(l_s_out);
+
+    l_s_out = dap_strdown(l_s_in, -1);
+    dap_assert(!strcmp(l_s_out, l_s_down_check), "Test dap_strdown()");
+    DAP_DELETE(l_s_out);
+
+    l_s_out = dap_strdup(l_s_in);
+    dap_strreverse(l_s_out);
+    dap_assert_PIF(strcmp(l_s_out, l_s_in), "String not modified");
+    dap_strreverse(l_s_out);
+    dap_assert(!strcmp(l_s_out, l_s_in), "Test dap_strreverse()");
+    DAP_DELETE(l_s_out);
+
+    l_s_out = dap_strdup_printf("      %s  ", l_s_in);
+    dap_strstrip(l_s_out);
+    dap_assert(!strcmp(l_s_out, l_s_in), "Test dap_strstrip()");
+    DAP_DELETE(l_s_out);
+}
+
+void dap_str_array_test(void)
+{
+    const char *l_s_in = "1:23:: Test:  :\n:String:";
+    char **l_s_array = dap_strsplit(l_s_in, ":", -1);
+
+    size_t l_count = 1;
+    char *l_s_tmp = dap_strstr_len(l_s_in, -1, ":");
+    while(l_s_tmp) {
+        l_s_tmp = dap_strstr_len(l_s_tmp + 1, -1, ":");
+        l_count++;
+    }
+
+    char **l_s_array_copy = dap_strdupv((const char**)l_s_array);
+
+    dap_assert_PIF(dap_str_countv(l_s_array) == l_count, "String split");
+    dap_assert_PIF(dap_str_countv(l_s_array_copy) == l_count, "String copy");
+    char *l_s_out = dap_strjoinv(":", l_s_array);
+    dap_assert(!strcmp(l_s_out, l_s_in), "Test string array functions");
+
+    dap_strfreev(l_s_array);
+    dap_strfreev(l_s_array_copy);
+    DAP_DELETE(l_s_out);
+}
+
+static void list_delete(void* a_pointer)
+{
+    DAP_DELETE(a_pointer);
+}
+
+void dap_list_test(void)
+{
+    dap_list_t *l_list = NULL;
+    l_list = dap_list_append(l_list, "item 1");
+    l_list = dap_list_append(l_list, "item 2");
+    l_list = dap_list_append(l_list, "item 3");
+    l_list = dap_list_prepend(l_list, "item 0");
+
+    dap_list_t *l_list_tmp = dap_list_find(l_list, "item 2");
+    unsigned int l_count = dap_list_length(l_list);
+    dap_list_remove(l_list, "item 3");
+    unsigned int l_count_after = dap_list_length(l_list);
+
+    dap_assert_PIF(l_count == 4, "Test dap_list_length()");
+    dap_assert_PIF(l_count_after == 3, "Test dap_list_remove()");
+    dap_assert_PIF(!strcmp(l_list_tmp->data, "item 2"), "Test dap_list_find()");
+    dap_list_free(l_list);
+
+    // for test dap_list_free_full()
+    l_list = NULL;
+    l_list = dap_list_append(l_list, dap_strdup("item 1"));
+    l_list = dap_list_append(l_list, dap_strdup("item 2"));
+
+    dap_assert(l_list, "Test dap_list_t");
+    dap_list_free_full(l_list, list_delete);
+}
+
+void dap_string_test(void)
+{
+    dap_string_t *l_str = dap_string_new(NULL);
+    dap_string_append(l_str, "str=string 1");
+    dap_assert_PIF(!strcmp(l_str->str, "str=string 1"), "Test dap_string_append()");
+
+    dap_string_append_printf(l_str, " int=%d", 123);
+    dap_assert_PIF(!strcmp(l_str->str, "str=string 1 int=123"), "Test dap_string_append()");
+
+    dap_string_erase(l_str, 3, 9);
+    dap_assert_PIF(!strcmp(l_str->str, "str int=123"), "Test dap_string_erase()");
+
+    dap_string_append_len(l_str, " string2", strlen(" string2"));
+    dap_assert_PIF(!strcmp(l_str->str, "str int=123 string2"), "Test dap_string_append_len()");
+
+    dap_assert(l_str, "Test dap_string_t");
+    dap_string_free(l_str, true);
+}
+
+void dap_strfuncs_tests_run(void)
+{
+    dap_print_module_name("dap_strfuncs");
+
+    dap_str_dup_test();
+    dap_str_modify_test();
+    dap_str_array_test();
+    dap_list_test();
+    dap_string_test();
+
+    dap_usleep(0.5 * DAP_USEC_PER_SEC);
+    dap_assert(1, "Test dap_usleep(0.5 sec.)");
+
+}
diff --git a/test/dap_strfuncs_test.h b/test/dap_strfuncs_test.h
new file mode 100755
index 0000000..ef2e796
--- /dev/null
+++ b/test/dap_strfuncs_test.h
@@ -0,0 +1,5 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_strfuncs.h"
+
+extern void dap_strfuncs_tests_run(void);
diff --git a/test/main.c b/test/main.c
new file mode 100755
index 0000000..cf2804a
--- /dev/null
+++ b/test/main.c
@@ -0,0 +1,24 @@
+#include "dap_config_test.h"
+#include "dap_common_test.h"
+#include "dap_network_monitor_test.h"
+#include "dap_strfuncs_test.h"
+#include "dap_common.h"
+
+
+int main(void) {
+    // switch off debug info from library
+    dap_log_level_set(L_CRITICAL);
+    dap_strfuncs_tests_run();
+    dap_config_tests_run();
+    dap_common_test_run();
+#ifdef __unix__
+#include "dap_process_mem_test.h"
+#include "dap_cpu_monitor_test.h"
+#include "dap_network_monitor.h"
+#include "dap_circular_test.h"
+    dap_circular_test_run();
+    dap_process_mem_test_run();
+    dap_cpu_monitor_test_run();
+    dap_network_monitor_test_run();
+#endif
+}
diff --git a/test/unix/dap_cpu_monitor_test.c b/test/unix/dap_cpu_monitor_test.c
new file mode 100755
index 0000000..513ec97
--- /dev/null
+++ b/test/unix/dap_cpu_monitor_test.c
@@ -0,0 +1,38 @@
+#include "dap_cpu_monitor_test.h"
+#include "dap_cpu_monitor.h"
+#include "unistd.h"
+
+static void init_test_case()
+{
+    dap_assert(dap_cpu_monitor_init() == 0, "Cpu module init");
+}
+
+static void deinit_test_case()
+{
+    dap_cpu_monitor_deinit();
+}
+
+static void test_cpu_get_stats()
+{
+    dap_cpu_stats_t stat = dap_cpu_get_stats();
+    dap_assert(stat.cpu_cores_count > 0, "Check cpu count");
+    dap_assert(stat.cpu_summary.total_time > 0, "Check cpu summary total_time");
+    dap_assert(stat.cpu_summary.total_time > 0, "Check cpu summary idle_time");
+    dap_assert(stat.cpu_cores_count > 0, "Check cpu count");
+    for(unsigned i = 0; i < stat.cpu_cores_count; i++) {
+        dap_assert_PIF(stat.cpus[i].ncpu == i, "Check ncpu and index in array");
+        dap_assert_PIF(stat.cpus[i].idle_time > 0, "Check cpu idle_time");
+        dap_assert_PIF(stat.cpus[i].total_time > 0, "Check cpu total_time");
+    }
+}
+
+void dap_cpu_monitor_test_run()
+{
+    dap_print_module_name("dap_cpu_monitor");
+    init_test_case();
+    usleep(1000); // wait for new cpu parameters
+    test_cpu_get_stats();
+
+    dap_cpu_get_stats();
+    deinit_test_case();
+}
diff --git a/test/unix/dap_cpu_monitor_test.h b/test/unix/dap_cpu_monitor_test.h
new file mode 100755
index 0000000..e961e83
--- /dev/null
+++ b/test/unix/dap_cpu_monitor_test.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "dap_test.h"
+#include "dap_common.h"
+
+void dap_cpu_monitor_test_run(void);
diff --git a/test/unix/dap_network_monitor_test.c b/test/unix/dap_network_monitor_test.c
new file mode 100755
index 0000000..240b8a7
--- /dev/null
+++ b/test/unix/dap_network_monitor_test.c
@@ -0,0 +1,188 @@
+#include <arpa/inet.h>
+#include "linux/rtnetlink.h"
+
+#include "dap_network_monitor.h"
+#include "dap_network_monitor_test.h"
+
+enum events {
+    NEW_INTERFACE_EV,
+    NEW_GATEWAY_EV,
+    REMOVE_INTERFACE_EV,
+    REMOVE_GATEWAY_EV,
+    REMOVE_ROUTE_EV
+};
+
+#define COUNT_TEST_EVENT_CASES 5
+
+
+static dap_network_notification_t _test_event_cases[COUNT_TEST_EVENT_CASES];
+
+
+static bool list_events_done[COUNT_TEST_EVENT_CASES] = {0};
+
+void _network_callback(const dap_network_notification_t result)
+{
+    if(result.type == IP_ADDR_ADD || result.type == IP_ADDR_REMOVE)
+    {
+        dap_test_msg("Interface %s %s has IP address %s",
+               result.addr.interface_name, (result.type == IP_ADDR_ADD ? "now" : "no longer"),
+               result.addr.s_ip);
+        enum events event;
+        if(result.type == IP_ADDR_ADD) {
+            event = NEW_INTERFACE_EV;
+        } else {
+            event = REMOVE_INTERFACE_EV;
+        }
+
+        dap_test_msg("Checking %s" , (event == NEW_INTERFACE_EV ?
+                                          "add new interface callback" : "remove interface callback"));
+
+        dap_assert(result.addr.ip == _test_event_cases[event].addr.ip,
+                   "Check dest ip");
+
+        dap_assert(dap_str_equals(result.addr.s_ip, _test_event_cases[event].addr.s_ip),
+                   "Check dest str ip");
+
+        dap_assert(dap_str_equals(result.addr.interface_name,
+                                  _test_event_cases[event].addr.interface_name),
+                   "Check interface name");
+
+        list_events_done[event] = true;
+
+    } else if(result.type == IP_ROUTE_ADD || result.type == IP_ROUTE_REMOVE) {
+
+        if (result.type == IP_ROUTE_REMOVE) {
+
+            if(result.route.destination_address == _test_event_cases[REMOVE_GATEWAY_EV].route.gateway_address) {
+                dap_pass_msg("Gateway addr removed");
+                dap_assert(dap_str_equals(result.route.s_destination_address,
+                                          _test_event_cases[REMOVE_GATEWAY_EV].route.s_gateway_address),
+                           "Check gateway str ip");
+
+                dap_assert(result.route.protocol == _test_event_cases[REMOVE_GATEWAY_EV].route.protocol,
+                           "Check protocol");
+
+                list_events_done[REMOVE_GATEWAY_EV] = true;
+            } else if(result.route.destination_address ==
+                      _test_event_cases[REMOVE_ROUTE_EV].route.destination_address) {
+                dap_pass_msg("Destination address removed");
+
+                dap_assert(dap_str_equals(result.route.s_destination_address,
+                                          _test_event_cases[REMOVE_ROUTE_EV].route.s_destination_address),
+                           "Check dest str ip");
+
+                dap_assert(result.route.protocol == _test_event_cases[REMOVE_ROUTE_EV].route.protocol,
+                           "Check protocol");
+
+                list_events_done[REMOVE_ROUTE_EV] = true;
+            }
+
+//            dap_test_msg("Deleting route to destination --> %s/%d proto %d and gateway %s\n",
+//                         result.route.s_destination_address,
+//                         result.route.netmask,
+//                         result.route.protocol,
+//                         result.route.s_gateway_address);
+
+        } else  if (result.type == IP_ROUTE_ADD) {
+            if(result.route.gateway_address != (uint64_t) -1) { // gateway address is present
+                dap_test_msg("Checking new gateway addr");
+                dap_assert(result.route.gateway_address ==
+                           _test_event_cases[NEW_GATEWAY_EV].route.gateway_address,
+                           "Check gateway ip");
+
+                dap_assert(dap_str_equals(result.route.s_gateway_address,
+                                          _test_event_cases[NEW_GATEWAY_EV].route.s_gateway_address),
+                           "Check gateway str ip");
+
+                dap_assert(result.route.protocol == _test_event_cases[NEW_GATEWAY_EV].route.protocol,
+                           "Check protocol");
+
+                list_events_done[NEW_GATEWAY_EV] = true;
+            }
+//            dap_test_msg("Adding route to destination --> %s/%d proto %d and gateway %s\n",
+//                         result.route.s_destination_address,
+//                         result.route.netmask,
+//                         result.route.protocol,
+//                         result.route.s_gateway_address);
+        }
+    }
+}
+
+
+static void init_test_case()
+{
+    bzero(_test_event_cases, sizeof (_test_event_cases));
+
+    dap_network_notification_t * res;
+
+    // new_interface
+    res = &_test_event_cases[NEW_INTERFACE_EV];
+    res->type = IP_ADDR_ADD;
+    strcpy(res->addr.s_ip, "10.1.0.111");
+    strcpy(res->addr.interface_name, "tun10");
+    res->addr.ip = 167837807;
+
+    // new_gateway
+    res = &_test_event_cases[NEW_GATEWAY_EV];
+    res->type = IP_ROUTE_ADD;
+    strcpy(res->route.s_gateway_address, "10.1.0.1");
+    res->route.gateway_address = 167837697;
+    res->route.protocol = RTPROT_STATIC;
+
+    res = &_test_event_cases[REMOVE_GATEWAY_EV];
+    res->type = IP_ROUTE_REMOVE;
+    strcpy(res->route.s_gateway_address, "10.1.0.1");
+    res->route.gateway_address = 167837697;
+    res->route.protocol = RTPROT_STATIC;
+
+
+    // remove interface
+    res = &_test_event_cases[REMOVE_INTERFACE_EV];
+    res->type = IP_ADDR_REMOVE;
+    strcpy(res->addr.s_ip, "10.1.0.111");
+    strcpy(res->addr.interface_name, "tun10");
+    res->addr.ip = 167837807;
+
+    // remote route
+    res = &_test_event_cases[REMOVE_ROUTE_EV];
+    res->type = IP_ROUTE_REMOVE;
+    strcpy(res->route.s_destination_address, "10.1.0.111");
+    res->route.destination_address = 167837807;
+    res->route.protocol = RTPROT_KERNEL;
+}
+
+static void cleanup_test_case()
+{
+
+}
+
+void dap_network_monitor_test_run(void)
+{
+    dap_print_module_name("dap_network_monitor");
+
+    init_test_case();
+
+    dap_network_monitor_init(_network_callback);
+
+    const char *add_test_interfece = "sudo nmcli connection add type tun con-name "
+                                     "DiveVPNTest autoconnect false ifname tun10 "
+                                     "mode tun ip4 10.1.0.111 gw4 10.1.0.1";
+    const char *up_test_interfece = "sudo nmcli connection up DiveVPNTest";
+    const char *down_test_interfece = "sudo nmcli connection down DiveVPNTest";
+    const char *delete_test_interfece = "sudo nmcli connection delete DiveVPNTest 2> /dev/null";
+
+    system(delete_test_interfece);
+    system(add_test_interfece);
+    system(up_test_interfece);
+    system(down_test_interfece);
+    system(delete_test_interfece);
+
+    for(int i = 0; i < COUNT_TEST_EVENT_CASES; i++) {
+        if(list_events_done[i] == false) {
+            dap_fail("Not all events were processed");
+        }
+    }
+
+    dap_network_monitor_deinit();
+    cleanup_test_case();
+}
diff --git a/test/unix/dap_network_monitor_test.h b/test/unix/dap_network_monitor_test.h
new file mode 100755
index 0000000..1dae522
--- /dev/null
+++ b/test/unix/dap_network_monitor_test.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_common.h"
+void dap_network_monitor_test_run(void);
diff --git a/test/unix/dap_process_mem_test.c b/test/unix/dap_process_mem_test.c
new file mode 100755
index 0000000..182d039
--- /dev/null
+++ b/test/unix/dap_process_mem_test.c
@@ -0,0 +1,23 @@
+#include "dap_process_mem_test.h"
+#include "dap_process_memory.h"
+
+void test_current_process()
+{
+    dap_process_memory_t mem = get_proc_mem_current();
+    dap_assert(mem.vsz != 0, "Check vsz current process");
+    dap_assert(mem.rss != 0, "Check rss current process ");
+}
+
+void test_nonexistent_process()
+{
+    dap_process_memory_t mem = get_proc_mem_by_pid(-1);
+    dap_assert(mem.vsz == 0, "Check vsz nonexistent process");
+    dap_assert(mem.rss == 0, "Check rss nonexistent process");
+}
+
+void dap_process_mem_test_run()
+{
+    dap_print_module_name("dap_process_memory");
+    test_current_process();
+    test_nonexistent_process();
+}
diff --git a/test/unix/dap_process_mem_test.h b/test/unix/dap_process_mem_test.h
new file mode 100755
index 0000000..b098a8a
--- /dev/null
+++ b/test/unix/dap_process_mem_test.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_common.h"
+void dap_process_mem_test_run(void);
-- 
GitLab