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