Import Upstream version 1.1.1kylin1
This commit is contained in:
commit
2c07017a35
|
@ -0,0 +1,35 @@
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
|
||||||
|
project(kysdk-base)
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
include_directories("${PROJECT_BINARY_DIR}")
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
||||||
|
add_definitions(-Drelease)
|
||||||
|
set(CMAKE_C_FLAGS "$ENV{CFLAGS} -O1 -Wall")
|
||||||
|
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -O1 -Wall")
|
||||||
|
elseif(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||||
|
set(CMAKE_C_FLAGS "$ENV{CFLAGS} -g -O0 -Wall")
|
||||||
|
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -g -O0 -Wall")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message("Build Type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||||
|
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||||
|
|
||||||
|
set (CMAKE_INSTALL_PREFIX /usr)
|
||||||
|
set (CMAKE_INSTALL_RPATH /usr/lib/kysdk/kysdk-base)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
|
@ -0,0 +1,6 @@
|
||||||
|
base_version=1.1.1
|
||||||
|
|
||||||
|
Name: libkysdk-base
|
||||||
|
Description: kysdk base layer
|
||||||
|
Requires: kysdk-log >= ${base_version} kysdk-config >= ${base_version} kysdk-timer >= ${base_version} kysdk-utils >= ${base_version}
|
||||||
|
Version: 1.1.1
|
|
@ -0,0 +1,8 @@
|
||||||
|
base_version=1.1.1
|
||||||
|
|
||||||
|
Name: libkysdk-config
|
||||||
|
Description: kysdk base layer config component
|
||||||
|
Requires: kysdk-utils
|
||||||
|
Version: 1.1.1
|
||||||
|
Cflags: -I/usr/include/kysdk/kysdk-base/
|
||||||
|
Libs: -L/usr/lib/kysdk/kysdk-base/ -lkyconf -Wl,-rpath=/usr/lib/kysdk/kysdk-base/
|
|
@ -0,0 +1,8 @@
|
||||||
|
base_version=1.1.1
|
||||||
|
|
||||||
|
Name: libkysdk-log
|
||||||
|
Description: kysdk base layer log component
|
||||||
|
Requires: kysdk-config kysdk-utils libsystemd
|
||||||
|
Version: 1.1.1
|
||||||
|
Cflags: -I/usr/include/kysdk/kysdk-base/
|
||||||
|
Libs: -L/usr/lib/kysdk/kysdk-base/ -lkylog -Wl,-rpath=/usr/lib/kysdk/kysdk-base/
|
|
@ -0,0 +1,6 @@
|
||||||
|
Name: libkysdk-timer
|
||||||
|
Description: kysdk base layer time component
|
||||||
|
Requires: kysdk-utils
|
||||||
|
Version: 1.1.1
|
||||||
|
Cflags: -I/usr/include/kysdk/kysdk-base/
|
||||||
|
Libs: -L/usr/lib/kysdk/kysdk-base/ -lkytimer -Wl,-rpath=/usr/lib/kysdk/kysdk-base/
|
|
@ -0,0 +1,5 @@
|
||||||
|
Name: libkysdk-utils
|
||||||
|
Description: kysdk base layer utils component
|
||||||
|
Version: 1.1.1
|
||||||
|
Cflags:-I/usr/include/kysdk/kysdk-base/
|
||||||
|
Libs: -L/usr/lib/kysdk/kysdk-base/ -lkydatastruct -Wl,-rpath=/usr/lib/kysdk/kysdk-base/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
||||||
|
include_directories(config)
|
||||||
|
include_directories(utils)
|
||||||
|
include_directories(log)
|
||||||
|
include_directories(timer)
|
||||||
|
|
||||||
|
# add_subdirectory(communication)
|
||||||
|
add_subdirectory(config)
|
||||||
|
# add_subdirectory(debbuger)
|
||||||
|
add_subdirectory(log)
|
||||||
|
# add_subdirectory(process)
|
||||||
|
# add_subdirectory(thread)
|
||||||
|
add_subdirectory(timer)
|
||||||
|
add_subdirectory(utils)
|
|
@ -0,0 +1,10 @@
|
||||||
|
aux_source_directory(. SOURCESCODE)
|
||||||
|
add_library(kyconf SHARED ${SOURCESCODE})
|
||||||
|
add_executable(kyconf-test-struct test/test_structlist.c)
|
||||||
|
target_link_libraries(kyconf-test-struct kyconf kylog pthread)
|
||||||
|
|
||||||
|
install(TARGETS kyconf
|
||||||
|
DESTINATION lib/kysdk/kysdk-base)
|
||||||
|
|
||||||
|
# install(FILES libkyconf.h
|
||||||
|
# DESTINATION include/kysdk/kysdk-base)
|
|
@ -0,0 +1,20 @@
|
||||||
|
OBJ=gsettingsparse.o jsonparse.o structparse.o xmlparse.o
|
||||||
|
|
||||||
|
CFLAGS=-g -O0 -Wall -fPIC -I../ -I../utils/ -I../config/
|
||||||
|
CLIBS=-lpthread
|
||||||
|
CC:=$(shell bash -c 'type $(CC) &> /dev/null && echo $(CC) || echo gcc')
|
||||||
|
TARGET=libkyconf.so
|
||||||
|
LIBINST=libkyconf.so.1.0.0
|
||||||
|
LIBHEAD=kyconf.h
|
||||||
|
LIBINSTHEADER=/usr/include/kysdk/base/
|
||||||
|
|
||||||
|
all:$(OBJ)
|
||||||
|
$(CC) -o $(LIBINST) configure.c $(OBJ) $(CFLAGS) $(CLIBS) -shared
|
||||||
|
mkdir -p lib/
|
||||||
|
mv -f $(LIBINST) lib/
|
||||||
|
cd lib/ && ln -sf $(LIBINST) $(TARGET) && cd ..
|
||||||
|
-rm *.o &> /dev/null
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm *.o &> /dev/null
|
||||||
|
-rm ../lib/$(TARGET) &> /dev/null
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef KYSDK_BASE_CONFIGURE_TYPE_H__
|
||||||
|
#define KYSDK_BASE_CONFIGURE_TYPE_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef union KconfigureValue
|
||||||
|
{
|
||||||
|
int asInt;
|
||||||
|
long long asLLong;
|
||||||
|
double asDouble;
|
||||||
|
char *asString;
|
||||||
|
}KconfigureValue;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KconfigureInt,
|
||||||
|
KconfigureLLong,
|
||||||
|
KconfigureDouble,
|
||||||
|
KconfigureString
|
||||||
|
}KconfigureValueType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KconfigureNodeRoot,
|
||||||
|
KconfigureNodeGroup,
|
||||||
|
KconfigureNodeNode
|
||||||
|
}KconfigureNodeType;
|
||||||
|
|
||||||
|
typedef struct KconfigureDataNode{
|
||||||
|
char *group;
|
||||||
|
char *key;
|
||||||
|
size_t children_nums;
|
||||||
|
size_t max_children_nums;
|
||||||
|
char* value;
|
||||||
|
// KconfigureValueType valType;
|
||||||
|
KconfigureNodeType nodeType;
|
||||||
|
struct KconfigureDataNode **children;
|
||||||
|
}KconfigureDataNode;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "gsettingsparse.h"
|
||||||
|
|
||||||
|
int isgsettings(const char *conf)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef KDK_BASE_CONF_GSETTINGS_H__
|
||||||
|
#define KDK_BASE_CONF_GSETTINGS_H__
|
||||||
|
|
||||||
|
extern int isgsettings(const char *conf);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "jsonparse.h"
|
||||||
|
|
||||||
|
int isjson(const char *conf)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef KDK_BASE_CONF_JSON_H__
|
||||||
|
#define KDK_BASE_CONF_JSON_H__
|
||||||
|
|
||||||
|
extern int isjson(const char *conf);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,332 @@
|
||||||
|
#include "libkyconf.h"
|
||||||
|
#include "structparse.h"
|
||||||
|
#include <kerr.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
KDK_CONF_XML,
|
||||||
|
KDK_CONF_JSON,
|
||||||
|
KDK_CONF_GSETTINGS,
|
||||||
|
KDK_CONF_STRUCT
|
||||||
|
}KconfigureType;
|
||||||
|
|
||||||
|
typedef struct KconfigureSettings
|
||||||
|
{
|
||||||
|
char *confpath;
|
||||||
|
unsigned int id;
|
||||||
|
int8_t watch; // 监听配置变化
|
||||||
|
KconfigureType type;
|
||||||
|
void *data; // 指向配置解析数据的指针
|
||||||
|
}KconfigureSettings;
|
||||||
|
|
||||||
|
typedef struct KconfigureList
|
||||||
|
{
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
unsigned int counts;
|
||||||
|
unsigned int max_nums;
|
||||||
|
KconfigureSettings **conflist;
|
||||||
|
}KconfigureList;
|
||||||
|
|
||||||
|
static KconfigureList g_conflist;
|
||||||
|
|
||||||
|
static void init_conf_global()
|
||||||
|
{
|
||||||
|
static int inited;
|
||||||
|
// FixMe: 竞态风险
|
||||||
|
if (inited)
|
||||||
|
return;
|
||||||
|
inited = 1;
|
||||||
|
pthread_mutex_init(&g_conflist.lock, NULL);
|
||||||
|
g_conflist.counts = 0;
|
||||||
|
g_conflist.max_nums = 5;
|
||||||
|
g_conflist.conflist = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KconfigureType get_conf_type(const char* confpath)
|
||||||
|
{
|
||||||
|
if (isxml(confpath))
|
||||||
|
return KDK_CONF_XML;
|
||||||
|
else if (isjson(confpath))
|
||||||
|
return KDK_CONF_JSON;
|
||||||
|
else if (isgsettings(confpath))
|
||||||
|
return KDK_CONF_GSETTINGS;
|
||||||
|
return KDK_CONF_STRUCT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int kdk_test_file_exist(const char *file) ALWAYSINLINE;
|
||||||
|
static inline int kdk_test_file_exist(const char *file)
|
||||||
|
{
|
||||||
|
return access(file, R_OK | F_OK) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int lock_conf_list() ALWAYSINLINE;
|
||||||
|
static inline int unlock_conf_list() ALWAYSINLINE;
|
||||||
|
static inline int lock_conf_list()
|
||||||
|
{
|
||||||
|
return pthread_mutex_lock(&g_conflist.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int unlock_conf_list()
|
||||||
|
{
|
||||||
|
return pthread_mutex_unlock(&g_conflist.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KconfigureSettings* new_conf_settings()
|
||||||
|
{
|
||||||
|
KconfigureSettings *res = (KconfigureSettings *)calloc(1, sizeof(KconfigureSettings));
|
||||||
|
ASSERT_NOT_NULL(res, NULL);
|
||||||
|
lock_conf_list();
|
||||||
|
if (! g_conflist.conflist)
|
||||||
|
{
|
||||||
|
g_conflist.conflist = (KconfigureSettings **)malloc(sizeof(KconfigureSettings *) * g_conflist.max_nums);
|
||||||
|
if (! g_conflist.conflist)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (g_conflist.counts == g_conflist.max_nums)
|
||||||
|
{
|
||||||
|
g_conflist.max_nums += 5;
|
||||||
|
KconfigureSettings **tmp = g_conflist.conflist;
|
||||||
|
g_conflist.conflist = (KconfigureSettings **)realloc(g_conflist.conflist, \
|
||||||
|
sizeof(KconfigureSettings *) * g_conflist.max_nums);
|
||||||
|
if (! g_conflist.conflist)
|
||||||
|
{
|
||||||
|
g_conflist.conflist = tmp;
|
||||||
|
g_conflist.max_nums -= 5;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_conflist.conflist[g_conflist.counts] = res;
|
||||||
|
// counts仅单调递增
|
||||||
|
g_conflist.counts ++;
|
||||||
|
res->id = g_conflist.counts;
|
||||||
|
unlock_conf_list();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
err:
|
||||||
|
unlock_conf_list();
|
||||||
|
free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_conf_settings(KconfigureSettings *conf)
|
||||||
|
{
|
||||||
|
if (conf)
|
||||||
|
{
|
||||||
|
lock_conf_list();
|
||||||
|
// 根据配置文件的open/close频率特性,我们没有必要减少其引用计数和回收已经realloc的内存,置空就行了
|
||||||
|
g_conflist.conflist[conf->id - 1] = NULL;
|
||||||
|
unlock_conf_list();
|
||||||
|
SAFE_FREE(conf->confpath);
|
||||||
|
free(conf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdk_conf_init(const char* confpath)
|
||||||
|
{
|
||||||
|
if (!confpath || !kdk_test_file_exist(confpath))
|
||||||
|
return -KDK_EINVALIDARGS;
|
||||||
|
|
||||||
|
init_conf_global();
|
||||||
|
|
||||||
|
KconfigureSettings *conf = new_conf_settings();
|
||||||
|
ASSERT_NOT_NULL(conf, -1);
|
||||||
|
|
||||||
|
conf->confpath = (char *)malloc(sizeof(char) * (strlen(confpath) + 1));
|
||||||
|
if (! conf->confpath)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
strcpy(conf->confpath, confpath);
|
||||||
|
|
||||||
|
conf->type = get_conf_type(confpath);
|
||||||
|
|
||||||
|
if (conf->type == KDK_CONF_XML)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_JSON)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_GSETTINGS)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
conf->data = S_newParse();
|
||||||
|
if (!conf->data)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (S_parseFile(conf->data, conf->confpath))
|
||||||
|
{
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->id;
|
||||||
|
|
||||||
|
err:
|
||||||
|
free_conf_settings(conf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_conf_destroy(int id)
|
||||||
|
{
|
||||||
|
if (id > 0)
|
||||||
|
free_conf_settings(g_conflist.conflist[id - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdk_conf_reload(int id)
|
||||||
|
{
|
||||||
|
if (id <= 0)
|
||||||
|
return -KDK_EINVALIDARGS;
|
||||||
|
|
||||||
|
KconfigureSettings *conf = g_conflist.conflist[id - 1];
|
||||||
|
ASSERT_NOT_NULL(conf, -1);
|
||||||
|
|
||||||
|
if (conf->type == KDK_CONF_XML)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_JSON)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_GSETTINGS)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void *data = S_newParse();
|
||||||
|
if (! data)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (S_parseFile(data, conf->confpath))
|
||||||
|
{
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
S_destroyParse(&conf->data);
|
||||||
|
conf->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_conf_enable_autoreload(int id)
|
||||||
|
{
|
||||||
|
#ifdef KYSDK_FILEWATCHER_H
|
||||||
|
#endif // KYSDK_FILEWATCHER_H
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_conf_disable_autoreload(int id)
|
||||||
|
{
|
||||||
|
#ifdef KYSDK_FILEWATCHER_H
|
||||||
|
#endif // KYSDK_FILEWATCHER_H
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* kdk_conf_get_value(int id, const char* group, const char* key)
|
||||||
|
{
|
||||||
|
if (id <= 0 || id > g_conflist.counts || ! key)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!group || strlen(group) == 0)
|
||||||
|
group = "KDK_DefaultGroup";
|
||||||
|
const char *res = "";
|
||||||
|
|
||||||
|
KconfigureSettings *conf = g_conflist.conflist[id - 1];
|
||||||
|
ASSERT_NOT_NULL(conf, NULL);
|
||||||
|
|
||||||
|
if (conf->type == KDK_CONF_XML)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_JSON)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_GSETTINGS)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = S_getValue(conf->data, group, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdk_conf_set_value(int id, const char* group, const char* key, const char* value)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** const kdk_conf_list_key(int id, const char* group)
|
||||||
|
{
|
||||||
|
if (id < 1 || id > g_conflist.counts || ! group)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char** res = NULL;
|
||||||
|
KconfigureSettings *conf = g_conflist.conflist[id - 1];
|
||||||
|
ASSERT_NOT_NULL(conf, NULL);
|
||||||
|
|
||||||
|
if (conf->type == KDK_CONF_XML)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_JSON)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_GSETTINGS)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = S_getKeyList(conf->data, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** const kdk_conf_list_group(int id)
|
||||||
|
{
|
||||||
|
if (id < 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char** res = NULL;
|
||||||
|
KconfigureSettings *conf = g_conflist.conflist[id - 1];
|
||||||
|
ASSERT_NOT_NULL(conf, NULL);
|
||||||
|
|
||||||
|
if (conf->type == KDK_CONF_XML)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_JSON)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (conf->type == KDK_CONF_GSETTINGS)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = S_getGroupList(conf->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
#ifndef KYSDK_BASE_CONFIGURE_H__
|
||||||
|
#define KYSDK_BASE_CONFIGURE_H__
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup 配置文件模块
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file libkyconf.h
|
||||||
|
* @author liuyunhe (liuyunhe@kylinos.cn)
|
||||||
|
* @brief KYSDK配置文件处理库,支持标准格式、XML(未实现)、JSON(未实现)的配置文件处理,包括配置文件的读与写操作
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2021-10-28
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sdkmarcos.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化配置文件
|
||||||
|
*
|
||||||
|
* @param confpath 配置文件的路径
|
||||||
|
* @return int 成功返回句柄号(非负值),失败返回错误码(负值)
|
||||||
|
*/
|
||||||
|
extern int kdk_conf_init(const char* confpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 销毁指定的配置文件句柄
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的配置文件句柄
|
||||||
|
*/
|
||||||
|
extern void kdk_conf_destroy(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重新载入配置文件
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的配置文件句柄
|
||||||
|
* @return int 成功返回0,失败返回错误码
|
||||||
|
*/
|
||||||
|
extern int kdk_conf_reload(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief [未实现] 启用配置文件自动刷新功能,自动刷新会在配置文件被修改时,自动将所有的配置项重新载入内存;下次再获取某个值时就是最新的配置值了
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的配置文件句柄
|
||||||
|
*/
|
||||||
|
extern void kdk_conf_enable_autoreload(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief [未实现] 禁用配置文件自动刷新功能
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的配置文件句柄
|
||||||
|
*/
|
||||||
|
extern void kdk_conf_disable_autoreload(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取指定配置项的值
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的配置文件句柄
|
||||||
|
* @param group key所在的组名称
|
||||||
|
* @param key 配置项名称
|
||||||
|
* @return const char* 配置项所拥有的值,若key不存在,则返回一个空字符串
|
||||||
|
*/
|
||||||
|
extern const char* kdk_conf_get_value(int id, const char* group, const char* key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief [未实现] 设置对应key的值
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的配置文件句柄
|
||||||
|
* @param group key所对应的组名称
|
||||||
|
* @param key 需要修改的key的名称,若key原来不存在于该Group中,则会自动添加一个key
|
||||||
|
* @param value 需要修改为的值
|
||||||
|
* @return int 成功返回0,失败返回错误码
|
||||||
|
*/
|
||||||
|
extern int kdk_conf_set_value(int id, const char* group, const char* key, const char* value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 列举id对应配置文件的指定Group下的key值,结尾以NULL指针表示
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的句柄值
|
||||||
|
* @param group 需要列举的Group名称
|
||||||
|
* @return const char** const 以NULL结尾的字符串列表,每个字符串都是一个key名称,字符串列表本身是由alloc分配的内存,需要被free释放;字符串不需要释放
|
||||||
|
*/
|
||||||
|
extern char** const kdk_conf_list_key(int id, const char* group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 列举id对应配置文件的所有Group,结尾以NULL指针表示
|
||||||
|
*
|
||||||
|
* @param id 由kdk_conf_init返回的句柄值
|
||||||
|
* @return const char** const 以NULL结尾的字符串列表,每个字符串都是一个组名称,字符串列表本身是由alloc分配的内存,需要被free释放;字符串不需要释放
|
||||||
|
*/
|
||||||
|
extern char** const kdk_conf_list_group(int id);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //KYSDK_BASE_CONFIGURE_H__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \example kysdk-base/src/config/test/test_structlist.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,322 @@
|
||||||
|
#include "structparse.h"
|
||||||
|
#include <sdkmarcos.h>
|
||||||
|
#include <cstring-extension.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define LINEMAX 1024
|
||||||
|
|
||||||
|
|
||||||
|
static KconfigureDataNode* get_group_node(structParse *parse, const char *group)
|
||||||
|
{
|
||||||
|
KconfigureDataNode **grouplist = parse->content.groupList;
|
||||||
|
size_t groupSize = parse->content.curGroupSize;
|
||||||
|
for (size_t i = 0; i < groupSize; i ++)
|
||||||
|
{
|
||||||
|
if (strcmp(grouplist[i]->group, group) == 0)
|
||||||
|
return grouplist[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KconfigureDataNode* get_key_node(KconfigureDataNode *groupnode, const char *key)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < groupnode->children_nums; i ++)
|
||||||
|
{
|
||||||
|
if (strcmp(groupnode->children[i]->key, key) == 0)
|
||||||
|
return groupnode->children[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KconfigureDataNode* append_group(structParse *parse, const char *group)
|
||||||
|
{
|
||||||
|
if (parse->content.maxGroupSize <= parse->content.curGroupSize)
|
||||||
|
{
|
||||||
|
KconfigureDataNode **newGroupList = (KconfigureDataNode **)realloc(parse->content.groupList, (parse->content.maxGroupSize + 4) * sizeof(KconfigureDataNode *));
|
||||||
|
if (!newGroupList)
|
||||||
|
{
|
||||||
|
parse->content.curGroupSize --;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse->content.maxGroupSize += 4;
|
||||||
|
parse->content.groupList = newGroupList;
|
||||||
|
}
|
||||||
|
KconfigureDataNode *groupnode = (KconfigureDataNode *)calloc(1, sizeof(KconfigureDataNode));
|
||||||
|
if (!group)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
groupnode->nodeType = KconfigureNodeGroup;
|
||||||
|
groupnode->group = (char *)group;
|
||||||
|
parse->content.groupList[parse->content.curGroupSize] = groupnode;
|
||||||
|
parse->content.curGroupSize ++;
|
||||||
|
|
||||||
|
return groupnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KconfigureDataNode* append_key(KconfigureDataNode *groupnode, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
KconfigureDataNode *keynode = NULL;
|
||||||
|
for (size_t i = 0; i < groupnode->children_nums; i ++)
|
||||||
|
{
|
||||||
|
if (strcmp(groupnode->children[i]->key, key) == 0)
|
||||||
|
{
|
||||||
|
keynode = groupnode->children[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建key
|
||||||
|
if (! keynode)
|
||||||
|
{
|
||||||
|
if (groupnode->max_children_nums <= groupnode->children_nums)
|
||||||
|
{
|
||||||
|
KconfigureDataNode **newKeyList = (KconfigureDataNode **)realloc(groupnode->children, (groupnode->max_children_nums + 4) * sizeof(KconfigureDataNode *));
|
||||||
|
if (! newKeyList)
|
||||||
|
return NULL;
|
||||||
|
groupnode->max_children_nums += 4;
|
||||||
|
groupnode->children = newKeyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
keynode = (KconfigureDataNode *)calloc(1, sizeof(KconfigureDataNode));
|
||||||
|
if (!keynode)
|
||||||
|
return NULL;
|
||||||
|
groupnode->children[groupnode->children_nums] = keynode;
|
||||||
|
keynode->group = strdup(groupnode->group);
|
||||||
|
keynode->key = (char *)key;
|
||||||
|
groupnode->children_nums ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
keynode->value = (char *)value;
|
||||||
|
// keynode->valType = KconfigureString;
|
||||||
|
|
||||||
|
return keynode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parseFile(structParse *parse, const char *filepath)
|
||||||
|
{
|
||||||
|
FILE *cfp = fopen(filepath, "rt");
|
||||||
|
ASSERT_NOT_NULL(cfp, -1);
|
||||||
|
|
||||||
|
KconfigureDataNode *cur_group_node = NULL;
|
||||||
|
|
||||||
|
char buf[LINEMAX];
|
||||||
|
while (feof(cfp) == 0)
|
||||||
|
{
|
||||||
|
if (fgets(buf, LINEMAX, cfp) == NULL)
|
||||||
|
break;
|
||||||
|
#ifdef __linux__
|
||||||
|
strstrip(buf, '\n');
|
||||||
|
strskipblank(buf);
|
||||||
|
#elif defined __win32__
|
||||||
|
strstrip(buf, '\n');
|
||||||
|
strstrip(buf, '\r');
|
||||||
|
#endif
|
||||||
|
if (strlen(buf) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
if (strstartswith(buf, "#") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Group
|
||||||
|
if (strstartswith(buf, "[") == 0 && strendwith(buf, "]") == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
cur_group_node = append_group(parse, strndup(&buf[1], strlen(buf) - 2));
|
||||||
|
if (!cur_group_node)
|
||||||
|
{
|
||||||
|
// err output
|
||||||
|
fclose(cfp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key
|
||||||
|
int depos = strfirstof(buf, parse->delimiter.assignDelimiter);
|
||||||
|
char *keyStr, *valStr;
|
||||||
|
if (depos < 0)
|
||||||
|
{
|
||||||
|
// 没有赋值符号,可能是什么情况?
|
||||||
|
keyStr = buf;
|
||||||
|
valStr = "";
|
||||||
|
strstripspace(keyStr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf[depos] = 0;
|
||||||
|
keyStr = buf;
|
||||||
|
valStr = &buf[depos + 1];
|
||||||
|
strstripspace(keyStr);
|
||||||
|
strstripspace(valStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
KconfigureDataNode *groupnode = cur_group_node;
|
||||||
|
if (!groupnode)
|
||||||
|
{
|
||||||
|
// Default group
|
||||||
|
char *defaultnode = malloc(36);
|
||||||
|
if (! defaultnode)
|
||||||
|
{
|
||||||
|
// OOM
|
||||||
|
fclose(cfp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strcpy(defaultnode, "KDK_DefaultGroup");
|
||||||
|
|
||||||
|
cur_group_node = append_group(parse, defaultnode);
|
||||||
|
groupnode = cur_group_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多重赋值的情况
|
||||||
|
size_t multikeys = strcounts(keyStr, parse->delimiter.keyDelimiter);
|
||||||
|
if (multikeys)
|
||||||
|
{
|
||||||
|
multikeys += 1;
|
||||||
|
char **keylist = strsplit(keyStr, parse->delimiter.keyDelimiter);
|
||||||
|
if (!keylist)
|
||||||
|
{
|
||||||
|
// OOM
|
||||||
|
fclose(cfp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t multivals = strcounts(valStr, parse->delimiter.valueDelimiter) + 1;
|
||||||
|
char **vallist = strsplit(valStr, parse->delimiter.valueDelimiter);
|
||||||
|
if (!vallist)
|
||||||
|
{
|
||||||
|
// OOM
|
||||||
|
free(keylist);
|
||||||
|
fclose(cfp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < multikeys; i ++)
|
||||||
|
{
|
||||||
|
append_key(groupnode, strdup(strskipspace(keylist[i])), strdup(i >= multivals ? "" : strskipspace(vallist[i])));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(keylist);
|
||||||
|
free(vallist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
append_key(groupnode, strdup(keyStr), strdup(valStr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(cfp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
structParse* S_newParse()
|
||||||
|
{
|
||||||
|
structParse *parse = (structParse *)calloc(1, sizeof(structParse));
|
||||||
|
ASSERT_NOT_NULL(parse, NULL);
|
||||||
|
parse->delimiter.assignDelimiter = '=';
|
||||||
|
parse->delimiter.keyDelimiter = ',';
|
||||||
|
parse->delimiter.valueDelimiter = ',';
|
||||||
|
return parse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void S_setKeyDelimiter(structParse *parse, char ch)
|
||||||
|
{
|
||||||
|
parse->delimiter.keyDelimiter = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void S_setValueDelimiter(structParse *parse, char ch)
|
||||||
|
{
|
||||||
|
parse->delimiter.valueDelimiter = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void S_setAssignmentDelimiter(structParse *parse, char ch)
|
||||||
|
{
|
||||||
|
parse->delimiter.assignDelimiter = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
int S_parseFile(structParse *parse, const char *filename)
|
||||||
|
{
|
||||||
|
if (access(filename, F_OK))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (parse->confpath)
|
||||||
|
{
|
||||||
|
free(parse->confpath);
|
||||||
|
parse->confpath = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse->confpath = (char*)malloc(strlen(filename) + 1);
|
||||||
|
ASSERT_NOT_NULL(parse->confpath, -1);
|
||||||
|
strcpy(parse->confpath, filename);
|
||||||
|
return parseFile(parse, parse->confpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* const S_getValue(structParse *parse, const char *group, const char *key)
|
||||||
|
{
|
||||||
|
KconfigureDataNode *groupnode = get_group_node(parse, group);
|
||||||
|
|
||||||
|
ASSERT_NOT_NULL(groupnode, "");
|
||||||
|
|
||||||
|
KconfigureDataNode *keynode = get_key_node(groupnode, key);
|
||||||
|
|
||||||
|
ASSERT_NOT_NULL(keynode, "");
|
||||||
|
|
||||||
|
return keynode->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void S_setValue(structParse *parse, const char *group, const char *key, KconfigureValue value, KconfigureValueType valType)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int S_writeBack(structParse *parse)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int S_write2File(structParse *parse, const char *filename)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void S_destroyParse(structParse **parse)
|
||||||
|
{
|
||||||
|
if (!parse || !*parse)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SAFE_FREE((*parse)->confpath);
|
||||||
|
|
||||||
|
|
||||||
|
free(*parse);
|
||||||
|
}
|
||||||
|
|
||||||
|
char** const S_getKeyList(structParse *parse, const char *group)
|
||||||
|
{
|
||||||
|
KconfigureDataNode *pg = get_group_node(parse, group);
|
||||||
|
ASSERT_NOT_NULL(pg, NULL);
|
||||||
|
char **res = (char **)calloc(1, (pg->children_nums + 1) * sizeof(char *));
|
||||||
|
ASSERT_NOT_NULL(res, NULL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < pg->children_nums; i ++)
|
||||||
|
{
|
||||||
|
res[i] = pg->children[i]->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** const S_getGroupList(structParse *parse)
|
||||||
|
{
|
||||||
|
char **res = (char **)calloc(1, (parse->content.curGroupSize + 1) * sizeof(char *));
|
||||||
|
ASSERT_NOT_NULL(res, NULL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < parse->content.curGroupSize; i ++)
|
||||||
|
{
|
||||||
|
res[i] = parse->content.groupList[i]->group;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef KYSDK_BASE_CONFIGURE_STRUCT_H__
|
||||||
|
#define KYSDK_BASE_CONFIGURE_STRUCT_H__
|
||||||
|
|
||||||
|
#include "datatype.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
KconfigureDataNode **groupList; // Group List
|
||||||
|
size_t maxGroupSize;
|
||||||
|
size_t curGroupSize;
|
||||||
|
}structContenct;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
char keyDelimiter;
|
||||||
|
char valueDelimiter;
|
||||||
|
char assignDelimiter;
|
||||||
|
}delimiter;
|
||||||
|
|
||||||
|
char *confpath;
|
||||||
|
structContenct content;
|
||||||
|
}structParse;
|
||||||
|
|
||||||
|
extern structParse* S_newParse();
|
||||||
|
extern void S_setKeyDelimiter(structParse *parse, char ch);
|
||||||
|
extern void S_setValueDelimiter(structParse *parse, char ch);
|
||||||
|
extern void S_setAssignmentDelimiter(structParse *parse, char ch);
|
||||||
|
extern int S_parseFile(structParse *parse, const char *filename);
|
||||||
|
extern char* const S_getValue(structParse *parse, const char *group, const char *key);
|
||||||
|
extern void S_setValue(structParse *parse, const char *group, const char *key, KconfigureValue value, KconfigureValueType valType);
|
||||||
|
extern char** const S_getKeyList(structParse *parse, const char *group);
|
||||||
|
extern char** const S_getGroupList(structParse *parse);
|
||||||
|
extern int S_writeBack(structParse *parse);
|
||||||
|
extern int S_write2File(structParse *parse, const char *filename);
|
||||||
|
extern void S_destroyParse(structParse **parse);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
LDFLAGS =
|
||||||
|
FILE=$(file)
|
||||||
|
CFLAGS=$(cflags)
|
||||||
|
CFLAGS += -g -O0 -std=gnu11 -Wall -DPWD=\"$(PWD)\" -I/usr/include/glib-2.0/glib/ -I../ -I../../utils/
|
||||||
|
CLIBS= -lpthread
|
||||||
|
CLIBS += $(clibs)
|
||||||
|
|
||||||
|
all:list
|
||||||
|
|
||||||
|
list:test_structlist.c ../configure.c ../structparse.c ../jsonparse.c ../gsettingsparse.c ../xmlparse.c
|
||||||
|
gcc -o $(basename $<) $^ $(CFLAGS) $(CLIBS)
|
|
@ -0,0 +1,15 @@
|
||||||
|
multest1, multest2, multest3 = hello1, hello2, hello3, hello4
|
||||||
|
defaultkey= 1
|
||||||
|
unreachable = false
|
||||||
|
|
||||||
|
[Testmode]
|
||||||
|
# comments : this is test mode
|
||||||
|
key1 = value1
|
||||||
|
key2 = value2
|
||||||
|
key3, key4 = value3, value4
|
||||||
|
key5, key6 = value5
|
||||||
|
key1 = override1
|
||||||
|
|
||||||
|
[System]
|
||||||
|
OS = KylinOS
|
||||||
|
Version = v10sp1
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include <libkyconf.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int id = kdk_conf_init("struct.conf");
|
||||||
|
ASSERT_NOT_NULL(id, -1);
|
||||||
|
|
||||||
|
char **grouplist = kdk_conf_list_group(id);
|
||||||
|
ASSERT_NOT_NULL(grouplist, -1);
|
||||||
|
|
||||||
|
char *tmpgroup;
|
||||||
|
int index = 0;
|
||||||
|
while ((tmpgroup = grouplist[index]))
|
||||||
|
{
|
||||||
|
printf("Group: %s\n", tmpgroup);
|
||||||
|
char **keylist = kdk_conf_list_key(id, tmpgroup);
|
||||||
|
ASSERT_NOT_NULL(keylist, -1);
|
||||||
|
char *tmpkey;
|
||||||
|
int k_index = 0;
|
||||||
|
while ((tmpkey = keylist[k_index]))
|
||||||
|
{
|
||||||
|
const char *tmpval = kdk_conf_get_value(id, tmpgroup, tmpkey);
|
||||||
|
printf("%s = %s\n", tmpkey , tmpval);
|
||||||
|
k_index ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
index ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
kdk_conf_destroy(id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "xmlparse.h"
|
||||||
|
|
||||||
|
int isxml(const char *conf)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef KDK_BASE_CONF_XML_H__
|
||||||
|
#define KDK_BASE_CONF_XML_H__
|
||||||
|
|
||||||
|
extern int isxml(const char *conf);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,21 @@
|
||||||
|
aux_source_directory(. SOURCESCODE)
|
||||||
|
add_library(kylog SHARED ${SOURCESCODE})
|
||||||
|
add_executable(kylog-testlog test/test-log.c)
|
||||||
|
add_executable(kylog-testsetdir test/test-setdir.c)
|
||||||
|
add_executable(kylog-testpressure test/test-pressure.c)
|
||||||
|
add_executable(kylog-testautowrap test/test-autowrap.c)
|
||||||
|
find_library(SYSTEMD_LIB systemd)
|
||||||
|
target_link_libraries(kylog kyconf pthread ${SYSTEMD_LIB})
|
||||||
|
target_link_libraries(kylog-testlog kylog)
|
||||||
|
target_link_libraries(kylog-testsetdir kylog)
|
||||||
|
target_link_libraries(kylog-testautowrap kylog)
|
||||||
|
target_link_libraries(kylog-testpressure kylog)
|
||||||
|
|
||||||
|
install(TARGETS kylog
|
||||||
|
DESTINATION lib/kysdk/kysdk-base)
|
||||||
|
|
||||||
|
install(FILES libkylog.h
|
||||||
|
DESTINATION include/kysdk/kysdk-base)
|
||||||
|
|
||||||
|
install(FILES kylog-rotate-default
|
||||||
|
DESTINATION /etc/kysdk/kysdk-base)
|
|
@ -0,0 +1,20 @@
|
||||||
|
OBJ=core.o format.o klog_mqueue.o kylog.o write.o writeFile.o
|
||||||
|
|
||||||
|
CFLAGS=-g -O0 -Wall -fPIC -I../ -I../utils/ -I../config/
|
||||||
|
# CLIBS=-lpthread -lsystemd
|
||||||
|
CC:=$(shell bash -c 'type $(CC) &> /dev/null && echo $(CC) || echo gcc')
|
||||||
|
TARGET=libkylog.so
|
||||||
|
LIBINST=libkylog.so.1.0.0
|
||||||
|
LIBHEAD=kylog.h
|
||||||
|
LIBINSTHEADER=/usr/include/kylog/
|
||||||
|
|
||||||
|
all:$(OBJ)
|
||||||
|
$(CC) -o $(LIBINST) $(OBJ) $(CFLAGS) $(CLIBS) -shared
|
||||||
|
mkdir -p lib/
|
||||||
|
mv -f $(LIBINST) lib/
|
||||||
|
cd lib/ && ln -sf $(LIBINST) $(TARGET) && cd ..
|
||||||
|
-rm *.o &> /dev/null
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm *.o &> /dev/null
|
||||||
|
-rm ../lib/$(TARGET) &> /dev/null
|
|
@ -0,0 +1,336 @@
|
||||||
|
#include "core.h"
|
||||||
|
#include "klog_dump.h"
|
||||||
|
#include <kerr.h>
|
||||||
|
#include <libkyconf.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
KLogger* logger;
|
||||||
|
const char* stringLevel[8] = {"EMERG", "ALERT", "CRIT", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"};
|
||||||
|
const char* stringLType[LTENUMMAX] = {"user." , "local3." , "syslog."};
|
||||||
|
|
||||||
|
static int _dir_exist(const char *dpath)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (stat(dpath, &st))
|
||||||
|
return 0;
|
||||||
|
if (S_ISDIR(st.st_mode))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _create_dir(const char *dpath)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
char *command = malloc(strlen(dpath) + 10);
|
||||||
|
if (!command)
|
||||||
|
return -1;
|
||||||
|
sprintf(command, "mkdir -p %s", dpath);
|
||||||
|
return system(command);
|
||||||
|
#else
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 初始化KLogger核心结构体
|
||||||
|
*/
|
||||||
|
int initKLogger(int cid)
|
||||||
|
{
|
||||||
|
if (logger)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = (KLogger*)calloc(1 , sizeof(KLogger));
|
||||||
|
if (!logger)
|
||||||
|
{
|
||||||
|
printf("kdk_logger实例内存分配失败:%s\n" , strerror(errno));
|
||||||
|
return KDK_ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *identer , *stype , *otype , *specfile;
|
||||||
|
|
||||||
|
identer = kdk_conf_get_value(cid, "TYPE" , "identifier");
|
||||||
|
if (identer)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(identer , "user"))
|
||||||
|
logger->identer = LT_USER;
|
||||||
|
else if (!strcasecmp(identer , "local3"))
|
||||||
|
logger->identer = LT_LOCAL3;
|
||||||
|
else if (!strcasecmp(identer , "syslog"))
|
||||||
|
logger->identer = LT_SYSLOG;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger->identer = LT_SPEC;
|
||||||
|
strncpy(logger->specLogType , identer , KLOG_MAXPRIVALSIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger->identer = DEFAULT_LOGTYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
stype = kdk_conf_get_value(cid, "TYPE" , "synctype");
|
||||||
|
if (stype)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(stype , "async"))
|
||||||
|
logger->stype = ASYNC;
|
||||||
|
else if (!strcasecmp(stype , "sync"))
|
||||||
|
logger->stype = SYNC;
|
||||||
|
else
|
||||||
|
logger->stype = DEFAULT_SYNCTYPE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger->stype = DEFAULT_SYNCTYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
otype = kdk_conf_get_value(cid, "TYPE" , "output");
|
||||||
|
if (otype)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(otype , "syslog"))
|
||||||
|
logger->otype = OUT_SYSLOG;
|
||||||
|
else if (!strcasecmp(otype , "specfile"))
|
||||||
|
logger->otype = OUT_SPECFILE;
|
||||||
|
else if (!strcasecmp(otype , "stdout"))
|
||||||
|
logger->otype = OUT_STDOUT;
|
||||||
|
else
|
||||||
|
logger->otype = DEFAULT_OUTPUTTYPE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger->otype = DEFAULT_OUTPUTTYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger->levelBasedStorage = atoi(kdk_conf_get_value(cid, "CUSTOM" , "levelBasedStorage"));
|
||||||
|
if (logger->levelBasedStorage != 0)
|
||||||
|
logger->levelBasedStorage = 1;
|
||||||
|
|
||||||
|
logger->levelBasedContainHigherLevel = atoi(kdk_conf_get_value(cid, "CUSTOM" , "levelBasedContainHigherLevel"));
|
||||||
|
if (logger->levelBasedContainHigherLevel != 0)
|
||||||
|
logger->levelBasedContainHigherLevel = 1;
|
||||||
|
|
||||||
|
logger->level = atoi(kdk_conf_get_value(cid, "CUSTOM" , "logLevel"));
|
||||||
|
if (logger->level < KLOG_EMERG || logger->level > KLOG_TRACE)
|
||||||
|
logger->level = DEFAULT_LOGLEVEL;
|
||||||
|
|
||||||
|
logger->pid = getpid();
|
||||||
|
snprintf(logger->stringPID , 15 , "%d" , logger->pid);
|
||||||
|
char processPath[KLOG_MAXPATHLEN + 1] = {0};
|
||||||
|
if (readlink("/proc/self/exe" , processPath , KLOG_MAXPATHLEN) <= 0)
|
||||||
|
{
|
||||||
|
printf("无法读取可执行文件名:%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* pPName = strrchr(processPath , '/');
|
||||||
|
if (pPName)
|
||||||
|
strncpy(logger->processName , ++ pPName , KLOG_PROCESSNAME_LEN);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(logger->processName , "untagged");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger->mlock = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
|
||||||
|
if (!logger->mlock)
|
||||||
|
{
|
||||||
|
printf("kdk_logger实例锁初始化失败:%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(logger->mlock , NULL);
|
||||||
|
memset(logger->fp.classfiedfp , 0 , sizeof(FILE*) * 8);
|
||||||
|
|
||||||
|
if (logger->otype == OUT_SYSLOG)
|
||||||
|
printf("日志记录位置:SYSLOG\n");
|
||||||
|
else if (logger->otype == OUT_SPECFILE)
|
||||||
|
{
|
||||||
|
specfile = kdk_conf_get_value(cid, "CUSTOM" , "specName");
|
||||||
|
const char *dpath = kdk_conf_get_value(cid, "CUSTOM", "logDir");
|
||||||
|
if (dpath && strlen(dpath))
|
||||||
|
strcpy(logger->rootPath, dpath);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *hpath = getenv("HOME");
|
||||||
|
if (!hpath || strcmp(hpath, "/root") == 0)
|
||||||
|
strcpy(logger->rootPath, "/var/log");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(logger->rootPath, hpath);
|
||||||
|
strcat(logger->rootPath, "/.log");
|
||||||
|
if (!_dir_exist(logger->rootPath))
|
||||||
|
{
|
||||||
|
if (_create_dir(logger->rootPath))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!specfile || !strcmp(specfile , "")) //未指定名称,则使用进程名作为文件名称
|
||||||
|
{
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
sprintf(logger->logfileName.classfiedfileName[i] , "%s-%s.log" , logger->processName , stringLevel[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(logger->logfileName.commonlogfileName , KLOG_MAXPATHLEN , "%s.log" , logger->processName);
|
||||||
|
}
|
||||||
|
|
||||||
|
klog_rotate_init(cid, logger->processName, logger->rootPath);
|
||||||
|
}
|
||||||
|
else //使用指定的specName作为日志名称
|
||||||
|
{
|
||||||
|
const char* fName = strrchr(specfile , '/');
|
||||||
|
if (!fName)
|
||||||
|
{
|
||||||
|
fName = specfile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fName ++;
|
||||||
|
}
|
||||||
|
strcpy(logger->specName , fName); //将指定名称保存到结构体中
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
sprintf(logger->logfileName.classfiedfileName[i] , "%s-%s.log" , fName , stringLevel[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(logger->logfileName.commonlogfileName , KLOG_MAXPATHLEN , "%s.log" , fName);
|
||||||
|
}
|
||||||
|
klog_rotate_init(cid, logger->specName, logger->rootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
char logPath[(KLOG_MAXPATHLEN << 1) + 1];
|
||||||
|
for (int i = 0 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
snprintf(logPath, KLOG_MAXPATHLEN << 1, "%s/%s", logger->rootPath, logger->logfileName.classfiedfileName[i]);
|
||||||
|
logger->fp.classfiedfp[i] = fopen(logger->logfileName.classfiedfileName[i] , "at");
|
||||||
|
if (!logger->fp.classfiedfp[i])
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件%s:%s\n" , logPath, strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char logPath[(KLOG_MAXPATHLEN << 1) + 1];
|
||||||
|
snprintf(logPath, KLOG_MAXPATHLEN << 1, "%s/%s", logger->rootPath, logger->logfileName.commonlogfileName);
|
||||||
|
logger->fp.commonfp = fopen(logPath , "at");
|
||||||
|
if (!logger->fp.commonfp)
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件%s:%s\n" ,logPath, strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
printf("日志记录文件:%s\n" , logPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setRootDir(const char *dpath)
|
||||||
|
{
|
||||||
|
if (!logger || logger->otype != OUT_SPECFILE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!_dir_exist(dpath))
|
||||||
|
{
|
||||||
|
if (_create_dir(dpath))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(logger->rootPath, dpath, KLOG_MAXPATHLEN);
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
char logPath[KLOG_MAXPATHLEN * 2];
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
fclose(logger->fp.classfiedfp[i]);
|
||||||
|
sprintf(logPath, "%s/%s", logger->rootPath, logger->logfileName.classfiedfileName[i]);
|
||||||
|
logger->fp.classfiedfp[i] = fopen(logger->logfileName.classfiedfileName[i], "at");
|
||||||
|
if (!logger->fp.classfiedfp[i])
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件%s:%s\n", logPath, strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char logPath[KLOG_MAXPATHLEN * 2];
|
||||||
|
fclose(logger->fp.commonfp);
|
||||||
|
sprintf(logPath, "%s/%s", logger->rootPath, logger->logfileName.commonlogfileName);
|
||||||
|
logger->fp.commonfp = fopen(logPath, "at");
|
||||||
|
if (!logger->fp.commonfp)
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件%s:%s\n", logPath, strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("日志记录位置已修改:%s\n", logger->rootPath);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyKLogger()
|
||||||
|
{
|
||||||
|
if (logger)
|
||||||
|
{
|
||||||
|
MLOCK(logger->mlock);
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
if (logger->fp.classfiedfp[i])
|
||||||
|
{
|
||||||
|
fclose(logger->fp.classfiedfp[i]);
|
||||||
|
logger->fp.classfiedfp[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (logger->fp.commonfp)
|
||||||
|
{
|
||||||
|
fclose(logger->fp.commonfp);
|
||||||
|
logger->fp.commonfp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MUNLOCK(logger->mlock);
|
||||||
|
pthread_mutex_destroy(logger->mlock);
|
||||||
|
free(logger->mlock);
|
||||||
|
free(logger);
|
||||||
|
logger = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_autowrap(int autowrap)
|
||||||
|
{
|
||||||
|
if (! logger)
|
||||||
|
return;
|
||||||
|
MLOCK(logger->mlock);
|
||||||
|
logger->autowrap = autowrap ? 1 : 0;
|
||||||
|
MUNLOCK(logger->mlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_wrap(char *message)
|
||||||
|
{
|
||||||
|
if (logger->autowrap && logger->otype != OUT_SYSLOG)
|
||||||
|
strcat(message, "\n");
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
#ifndef __KLOG_CORE_H__
|
||||||
|
#define __KLOG_CORE_H__
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "libkylog.h"
|
||||||
|
|
||||||
|
#define KLOG_DEFAULT_CONFPATH "/etc/kylog.conf"
|
||||||
|
|
||||||
|
#define KLOG_DEFAULT_MSGFLUSHINTERVAL 100
|
||||||
|
#define KLOG_DEFAULT_MSGGROWTH 4096 //消息存储超阈值后单次增长率
|
||||||
|
#define KLOG_DEFAULT_MSGGROWTHRESHOLD 204800 //消息增长阈值
|
||||||
|
|
||||||
|
#define MLOCK(x) (pthread_mutex_lock(x))
|
||||||
|
#define MTRYLOCK(x) (pthread_mutex_trylock(x))
|
||||||
|
#define MUNLOCK(x) (pthread_mutex_unlock(x))
|
||||||
|
|
||||||
|
// #ifndef RELEASE
|
||||||
|
// #define OUTPUT(fmt , ...) printf("[%s:%d]"fmt , __FUNCTION__ , __LINE__ , ##__VA_ARGS__)
|
||||||
|
// #else
|
||||||
|
// #define OUTPUT(fmt, ...)
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
#define KLOG_MAXPATHLEN 1024 //文件路径最大长度
|
||||||
|
#define KLOG_MAXMSGSIZE 2048 //每条消息最大长度
|
||||||
|
#define KLOG_MAXPRIVALSIZE 128 //自定义内容最大长度
|
||||||
|
#define KLOG_PROCESSNAME_LEN 128 //可执行文件名最大长度
|
||||||
|
#define KLOG_MAXDATELEN 64 //日期最大长度
|
||||||
|
#define KLOG_CONF_GROUPSIZE 64 //配置文件Group最大长度
|
||||||
|
#define KLOG_CONF_KEYSIZE 64 //配置文件Key最大长度
|
||||||
|
#define KLOG_CONF_VALUESIZE KLOG_MAXPATHLEN //配置文件value最大长度
|
||||||
|
|
||||||
|
#define KLOG_OPT_FORMAT 0x0001
|
||||||
|
#define KLOG_OPT_LOGTYPE 0x0002
|
||||||
|
#define KLOG_OPT_SYNC 0x0003
|
||||||
|
#define KLOG_OPT_OUTPUT 0x0004
|
||||||
|
#define KLOG_OPT_SPECFILE 0x0005
|
||||||
|
#define KLOG_OPT_SPECCONTENT 0x0006
|
||||||
|
|
||||||
|
#ifndef CORE_DEFINE
|
||||||
|
#define CORE_DEFINE
|
||||||
|
enum logIdentifier{
|
||||||
|
LT_USER = 0,
|
||||||
|
LT_LOCAL3 = 1,
|
||||||
|
LT_SYSLOG = 2,
|
||||||
|
LT_SPEC,
|
||||||
|
LTENUMMAX
|
||||||
|
}; //日志类别
|
||||||
|
|
||||||
|
enum syncType{
|
||||||
|
ASYNC=1, // 异步模式,会较大程度提高日志写入速度,减小日志写入带来的IO等待时间,但有可能出现程序崩溃时,日志尚未写入的情况
|
||||||
|
SYNC, // 同步模式,每次的写入都会等待写入事件完成
|
||||||
|
STENUMMAX
|
||||||
|
}; //记录方式
|
||||||
|
|
||||||
|
enum outputType{
|
||||||
|
OUT_SYSLOG=0,
|
||||||
|
OUT_SPECFILE,
|
||||||
|
OUT_STDOUT,
|
||||||
|
OTENUMMAX
|
||||||
|
}; //日志输出位置
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct KLogger
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
FILE* commonfp;
|
||||||
|
FILE* classfiedfp[8];
|
||||||
|
}fp;
|
||||||
|
enum logIdentifier identer; // 日志输出的标识符
|
||||||
|
enum syncType stype; // 标明在spec模式下,刷入日志的模式,异步/同步
|
||||||
|
enum outputType otype; // 标明日志输出的位置,可以输出到syslog/指定文件/标准输出;目前syslog尚未验证
|
||||||
|
int levelBasedStorage; //按照等级分类存储
|
||||||
|
int levelBasedContainHigherLevel; //按等级分类存储时,低优先级日志是否需要包含高优先级日志(数字越小优先级越高)
|
||||||
|
int level; //日志记录等级
|
||||||
|
int autowrap; // 自动换行
|
||||||
|
pid_t pid;
|
||||||
|
char stringPID[16];
|
||||||
|
char rootPath[KLOG_MAXPATHLEN + 1];
|
||||||
|
char specName[KLOG_MAXPATHLEN + 1];
|
||||||
|
union
|
||||||
|
{
|
||||||
|
char commonlogfileName[KLOG_MAXPATHLEN + 1]; //自定义日志文件路径
|
||||||
|
char classfiedfileName[8][KLOG_MAXPATHLEN + 1];
|
||||||
|
}logfileName;
|
||||||
|
char processName[KLOG_PROCESSNAME_LEN + 1]; //可执行文件名称
|
||||||
|
char specLogType[KLOG_MAXPRIVALSIZE + 1]; //自定义记录类型
|
||||||
|
|
||||||
|
pthread_mutex_t* mlock;
|
||||||
|
}KLogger;
|
||||||
|
|
||||||
|
#define DEFAULT_LOGTYPE LT_LOCAL3
|
||||||
|
#define DEFAULT_OUTPUTTYPE OUT_SPECFILE
|
||||||
|
#define DEFAULT_SYNCTYPE SYNC
|
||||||
|
#define DEFAULT_LOGLEVEL KLOG_DEBUG
|
||||||
|
|
||||||
|
extern int initKLogger(int cid);
|
||||||
|
extern int setRootDir(const char* dpath) NOTNULL();
|
||||||
|
extern void destroyKLogger();
|
||||||
|
extern void set_autowrap(int autowrap);
|
||||||
|
extern void append_wrap(char *message);
|
||||||
|
|
||||||
|
extern KLogger* logger;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include "format.h"
|
||||||
|
#include "libkyconf.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
PrintFormat klog_printformat;
|
||||||
|
|
||||||
|
extern const char* stringLevel[8];
|
||||||
|
extern const char* stringLType[LTENUMMAX];
|
||||||
|
|
||||||
|
int getRecordDate(char* date) NOTNULL();
|
||||||
|
int formatMessage(int lvl , const char *filename , const char *func , int linenum , const char *message , char* result , unsigned int resultSize) NOTNULL();
|
||||||
|
|
||||||
|
int getRecordDate(char* date)
|
||||||
|
{
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
if (ctime_r(&now , date))
|
||||||
|
date[strlen(date) - 1] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFormatOptions(int id)
|
||||||
|
{
|
||||||
|
klog_printformat.vis_execName = atoi(kdk_conf_get_value(id, "FORMAT" , "f_processname")) ? true : false;
|
||||||
|
klog_printformat.vis_filename = atoi(kdk_conf_get_value(id, "FORMAT" , "f_filename")) ? true : false;
|
||||||
|
klog_printformat.vis_funcline = atoi(kdk_conf_get_value(id, "FORMAT" , "f_funcline")) ? true : false;
|
||||||
|
klog_printformat.vis_identifier = atoi(kdk_conf_get_value(id, "FORMAT" , "f_identifier")) ? true : false;
|
||||||
|
klog_printformat.vis_pid = atoi(kdk_conf_get_value(id, "FORMAT" , "f_pid")) ? true : false;
|
||||||
|
klog_printformat.vis_tid = atoi(kdk_conf_get_value(id, "FORMAT" , "f_tid")) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int formatMessage(int lvl , const char *filename , const char *func , int linenum , const char *message , char* result , unsigned int resultSize)
|
||||||
|
{
|
||||||
|
char buffer[KLOG_MAXMSGSIZE + 1] = {"["}; //TODO:这里开4097字节可能有越界风险
|
||||||
|
char* pos = buffer;
|
||||||
|
pos ++;
|
||||||
|
|
||||||
|
//[类型.等级]
|
||||||
|
if (klog_printformat.vis_identifier)
|
||||||
|
{
|
||||||
|
if (logger->identer != LT_SPEC)
|
||||||
|
{
|
||||||
|
memcpy(pos , stringLType[logger->identer] , strlen(stringLType[logger->identer]) * sizeof(char));
|
||||||
|
pos += strlen(stringLType[logger->identer]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(pos , logger->specLogType , strlen(logger->specLogType) * sizeof(char));
|
||||||
|
pos += strlen(logger->specLogType);
|
||||||
|
strcat(pos , ".");
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(pos , stringLevel[lvl] , strlen(stringLevel[lvl]) * sizeof(char));
|
||||||
|
pos += strlen(stringLevel[lvl]);
|
||||||
|
strcpy(pos , "] ");
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
//[日期]
|
||||||
|
strcpy(pos , "[");
|
||||||
|
pos += 1;
|
||||||
|
char nowtime[KLOG_MAXDATELEN + 1] = {0};
|
||||||
|
getRecordDate(nowtime);
|
||||||
|
memcpy(pos , nowtime , strlen(nowtime) * sizeof(char));
|
||||||
|
pos += strlen(nowtime);
|
||||||
|
strcpy(pos , "] ");
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
//[进程名:PID-TID]
|
||||||
|
if (klog_printformat.vis_execName || klog_printformat.vis_pid)
|
||||||
|
{
|
||||||
|
strcpy(pos , "[");
|
||||||
|
pos ++;
|
||||||
|
|
||||||
|
if (klog_printformat.vis_execName)
|
||||||
|
{
|
||||||
|
memcpy(pos , logger->processName , strlen(logger->processName) * sizeof(char));
|
||||||
|
pos += strlen(logger->processName);
|
||||||
|
}
|
||||||
|
if (klog_printformat.vis_pid)
|
||||||
|
{
|
||||||
|
if (__glibc_likely(klog_printformat.vis_execName))
|
||||||
|
{
|
||||||
|
strcpy(pos , ":");
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
memcpy(pos , logger->stringPID , strlen(logger->stringPID) * sizeof(char));
|
||||||
|
pos += strlen(logger->stringPID);
|
||||||
|
}
|
||||||
|
if (klog_printformat.vis_tid)
|
||||||
|
{
|
||||||
|
if (__glibc_likely(klog_printformat.vis_pid))
|
||||||
|
{
|
||||||
|
strcpy(pos , "-");
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
char tid[32] = {0};
|
||||||
|
sprintf(tid , "%lu" , pthread_self());
|
||||||
|
memmove(pos , tid , strlen(tid) * sizeof(char));
|
||||||
|
pos += strlen(tid);
|
||||||
|
}
|
||||||
|
strcpy(pos , "] ");
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:[自定义]
|
||||||
|
|
||||||
|
//[文件:函数-行号]
|
||||||
|
if (klog_printformat.vis_filename || klog_printformat.vis_funcline)
|
||||||
|
{
|
||||||
|
strcpy(pos , "[");
|
||||||
|
pos += 1;
|
||||||
|
if (klog_printformat.vis_filename)
|
||||||
|
{
|
||||||
|
memcpy(pos , filename , strlen(filename) * sizeof(char));
|
||||||
|
pos += strlen(filename);
|
||||||
|
}
|
||||||
|
if (klog_printformat.vis_funcline)
|
||||||
|
{
|
||||||
|
if (__glibc_likely(klog_printformat.vis_filename))
|
||||||
|
{
|
||||||
|
strcpy(pos , ":");
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
memcpy(pos , func , strlen(func) * sizeof(char));
|
||||||
|
pos += strlen(func);
|
||||||
|
char line[10] = {0};
|
||||||
|
snprintf(line , 9 , "-%d" , linenum);
|
||||||
|
memcpy(pos , line , strlen(line) * sizeof(char));
|
||||||
|
pos += strlen(line);
|
||||||
|
}
|
||||||
|
strcpy(pos , "] ");
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
size_t remainMsgSize = KLOG_MAXMSGSIZE - strlen(buffer);
|
||||||
|
size_t rawMsgSize = strlen(message) * sizeof(char);
|
||||||
|
memcpy(pos , message , rawMsgSize > remainMsgSize ? remainMsgSize : rawMsgSize);
|
||||||
|
memcpy(result , buffer , resultSize * sizeof(char));
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef __KLOG_FMT_H__
|
||||||
|
#define __KLOG_FMT_H__
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
typedef struct format
|
||||||
|
{
|
||||||
|
bool vis_identifier;
|
||||||
|
bool vis_execName;
|
||||||
|
bool vis_pid;
|
||||||
|
bool vis_tid;
|
||||||
|
bool vis_filename;
|
||||||
|
bool vis_funcline;
|
||||||
|
}PrintFormat;
|
||||||
|
|
||||||
|
extern PrintFormat klog_printformat;
|
||||||
|
|
||||||
|
#ifndef KLOG_FORMATSET
|
||||||
|
#define KLOG_FORMATSET
|
||||||
|
#define FORMAT_LOGTYPE 0x0001
|
||||||
|
#define FORMAT_PROCESSNAME 0x0002
|
||||||
|
#define FORMAT_PID 0x0004
|
||||||
|
#define FORMAT_FILENAME 0x0008
|
||||||
|
#define FORMAT_LINENUM 0x0010
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int getRecordDate(char* date) NOTNULL();
|
||||||
|
extern void loadFormatOptions();
|
||||||
|
extern int formatMessage(int lvl , const char *filename , const char *func , int linenum , const char *message , char* result , unsigned int resultSize) NOTNULL();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,59 @@
|
||||||
|
#include "klog_dump.h"
|
||||||
|
#include "libkyconf.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int klog_rotate_init(int cid, const char *name, const char *rootpath)
|
||||||
|
{
|
||||||
|
const char *ruler = kdk_conf_get_value(cid, "DUMP", "rules");
|
||||||
|
if (strcasecmp(ruler, "none") == 0)
|
||||||
|
return 0;
|
||||||
|
const char *oversize = kdk_conf_get_value(cid, "DUMP", "size");
|
||||||
|
int compress = atoi(kdk_conf_get_value(cid, "DUMP", "compress"));
|
||||||
|
|
||||||
|
char tmp[1025];
|
||||||
|
snprintf(tmp, 1024, "/etc/kysdk/kysdk-base/logrotate.d/%s", name);
|
||||||
|
FILE *fp = fopen(tmp, "wt+");
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
snprintf(tmp, 1024, "%s/%s* {\n", rootpath, name);
|
||||||
|
fputs(tmp, fp);
|
||||||
|
if (strcasecmp(ruler, "daily") == 0)
|
||||||
|
fputs("\tdaily\n", fp);
|
||||||
|
else if (strcasecmp(ruler, "weekly") == 0)
|
||||||
|
fputs("\tweekly\n", fp);
|
||||||
|
else
|
||||||
|
fputs("\tmonthly\n", fp);
|
||||||
|
|
||||||
|
fputs("\trotate 7\n", fp);
|
||||||
|
fputs("\tnotifempty\n", fp);
|
||||||
|
fputs("\tnocopytruncate\n", fp);
|
||||||
|
if (compress)
|
||||||
|
fputs("\tcompress\n", fp);
|
||||||
|
else
|
||||||
|
fputs("\tnocompress\n", fp);
|
||||||
|
|
||||||
|
if (strcasecmp(ruler, "size") == 0)
|
||||||
|
{
|
||||||
|
int size = atoi(oversize);
|
||||||
|
snprintf(tmp, 1024, "\tsize %d", size);
|
||||||
|
char *p = oversize;
|
||||||
|
while (*p && isdigit(p))
|
||||||
|
p ++;
|
||||||
|
if (strncasecmp(p, "M", 1) == 0)
|
||||||
|
strcat(tmp, "M");
|
||||||
|
else if (strncasecmp(p, "K", 1) == 0)
|
||||||
|
strcat(tmp, "k");
|
||||||
|
else if (strncasecmp(p, "G", 1) == 0)
|
||||||
|
strcat(tmp, "G");
|
||||||
|
strcat(tmp, "\n");
|
||||||
|
fputs(tmp, fp);
|
||||||
|
}
|
||||||
|
fputs("}", fp);
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef KDK_BASE_LOG_DUMP_H__
|
||||||
|
#define KDK_BASE_LOG_DUMP_H__
|
||||||
|
|
||||||
|
extern int klog_rotate_init(int cid, const char *name, const char *rootpath);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,266 @@
|
||||||
|
#include "klog_mqueue.h"
|
||||||
|
#include "writeFile.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
int initMessageQueue(int flushInterval , int autoIncrement);
|
||||||
|
int insertMessage(int lvl , const char *message);
|
||||||
|
int flushMessageQueue(int locked);
|
||||||
|
void emptyMessageQueue();
|
||||||
|
void destroyMessageQueue();
|
||||||
|
void* startMQDaemon(void* msec);
|
||||||
|
void recycle() DESTRUCTOR;
|
||||||
|
|
||||||
|
static KLMessageQueue* pMQ;
|
||||||
|
|
||||||
|
int initMessageQueue(int flushInterval , int autoIncrement)
|
||||||
|
{
|
||||||
|
if (pMQ)
|
||||||
|
return 0;
|
||||||
|
pMQ = (KLMessageQueue*)calloc(1 , sizeof(KLMessageQueue));
|
||||||
|
if (!pMQ)
|
||||||
|
{
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
pMQ->interval = flushInterval > 0 ? flushInterval : KLOG_DEFAULT_MSGFLUSHINTERVAL;
|
||||||
|
pMQ->autoIncrementQueueSize = autoIncrement != 0 ? 1 : 0;
|
||||||
|
|
||||||
|
pMQ->mnum = 0;
|
||||||
|
pMQ->mlock = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
|
||||||
|
if (!pMQ->mlock)
|
||||||
|
{
|
||||||
|
printf("消息队列锁分配失败!%s" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(pMQ->mlock , NULL);
|
||||||
|
|
||||||
|
pMQ->message = (KL_MessageNode**)calloc(1 , sizeof(KL_MessageNode*) * KLOG_DEFAULT_MSGGROWTH);
|
||||||
|
if (!pMQ->message)
|
||||||
|
{
|
||||||
|
printf("消息缓冲内存申请失败:%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
pMQ->maxmessage = KLOG_DEFAULT_MSGGROWTH;
|
||||||
|
|
||||||
|
//创建异步刷新线程
|
||||||
|
pMQ->thread_id = 0;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
if (pthread_create(&pMQ->thread_id , &attr , startMQDaemon , &pMQ->interval))
|
||||||
|
{
|
||||||
|
printf("异步刷新线程创建失败:%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int insertMessage(int lvl , const char *message)
|
||||||
|
{
|
||||||
|
if (!pMQ && initMessageQueue(0 , 0))
|
||||||
|
return errno;
|
||||||
|
int retv = 0;
|
||||||
|
|
||||||
|
//构造MessageNode
|
||||||
|
KL_MessageNode *node = (KL_MessageNode*)calloc(1 , sizeof(KL_MessageNode));
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
retv = errno;
|
||||||
|
goto clean_up;
|
||||||
|
}
|
||||||
|
node->lvl = lvl;
|
||||||
|
node->bufSize = strlen(message);
|
||||||
|
node->bufSize = node->bufSize > sizeof(node->buf) ? sizeof(node->buf) : node->bufSize;
|
||||||
|
memcpy(node->buf , message , node->bufSize * sizeof(char));
|
||||||
|
|
||||||
|
//插入消息队列
|
||||||
|
MLOCK(pMQ->mlock);
|
||||||
|
if (__glibc_unlikely(pMQ->mnum >= pMQ->maxmessage)) //当前消息缓冲已满,扩充缓冲区
|
||||||
|
{
|
||||||
|
// OUTPUT("缓冲区满\n");
|
||||||
|
if (pMQ->autoIncrementQueueSize)
|
||||||
|
{
|
||||||
|
KL_MessageNode** old = pMQ->message;
|
||||||
|
unsigned long nextsize = pMQ->maxmessage;
|
||||||
|
if (nextsize >= KLOG_DEFAULT_MSGGROWTHRESHOLD) //若已达到指定阈值,则降低为缓慢扩充模式
|
||||||
|
nextsize += KLOG_DEFAULT_MSGGROWTH;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextsize <<= 1; //以乘二的速率扩充
|
||||||
|
}
|
||||||
|
|
||||||
|
pMQ->message = (KL_MessageNode**)realloc(pMQ->message , sizeof(KL_MessageNode*) * nextsize);
|
||||||
|
if (!pMQ->message)
|
||||||
|
{
|
||||||
|
char errmsg[1024] = {0};
|
||||||
|
sprintf(errmsg , "[SYSTEM.emerg]消息队列缓冲扩充失败:%s\n" , strerror(errno));
|
||||||
|
// OUTPUT("[SYSTEM.emerg]消息队列缓冲扩充失败:%s\n" , strerror(errno));
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
fwrite(errmsg , sizeof(char) , strlen(errmsg) , logger->fp.classfiedfp[KLOG_EMERG]);
|
||||||
|
fflush(logger->fp.classfiedfp[KLOG_EMERG]);
|
||||||
|
if (logger->levelBasedContainHigherLevel)
|
||||||
|
{
|
||||||
|
for (int i = KLOG_EMERG + 1 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
fwrite(errmsg , sizeof(char) , strlen(errmsg) , logger->fp.classfiedfp[i]);
|
||||||
|
fflush(logger->fp.classfiedfp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwrite(errmsg , sizeof(char) , strlen(errmsg) , logger->fp.commonfp);
|
||||||
|
fflush(logger->fp.commonfp);
|
||||||
|
}
|
||||||
|
pMQ->message = old;
|
||||||
|
flushMessageQueue(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pMQ->maxmessage = nextsize;
|
||||||
|
// OUTPUT("扩充缓冲至%lu\n" , pMQ->maxmessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// OUTPUT("缓冲区满,正在刷新缓冲区\n");
|
||||||
|
flushMessageQueue(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pMQ->message[pMQ->mnum] = node;
|
||||||
|
pMQ->mnum ++;
|
||||||
|
|
||||||
|
MUNLOCK(pMQ->mlock);
|
||||||
|
|
||||||
|
clean_up:
|
||||||
|
return retv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int flushMessageQueue(int locked)
|
||||||
|
{
|
||||||
|
if (!pMQ && initMessageQueue(0 , 0))
|
||||||
|
return errno;
|
||||||
|
if (!locked)
|
||||||
|
MLOCK(pMQ->mlock);
|
||||||
|
unsigned long msgcounts = pMQ->mnum;
|
||||||
|
if (pMQ->mnum == 0)
|
||||||
|
{
|
||||||
|
if (!locked)
|
||||||
|
MUNLOCK(pMQ->mlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//将整个链表内存拷贝到另一个位置,然后清空原链表
|
||||||
|
KL_MessageNode** list = pMQ->message;
|
||||||
|
if (pMQ->autoIncrementQueueSize)
|
||||||
|
{
|
||||||
|
pMQ->maxmessage >>= 1; //折半重新开始扩充
|
||||||
|
pMQ->message = (KL_MessageNode**)calloc(1 , sizeof(KL_MessageNode*) * pMQ->maxmessage);
|
||||||
|
if (!pMQ->message)
|
||||||
|
{
|
||||||
|
char errmsg[1024] = {0};
|
||||||
|
sprintf(errmsg , "[SYSTEM.emerg]消息队列缩减失败:%s\n" , strerror(errno));
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
fwrite(errmsg , sizeof(char) , strlen(errmsg) , logger->fp.classfiedfp[KLOG_EMERG]);
|
||||||
|
fflush(logger->fp.classfiedfp[KLOG_EMERG]);
|
||||||
|
if (logger->levelBasedContainHigherLevel)
|
||||||
|
{
|
||||||
|
for (int i = KLOG_EMERG + 1 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
fwrite(errmsg , sizeof(char) , strlen(errmsg) , logger->fp.classfiedfp[i]);
|
||||||
|
fflush(logger->fp.classfiedfp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwrite(errmsg , sizeof(char) , strlen(errmsg) , logger->fp.commonfp);
|
||||||
|
fflush(logger->fp.commonfp);
|
||||||
|
}
|
||||||
|
pMQ->maxmessage = 0;
|
||||||
|
}
|
||||||
|
// OUTPUT("消息队列缓冲重置为%lu\n" , pMQ->maxmessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pMQ->message = (KL_MessageNode**)calloc(1 , sizeof(KL_MessageNode*) * pMQ->maxmessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
pMQ->mnum = 0;
|
||||||
|
if (!locked)
|
||||||
|
MUNLOCK(pMQ->mlock);
|
||||||
|
//处理拷贝后的内容,逐条写入
|
||||||
|
KL_MessageNode* node = NULL;
|
||||||
|
for (unsigned long i = 0 ; i < msgcounts ; i ++)
|
||||||
|
{
|
||||||
|
node = list[i];
|
||||||
|
if (writeFile(node->lvl , node->buf , node->bufSize))
|
||||||
|
insertMessage(node->lvl , node->buf); //写入不成功的,重新插入队列
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
free(list);
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < 8 ; i ++)
|
||||||
|
fflush(logger->fp.classfiedfp[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fflush(logger->fp.commonfp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emptyMessageQueue()
|
||||||
|
{
|
||||||
|
if (!pMQ)
|
||||||
|
return;
|
||||||
|
MLOCK(pMQ->mlock);
|
||||||
|
for (unsigned long i = 0 ; i < pMQ->mnum ; i ++)
|
||||||
|
{
|
||||||
|
free(pMQ->message[i]);
|
||||||
|
pMQ->message[i] = NULL;
|
||||||
|
}
|
||||||
|
pMQ->mnum = 0;
|
||||||
|
MUNLOCK(pMQ->mlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyMessageQueue()
|
||||||
|
{
|
||||||
|
if (!pMQ)
|
||||||
|
return;
|
||||||
|
pthread_cancel(pMQ->thread_id);
|
||||||
|
pthread_join(pMQ->thread_id ,NULL);
|
||||||
|
while (pMQ->mnum)
|
||||||
|
{
|
||||||
|
flushMessageQueue(0);
|
||||||
|
}
|
||||||
|
pthread_mutex_destroy(pMQ->mlock);
|
||||||
|
free(pMQ->mlock);
|
||||||
|
free(pMQ);
|
||||||
|
pMQ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void recycle()
|
||||||
|
{
|
||||||
|
destroyMessageQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* startMQDaemon(void* msec)
|
||||||
|
{
|
||||||
|
int interval = *(int*)msec;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
|
||||||
|
pthread_testcancel();
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE , NULL);
|
||||||
|
usleep(interval * 1000);
|
||||||
|
flushMessageQueue(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef __KLOG_MQ_H__
|
||||||
|
#define __KLOG_MQ_H__
|
||||||
|
#include "core.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct _KL_MessageNode{
|
||||||
|
int lvl;
|
||||||
|
unsigned int bufSize;
|
||||||
|
char buf[KLOG_MAXMSGSIZE];
|
||||||
|
}KL_MessageNode;
|
||||||
|
|
||||||
|
typedef struct _KL_MessageQueue{
|
||||||
|
pthread_mutex_t* mlock; //用于在操作队列时锁定
|
||||||
|
int autoIncrementQueueSize; //是否自动扩充消息队列
|
||||||
|
unsigned long maxmessage; //当前最大可接受消息数量,该数量超限后会以每次KLOG_DEFAULT_MSGGROWTH的数量扩充
|
||||||
|
unsigned long mnum; //队列中消息的数量
|
||||||
|
pthread_t thread_id; //后台刷新线程ID
|
||||||
|
int interval; //刷新间隔
|
||||||
|
KL_MessageNode** message; //消息缓冲
|
||||||
|
}KLMessageQueue;
|
||||||
|
|
||||||
|
extern int initMessageQueue(int flushInterval , int autoIncrement);
|
||||||
|
extern int insertMessage(int lvl , const char *message);
|
||||||
|
extern int flushMessageQueue(int locked);
|
||||||
|
extern void emptyMessageQueue();
|
||||||
|
extern void destroyMessageQueue();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
[TYPE]
|
||||||
|
#日志标识,有user、Local3等类型,也可以自定义,但自定义只能使用specfile的输出模式
|
||||||
|
identifier=user
|
||||||
|
#写入方式,有SYNC和ASYNC两种
|
||||||
|
synctype=sync
|
||||||
|
#日志输出位置,有syslog、specfile、stdout三种,目前syslog还未支持
|
||||||
|
output=specfile
|
||||||
|
|
||||||
|
[CUSTOM]
|
||||||
|
logLevel=7
|
||||||
|
#当日志输出位置为specfile时,可以指定输出名称,若不指定则使用“进程名.log”作为日志名称。日志均保存在~/.log下
|
||||||
|
specName=
|
||||||
|
#日志按照等级分类存储
|
||||||
|
levelBasedStorage=0
|
||||||
|
#当按照等级分类存储时,低优先级日志是否需要包含高优先级日志(logLevel数字越小优先级越高)。这会显著的降低写入效率
|
||||||
|
levelBasedContainHigherLevel=1
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
#输出中是否包含日志类型
|
||||||
|
f_identifier=1
|
||||||
|
#输出中是否包含进程名称
|
||||||
|
f_processname=0
|
||||||
|
#输出中是否包含PID号
|
||||||
|
f_pid=1
|
||||||
|
#输出中是否包含TID号
|
||||||
|
f_tid=0
|
||||||
|
#输出中是否包含日志所在的源码文件名称
|
||||||
|
f_filename=0
|
||||||
|
#输出中是否包含日志所在的函数与行信息
|
||||||
|
f_funcline=0
|
||||||
|
|
||||||
|
[MSGQUEUE]
|
||||||
|
#异步模式下的消息刷新频率,单位毫秒
|
||||||
|
flushInterval=100
|
||||||
|
#是否自动扩充消息队列(若写入速率极高,自动扩充会占用大量内存)
|
||||||
|
autoIncrementQueueSize=0
|
||||||
|
|
||||||
|
[DUMP]
|
||||||
|
#转储规则,取值有daily(每日转储),weekly(每周转储),size(按大小转储),none(不转储)
|
||||||
|
rules=daily
|
||||||
|
#当转储规则为按大小转储时,转储阈值设置。支持GB、MB、KB的写法,若不加后缀则默认是以B为单位
|
||||||
|
thresholdAsSizeRules=1GB
|
||||||
|
#转储后是否需要压缩,1表示需要压缩
|
||||||
|
compress=1
|
|
@ -0,0 +1 @@
|
||||||
|
include /etc/kysdk/kysdk-base/logrotate.d
|
|
@ -0,0 +1,87 @@
|
||||||
|
#include "libkylog.h"
|
||||||
|
#include "klog_mqueue.h"
|
||||||
|
#include "write.h"
|
||||||
|
#include "format.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include <libkyconf.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int kdk_logger_init(const char *ini)
|
||||||
|
{
|
||||||
|
int retv = 0;
|
||||||
|
if (!ini)
|
||||||
|
{
|
||||||
|
ini = "/etc/kysdk/kysdk-base/kylog-default.conf";
|
||||||
|
}
|
||||||
|
int confid = kdk_conf_init(ini);
|
||||||
|
if (confid <= 0)
|
||||||
|
{
|
||||||
|
printf("%s配置文件加载失败\n", ini);
|
||||||
|
retv = -1;
|
||||||
|
goto clean_up;
|
||||||
|
}
|
||||||
|
loadFormatOptions(confid);
|
||||||
|
|
||||||
|
retv = initKLogger(confid);
|
||||||
|
if (retv)
|
||||||
|
{
|
||||||
|
printf("kdk_logger初始化失败!%d\n" , retv);
|
||||||
|
goto clean_up;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger->otype == OUT_SPECFILE && logger->stype == ASYNC)
|
||||||
|
{
|
||||||
|
retv = initMessageQueue(atoi(kdk_conf_get_value(confid, "MSGQUEUE" , "flushInterval")) , atoi(kdk_conf_get_value(confid, "MSGQUEUE" , "autoIncrementQueueSize")));
|
||||||
|
if (retv)
|
||||||
|
{
|
||||||
|
printf("消息队列初始化失败!%d\n" , retv);
|
||||||
|
goto clean_up;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_up:
|
||||||
|
return retv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdk_logger_setdir(const char* dpath)
|
||||||
|
{
|
||||||
|
return setRootDir(dpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdk_logger_write(int lvl , const char *filename , const char *func , int linenum , const char *fmt , ...)
|
||||||
|
{
|
||||||
|
if (!logger && kdk_logger_init(NULL))
|
||||||
|
{
|
||||||
|
printf("kdk_logger尚未初始化\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (lvl > logger->level)
|
||||||
|
return 0;
|
||||||
|
char message[KLOG_MAXMSGSIZE + 1] = {0};
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl , fmt);
|
||||||
|
vsnprintf(message , sizeof(char) * KLOG_MAXMSGSIZE , fmt , vl);
|
||||||
|
va_end(vl);
|
||||||
|
append_wrap(message);
|
||||||
|
return writeLog(lvl , filename , func , linenum , message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_logger_flush()
|
||||||
|
{
|
||||||
|
if (!logger)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (logger->stype == ASYNC)
|
||||||
|
flushMessageQueue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_logger_set_autowrap(int autowrap)
|
||||||
|
{
|
||||||
|
set_autowrap(autowrap);
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
#ifndef KYSDK_BASE_LOG_H__
|
||||||
|
#define KYSDK_BASE_LOG_H__
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup 日志模块
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file libkylog.h
|
||||||
|
* @author liuyunhe (liuyunhe@kylinos.cn)
|
||||||
|
* @brief KYSDK日志模块
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2021-10-28
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sdkmarcos.h>
|
||||||
|
|
||||||
|
#define KLOG_TRACE 8
|
||||||
|
#define KLOG_DEBUG 7
|
||||||
|
#define KLOG_INFO 6
|
||||||
|
#define KLOG_NOTICE 5
|
||||||
|
#define KLOG_WARNING 4
|
||||||
|
#define KLOG_ERROR 3
|
||||||
|
#define KLOG_CRIT 2
|
||||||
|
#define KLOG_ALERT 1
|
||||||
|
#define KLOG_EMERG 0
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在调用的位置自动输出一串日志,表明主调方函数被调用
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_trace(fmt , ...) (klog(KLOG_TRACE , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出debug级别日志,不会自动添加换行符
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_debug(fmt , ...) (klog(KLOG_DEBUG , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出info级别日志,不会自动添加换行符
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_info(fmt , ...) (klog(KLOG_INFO , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出notice级别日志,不会自动添加换行符
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_notice(fmt , ...) (klog(KLOG_NOTICE , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出warning级别日志,不会自动添加换行符
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_warning(fmt , ...) (klog(KLOG_WARNING , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出error级别日志,不会自动添加换行符。
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_err(fmt , ...) (klog(KLOG_ERROR , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出crit级别日志,不会自动添加换行符。该等级日志说明程序发生了重大问题,需要立即修复。
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_crit(fmt , ...) (klog(KLOG_CRIT , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出alert级别日志,不会自动添加换行符。该等级日志说明系统发生了极为严重的错误,需要立即修复。
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_alert(fmt , ...) (klog(KLOG_ALERT , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 输出emerg级别日志,不会自动添加换行符。该等级日志说明系统发生了无法挽回的故障,必须立即停止。
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog_emerg(fmt , ...) (klog(KLOG_EMERG , fmt , ##__VA_ARGS__))
|
||||||
|
|
||||||
|
#ifndef NOCALLTRACE
|
||||||
|
#define klog_calltrace() (klog_trace("[CALLTRACE]Enter %s\n" , __FUNCTION__))
|
||||||
|
#else
|
||||||
|
#define klog_calltrace()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 内部使用函数,不应在外部被调用
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define klog(lvl , fmt , ...) kdk_logger_write(lvl , __FILE__ , __FUNCTION__ , __LINE__ , fmt , ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化日志记录,也可以不调用该函数直接使用上方日志记录的宏,若以此方式运行,则程序会使用默认的日志配置文件
|
||||||
|
*
|
||||||
|
* @param ini:日志配置文件路径,若传入NULL则会使用默认的日志配置文件
|
||||||
|
* @return int,0表示成功,非0表示失败
|
||||||
|
*/
|
||||||
|
extern int kdk_logger_init(const char *ini);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在异步写入的方式下,可以调用该函数手动将缓存区中的日志写入文件中
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern void kdk_logger_flush() DESTRUCTOR; //当使用异步日志记录方式时,该函数可以手动将日志刷新到文件中
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 内部使用函数,不应在外部被调用
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern int kdk_logger_write(int lvl , const char *filename , const char *func , int linenum , const char *fmt , ...) NOTNULL() CHECK_FMT(5 , 6);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置日志的存储目录,若不设置,非root程序会被记录在~/.log下,root程序会被记录在/var/log下
|
||||||
|
*
|
||||||
|
* @param dpath
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
extern int kdk_logger_setdir(const char* dpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置日志输出自动换行
|
||||||
|
*
|
||||||
|
* @param autowarp 1表示启用启动换行,0表示禁止自动换行
|
||||||
|
*/
|
||||||
|
extern void kdk_logger_set_autowrap(int autowarp);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \example kysdk-base/src/log/test/test-log.c
|
||||||
|
* \example kysdk-base/src/log/test/test-pressure.c
|
||||||
|
* \example kysdk-base/src/log/test/test-setdir.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
|
@ -0,0 +1,2 @@
|
||||||
|
00 0 * * * /usr/bin/logrotate -s /tmp/kylog-rotate-tmp /etc/kysdk/kysdk-base/kylog-rotate-default
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
all:
|
||||||
|
gcc -o test-log test-log.c -g -O0 -I../ -I../../utils/ -L../lib -L../../config/lib/ -lkylog -lkyconf -lpthread -lsystemd -Wl,-rpath=../lib:../../config/lib/
|
||||||
|
gcc -o test-setdir test-setdir.c -g -O0 -I../ -I../../utils/ -L../lib -L../../config/lib/ -lkylog -lkyconf -lpthread -lsystemd -Wl,-rpath=../lib:../../config/lib/
|
||||||
|
gcc -o test-pressure test-pressure.c -g -O0 -I../ -I../../utils/ -L../lib -L../../config/lib/ -lkylog -lkyconf -lpthread -lsystemd -Wl,-rpath=../lib:../../config/lib/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm test-log test-setdir test-pressure
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function testmiss ()
|
||||||
|
{
|
||||||
|
let count=1
|
||||||
|
oldIFS=${IFS}
|
||||||
|
IFS=$'\n'
|
||||||
|
let missline=0
|
||||||
|
let flag=0
|
||||||
|
for line in `cat $1`;do
|
||||||
|
value=`echo ${line} | awk '{print $NF}'`
|
||||||
|
while [ ${value} -ne ${count} ];do
|
||||||
|
echo "missing ${count}" >> /tmp/logtest-missing-${TID[${index} - 1]}.rec
|
||||||
|
echo "TID - ${TID[${index} - 1]} : ${count} missmatch with ${count}"
|
||||||
|
((count ++))
|
||||||
|
((missline ++))
|
||||||
|
done
|
||||||
|
if [ $flag -eq 0 ];then
|
||||||
|
echo ""
|
||||||
|
((flag ++))
|
||||||
|
fi
|
||||||
|
printf "\rTID - ${TID[${index} - 1]} : ${count} matched."
|
||||||
|
((count ++))
|
||||||
|
done
|
||||||
|
return missline
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGFILE=/home/kylin/.log/test-pressure.log
|
||||||
|
|
||||||
|
TID=(`awk -F '[]-]' '{print $5}' ${LOGFILE} | sort -n | uniq`)
|
||||||
|
|
||||||
|
oldIFS=${IFS}
|
||||||
|
|
||||||
|
for index in `seq ${#TID[@]}`;do
|
||||||
|
grep ${TID[${index} - 1]} ${LOGFILE} > /tmp/logtest-${TID[${index} - 1]}.log
|
||||||
|
testmiss "/tmp/logtest-${TID[${index} - 1]}.log" &
|
||||||
|
done
|
||||||
|
|
||||||
|
wait
|
|
@ -0,0 +1,44 @@
|
||||||
|
[TYPE]
|
||||||
|
#日志标识,有User、Local3等类型,也可以自定义字符串,但自定义只能使用specfile的输出模式
|
||||||
|
identifier=test
|
||||||
|
#写入方式,有SYNC和ASYNC两种
|
||||||
|
synctype=async
|
||||||
|
#日志输出位置,有syslog、specfile、stdout三种
|
||||||
|
output=specfile
|
||||||
|
|
||||||
|
[CUSTOM]
|
||||||
|
logLevel=7
|
||||||
|
#当日志输出位置为specfile时,可以指定输出名称,若不指定则使用“进程名.log”作为日志名称。日志均保存在/var/log/下
|
||||||
|
specName=
|
||||||
|
#日志按照等级分类存储
|
||||||
|
levelBasedStorage=0
|
||||||
|
#当按照等级分类存储时,低优先级日志是否需要包含高优先级日志(logLevel数字越小优先级越高)。这会显著的降低写入效率
|
||||||
|
levelBasedContainHigherLevel=1
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
#输出中是否包含日志类型
|
||||||
|
f_identifier=1
|
||||||
|
#输出中是否包含进程名称
|
||||||
|
f_processname=1
|
||||||
|
#输出中是否包含PID号
|
||||||
|
f_pid=1
|
||||||
|
#输出中是否包含TID号
|
||||||
|
f_tid=1
|
||||||
|
#输出中是否包含日志所在的源码文件名称
|
||||||
|
f_filename=1
|
||||||
|
#输出中是否包含日志所在的函数与行信息
|
||||||
|
f_funcline=1
|
||||||
|
|
||||||
|
[MSGQUEUE]
|
||||||
|
#异步模式下的消息刷新频率,单位毫秒
|
||||||
|
flushInterval=100
|
||||||
|
#是否自动扩充消息队列(若写入速率极高,自动扩充会占用大量内存)
|
||||||
|
autoIncrementQueueSize=0
|
||||||
|
|
||||||
|
[DUMP]
|
||||||
|
#转储规则,取值有daily(每日转储),size(按大小转储)
|
||||||
|
rules=daily
|
||||||
|
#当转储规则为按大小转储时,转储阈值设置。支持GB、MB、KB的写法,若不加后缀则默认是以B为单位
|
||||||
|
thresholdAsSizeRules=1GB
|
||||||
|
#转储后是否需要压缩,1表示需要压缩
|
||||||
|
compress=1
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include <libkylog.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main(int argc , char** argv)
|
||||||
|
{
|
||||||
|
assert(kdk_logger_init("./logtest.conf") == 0);
|
||||||
|
kdk_logger_set_autowrap(1);
|
||||||
|
klog_calltrace();
|
||||||
|
klog_debug("Debug");
|
||||||
|
klog_info("Info");
|
||||||
|
klog_notice("Notice");
|
||||||
|
klog_warning("Warning");
|
||||||
|
klog_err("Error");
|
||||||
|
klog_crit("Crit");
|
||||||
|
klog_alert("Alert");
|
||||||
|
klog_emerg("Emerg");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include <libkylog.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main(int argc , char** argv)
|
||||||
|
{
|
||||||
|
assert(kdk_logger_init("./logtest.conf") == 0);
|
||||||
|
|
||||||
|
klog_calltrace();
|
||||||
|
klog_debug("Debug\n");
|
||||||
|
klog_info("Info\n");
|
||||||
|
klog_notice("Notice\n");
|
||||||
|
klog_warning("Warning\n");
|
||||||
|
klog_err("Error\n");
|
||||||
|
klog_crit("Crit\n");
|
||||||
|
klog_alert("Alert\n");
|
||||||
|
klog_emerg("Emerg\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <libkylog.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int stop = 0;
|
||||||
|
int start = 0;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
unsigned int counts = 0;
|
||||||
|
|
||||||
|
void stopHandler(int sig)
|
||||||
|
{
|
||||||
|
stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startTimer()
|
||||||
|
{
|
||||||
|
alarm(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* startlog(void* arg)
|
||||||
|
{
|
||||||
|
while (!start);
|
||||||
|
unsigned int count = 0;
|
||||||
|
srand(time(NULL));
|
||||||
|
while (!stop)
|
||||||
|
{
|
||||||
|
int lvl = rand() % 8;
|
||||||
|
switch (lvl)
|
||||||
|
{
|
||||||
|
case 0 :{
|
||||||
|
assert(klog_emerg("this is emerg count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 1 : {
|
||||||
|
assert(klog_alert("this is alert count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 2:{
|
||||||
|
assert(klog_crit("this is crit count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 3:{
|
||||||
|
assert(klog_err("this is err count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 4:{
|
||||||
|
assert(klog_warning("this is warning count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 5:{
|
||||||
|
assert(klog_notice("this is notice count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 6:{
|
||||||
|
assert(klog_info("this is info count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
case 7:{
|
||||||
|
assert(klog_debug("this is debug count %d\n" , ++ count) == 0);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
counts += count;
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
printf("共写入%d条日志\n" , count);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc , char** argv)
|
||||||
|
{
|
||||||
|
assert(kdk_logger_init("./logtest.conf") == 0);
|
||||||
|
|
||||||
|
// //越界测试
|
||||||
|
// char msg[4096];
|
||||||
|
// memset(msg , 'F' , sizeof(char) * 4096);
|
||||||
|
// klog_debug(msg);
|
||||||
|
|
||||||
|
//压力测试
|
||||||
|
signal(SIGALRM, stopHandler);
|
||||||
|
pthread_mutex_init(&lock, NULL);
|
||||||
|
pthread_t children[7] = {0};
|
||||||
|
for (int i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
pthread_create(&children[i], NULL, startlog, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = 1;
|
||||||
|
|
||||||
|
startTimer();
|
||||||
|
for (int i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
pthread_join(children[i], NULL);
|
||||||
|
}
|
||||||
|
kdk_logger_flush();
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
printf("所有线程写入总量:%u\n", counts);
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
return system("cat /var/log/logtest.log | wc -l");
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <libkylog.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main(int argc , char** argv)
|
||||||
|
{
|
||||||
|
assert(kdk_logger_init("./logtest.conf") == 0);
|
||||||
|
|
||||||
|
assert(kdk_logger_setdir("/tmp") == 0);
|
||||||
|
|
||||||
|
klog_calltrace();
|
||||||
|
klog_debug("Debug\n");
|
||||||
|
klog_info("Info\n");
|
||||||
|
klog_notice("Notice\n");
|
||||||
|
klog_warning("Warning\n");
|
||||||
|
klog_err("Error\n");
|
||||||
|
klog_crit("Crit\n");
|
||||||
|
klog_alert("Alert\n");
|
||||||
|
klog_emerg("Emerg\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "write.h"
|
||||||
|
#include "klog_mqueue.h"
|
||||||
|
#include "writeFile.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <systemd/sd-journal.h>
|
||||||
|
|
||||||
|
static inline void writeSyslog(int lvl , const char *message)
|
||||||
|
{
|
||||||
|
#ifndef __loongarch__
|
||||||
|
sd_journal_send("MESSAGE=%s", message , "PRIORITY=%i" , lvl ,\
|
||||||
|
"SYSLOG_FACILITY=%i" , 19 , NULL);
|
||||||
|
#else
|
||||||
|
// TODO: loongarch64 's syslog write
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int writeSpecFile(int lvl , const char *filename , const char *func , int linenum , const char *message)
|
||||||
|
{
|
||||||
|
char buffer[KLOG_MAXMSGSIZE + 1] = {0};
|
||||||
|
formatMessage(lvl , filename , func , linenum , message , buffer , KLOG_MAXMSGSIZE);
|
||||||
|
if (logger->stype == SYNC)
|
||||||
|
{
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
return writeFile(lvl , buffer , strlen(buffer)) || fflush(logger->fp.classfiedfp[lvl]);
|
||||||
|
else
|
||||||
|
return writeFile(lvl , buffer , strlen(buffer)) || fflush(logger->fp.commonfp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
insertMessage(lvl , buffer);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeStdout(int lvl , const char *filename , const char *func , int linenum , const char *message)
|
||||||
|
{
|
||||||
|
char buffer[KLOG_MAXMSGSIZE + 1] = {0};
|
||||||
|
formatMessage(lvl , filename , func , linenum , message , buffer , KLOG_MAXMSGSIZE);
|
||||||
|
fprintf(stdout , "%s" , buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int writeLog(int lvl , const char *filename , const char *func , int linenum , const char *message)
|
||||||
|
{
|
||||||
|
switch (logger->otype)
|
||||||
|
{
|
||||||
|
case OUT_SYSLOG:
|
||||||
|
{
|
||||||
|
writeSyslog(lvl , message);
|
||||||
|
}break;
|
||||||
|
case OUT_SPECFILE:{
|
||||||
|
writeSpecFile(lvl , filename , func , linenum , message);
|
||||||
|
}break;
|
||||||
|
case OUT_STDOUT:{
|
||||||
|
writeStdout(lvl , filename , func , linenum , message);
|
||||||
|
}break;
|
||||||
|
default:{
|
||||||
|
printf("输出类别无效!%d\n" , logger->otype);
|
||||||
|
return EINVAL;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __KLOG_WRITE_H__
|
||||||
|
#define __KLOG_WRITE_H__
|
||||||
|
#include "format.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
extern int writeLog(int lvl , const char *filename , const char *func , int linenum , const char *message) NOTNULL();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include "writeFile.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int writeFile(int lvl , const char *message , unsigned int len)
|
||||||
|
{
|
||||||
|
if (logger->levelBasedStorage)
|
||||||
|
{
|
||||||
|
if (!logger->fp.classfiedfp[lvl])
|
||||||
|
{
|
||||||
|
logger->fp.classfiedfp[lvl] = fopen(logger->logfileName.classfiedfileName[lvl] , "at");
|
||||||
|
if (!logger->fp.classfiedfp[lvl])
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件%s:%s\n" , logger->logfileName.classfiedfileName[lvl] , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fwrite(message , sizeof(char) , len , logger->fp.classfiedfp[lvl]) < len)
|
||||||
|
{
|
||||||
|
printf("日志写入错误,%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
if (logger->levelBasedContainHigherLevel) //当要求低优先级日志需要包含更高优先级的日志时,逐个写入日志文件
|
||||||
|
{
|
||||||
|
for (int i = lvl + 1 ; i < 8 ; i ++)
|
||||||
|
{
|
||||||
|
if (!logger->fp.classfiedfp[i])
|
||||||
|
{
|
||||||
|
logger->fp.classfiedfp[i] = fopen(logger->logfileName.classfiedfileName[i] , "at");
|
||||||
|
if (!logger->fp.classfiedfp[i])
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件%s:%s\n" , logger->logfileName.classfiedfileName[i] , strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fwrite(message , sizeof(char) , len , logger->fp.classfiedfp[i]) < len)
|
||||||
|
{
|
||||||
|
printf("日志写入错误,%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!logger->fp.commonfp)
|
||||||
|
{
|
||||||
|
logger->fp.commonfp = fopen(logger->logfileName.commonlogfileName , "at");
|
||||||
|
if (!logger->fp.commonfp)
|
||||||
|
{
|
||||||
|
printf("无法打开日志文件:%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fwrite(message , sizeof(char) , len , logger->fp.commonfp) < len)
|
||||||
|
{
|
||||||
|
printf("日志写入错误,%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef __KLOG_WRITEFILE_H__
|
||||||
|
#define __KLOG_WRITEFILE_H__
|
||||||
|
#include <sdkmarcos.h>
|
||||||
|
|
||||||
|
extern int writeFile(int lvl , const char *message , unsigned int len) NOTNULL();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include "processdaemon.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
namespace KYSDK_BASE {
|
||||||
|
static int g_dogID;
|
||||||
|
static char g_pipeName[64]; //管道名称
|
||||||
|
|
||||||
|
static void daemon(unsigned int interval, daemon_callback func, void* args)
|
||||||
|
{
|
||||||
|
static int pipefd = open(g_pipeName, O_RDONLY);
|
||||||
|
if (pipefd <= 0)
|
||||||
|
return;
|
||||||
|
fd_set read_fds, write_fds, except_fds;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
FD_ZERO(&write_fds);
|
||||||
|
FD_ZERO(&except_fds);
|
||||||
|
FD_SET(pipefd, &read_fds);
|
||||||
|
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = interval;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
char buf[24];
|
||||||
|
|
||||||
|
if (select(pipefd + 1, &read_fds, &write_fds, &except_fds, &timeout) > 0)
|
||||||
|
{
|
||||||
|
read(pipefd, buf, sizeof(buf));
|
||||||
|
printf("got %s\n", buf);
|
||||||
|
if (strcmp(buf, "quit") == 0)
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// timeout
|
||||||
|
if (func)
|
||||||
|
func(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(pipefd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int startProcDaemon(unsigned int interval, daemon_callback func, void* args)
|
||||||
|
{
|
||||||
|
if (g_dogID > 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sprintf(g_pipeName, "/tmp/pipe_procdaemon_%d", getpid());
|
||||||
|
|
||||||
|
if (mkfifo(g_pipeName, 0770) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
g_dogID = pid;
|
||||||
|
daemon(interval, func, args);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void feedDog()
|
||||||
|
{
|
||||||
|
static int pipefd = open(g_pipeName, O_RDWR);
|
||||||
|
if (pipefd <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
static unsigned long feedCounts = 0;
|
||||||
|
char buf[24];
|
||||||
|
sprintf(buf, "%023lu", feedCounts);
|
||||||
|
write(pipefd, buf, sizeof(buf));
|
||||||
|
feedCounts ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stopProcDaemon()
|
||||||
|
{
|
||||||
|
if (g_dogID <= 0)
|
||||||
|
return -1;
|
||||||
|
int pipefd = open(g_pipeName, O_RDWR);
|
||||||
|
if (pipefd <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char buf[24] = {0};
|
||||||
|
strcpy(buf, "quit");
|
||||||
|
write(pipefd, buf, sizeof(buf));
|
||||||
|
close(pipefd);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
waitpid(g_dogID, &status, 0);
|
||||||
|
g_dogID = 0;
|
||||||
|
|
||||||
|
remove(g_pipeName);
|
||||||
|
|
||||||
|
printf("dog quit.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef KYSDK_BASE_PROCESSDAEMON_H__
|
||||||
|
#define KYSDK_BASE_PROCESSDAEMON_H__
|
||||||
|
|
||||||
|
namespace KYSDK_BASE
|
||||||
|
{
|
||||||
|
typedef void (*daemon_callback)(void *args);
|
||||||
|
extern int startProcDaemon(unsigned int interval, daemon_callback func, void* args);
|
||||||
|
extern void feedDog();
|
||||||
|
extern int stopProcDaemon();
|
||||||
|
} // namespace KYSDK_BASE
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \example kysdk-base/src/process/test/main.cpp
|
||||||
|
*
|
||||||
|
*/
|
|
@ -0,0 +1,9 @@
|
||||||
|
CXXFLAGS += -g -O0 --std=c++11 -I../
|
||||||
|
VPATH=../:./
|
||||||
|
|
||||||
|
all:processdaemon.o
|
||||||
|
g++ -o processdaemon $(CXXFLAGS) main.cpp ../processdaemon.cpp
|
||||||
|
|
||||||
|
.PHONY:processdaemon.o
|
||||||
|
processdaemon.o:processdaemon.cpp
|
||||||
|
g++ -c -o processdaemon.o $(CXXFLAGS) ../processdaemon.cpp
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "processdaemon.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
int quit;
|
||||||
|
|
||||||
|
void func(void* data)
|
||||||
|
{
|
||||||
|
printf("dog fake.\n");
|
||||||
|
quit = 1;
|
||||||
|
KYSDK_BASE::stopProcDaemon();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
KYSDK_BASE::startProcDaemon(5, func, NULL);
|
||||||
|
int counts = 0;
|
||||||
|
srand(time(NULL));
|
||||||
|
while (!quit)
|
||||||
|
{
|
||||||
|
int stime = rand() % 7;
|
||||||
|
printf("sleep %d\n", stime);
|
||||||
|
sleep(stime);
|
||||||
|
KYSDK_BASE::feedDog();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("quit.\n");
|
||||||
|
KYSDK_BASE::stopProcDaemon();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
aux_source_directory(. SOURCESCODE)
|
||||||
|
add_library(kytimer SHARED ${SOURCESCODE})
|
||||||
|
add_executable(test-kytimer test/test-kytimer.c)
|
||||||
|
target_link_libraries(kytimer pthread)
|
||||||
|
target_link_libraries(test-kytimer kytimer)
|
||||||
|
|
||||||
|
install(TARGETS kytimer
|
||||||
|
DESTINATION lib/kysdk/kysdk-base)
|
||||||
|
|
||||||
|
# install(FILES libkytimer.h
|
||||||
|
# DESTINATION include/kysdk/kysdk-base)
|
|
@ -0,0 +1,26 @@
|
||||||
|
CLIBS=-lpthread -lrt
|
||||||
|
CFLAGS=-g -O0 -Wall -fPIC -shared
|
||||||
|
|
||||||
|
CC=cc
|
||||||
|
TARGET=libkytimer.so
|
||||||
|
LIBINSTALLNAME=libkytimer.so.1.0.0
|
||||||
|
LIBINSTALLDIR=/usr/lib/
|
||||||
|
HEADERINSTALLDIR=/usr/include/
|
||||||
|
HEADERS=kytimer.h
|
||||||
|
|
||||||
|
.PHONY:all
|
||||||
|
.PHONY:clean
|
||||||
|
.PHONY:lib
|
||||||
|
|
||||||
|
all:lib
|
||||||
|
|
||||||
|
lib:
|
||||||
|
$(CC) $(CFLAGS) $(CLIBS) -o $(TARGET) libkytimer.c
|
||||||
|
mkdir -p lib/
|
||||||
|
mv $(TARGET) lib/$(LIBINSTALLNAME)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm ../lib/$(LIBINSTALLNAME)
|
||||||
|
|
||||||
|
target_install:TARGET
|
||||||
|
cp $(LIBINSTALLNAME) $(LIBINSTALLDIR)
|
|
@ -0,0 +1,362 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include "libkytimer.h"
|
||||||
|
#include <sdkmarcos.h>
|
||||||
|
|
||||||
|
static void* timerCoreThread(void* data); //定时器循环核心
|
||||||
|
static KTimerNode* findNodeByFD(unsigned int fd); //根据fd查找节点
|
||||||
|
static KTimerNode* newTimerNode();
|
||||||
|
static void freeTimerNode(KTimerNode* node);
|
||||||
|
static int testNodeInList(KTimerNode* node);
|
||||||
|
static void insertIntoList(KTimerNode* node); //插入节点,按照间隔时间的顺序从小到大排序
|
||||||
|
static void deleteFromList(KTimerNode* node , int locked); //从链表中删除节点
|
||||||
|
static pthread_t g_coreThreadID; //核心循环线程
|
||||||
|
static KTimerNode* g_list = NULL;
|
||||||
|
static int epollfd; //事件池句柄
|
||||||
|
static int curTimerCounts; //当前已注册定时器数量
|
||||||
|
|
||||||
|
int kdk_timer_init(); //初始化定时器核心
|
||||||
|
size_t kdk_timer_start(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
|
||||||
|
void kdk_timer_stop(size_t timerfd); //停止一个定时器
|
||||||
|
void kdk_timer_reset(size_t timerfd , unsigned int intervalms);
|
||||||
|
void kdk_timer_destroy() DESTRUCTOR; //销毁定时器核心
|
||||||
|
|
||||||
|
#ifndef TFD_TIMER_CANCEL_ON_SET
|
||||||
|
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int kdk_timer_init()
|
||||||
|
{
|
||||||
|
if (g_coreThreadID > 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
epollfd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
|
if (epollfd <= 0)
|
||||||
|
{
|
||||||
|
printf("Epoll事件池创建失败!%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_create(&g_coreThreadID , NULL , timerCoreThread , NULL))
|
||||||
|
{
|
||||||
|
printf("kyTimer 定时器核心初始化失败:%s\n" , strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t kdk_timer_start(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete)
|
||||||
|
{
|
||||||
|
if (g_coreThreadID <= 0 && kdk_timer_init())
|
||||||
|
{
|
||||||
|
printf("定时器注册失败:定时器全局句柄初始化失败!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (curTimerCounts >= KTIMER_MAXTIMERFD)
|
||||||
|
{
|
||||||
|
printf("定时器注册失败:定时器注册数量已超限!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
KTimerNode* node = newTimerNode();
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
printf("定时器注册失败:无法分配内存,%s\n" , strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->callback = callback;
|
||||||
|
node->userdata = userdata;
|
||||||
|
node->intervalms = intervalms;
|
||||||
|
node->attr = attr;
|
||||||
|
node->type = type;
|
||||||
|
node->freeOnDelete = freeOnDelete;
|
||||||
|
|
||||||
|
if (type == KTIMER_ABSOLUTE)
|
||||||
|
node->fd = timerfd_create(CLOCK_MONOTONIC , TFD_CLOEXEC|TFD_NONBLOCK);
|
||||||
|
else
|
||||||
|
node->fd = timerfd_create(CLOCK_REALTIME , TFD_CLOEXEC|TFD_NONBLOCK);
|
||||||
|
|
||||||
|
if ((int)node->fd <= 0)
|
||||||
|
{
|
||||||
|
printf("定时器注册失败:timerfd_create失败,%s\n" , strerror(errno));
|
||||||
|
freeTimerNode(node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct itimerspec tval;
|
||||||
|
tval.it_value.tv_sec = intervalms / 1000;
|
||||||
|
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
|
||||||
|
|
||||||
|
if (attr == KTIMER_PERIODIC) //多次触发时设置interval项
|
||||||
|
{
|
||||||
|
tval.it_interval.tv_sec = tval.it_value.tv_sec;
|
||||||
|
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tval.it_interval.tv_sec = 0;
|
||||||
|
tval.it_interval.tv_nsec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerfd_settime(node->fd, 0, &tval, NULL);
|
||||||
|
|
||||||
|
//注册到Epoll池中
|
||||||
|
struct epoll_event ev;
|
||||||
|
memset(&ev , 0 , sizeof(struct epoll_event));
|
||||||
|
if (attr == KTIMER_SINGLESHOT)
|
||||||
|
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||||
|
else
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.ptr = node;
|
||||||
|
if (epoll_ctl(epollfd , EPOLL_CTL_ADD , node->fd , &ev))
|
||||||
|
{
|
||||||
|
printf("定时器注册失败:epoll_ctl错误,%s\n" , strerror(errno));
|
||||||
|
freeTimerNode(node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertIntoList(node);
|
||||||
|
|
||||||
|
return node->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
//停止一个定时器,并从列表中移除
|
||||||
|
void kdk_timer_stop(size_t timerfd)
|
||||||
|
{
|
||||||
|
if (timerfd <= 0)
|
||||||
|
return;
|
||||||
|
KTimerNode* node = findNodeByFD(timerfd);
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
if (epoll_ctl(epollfd , EPOLL_CTL_DEL , timerfd , NULL))
|
||||||
|
{
|
||||||
|
printf("无法注销文件句柄%zd : %s\n" , timerfd , strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deleteFromList(node , 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//定时器核心线程,负责循环处理已到期的定时器
|
||||||
|
static void* timerCoreThread(void* data)
|
||||||
|
{
|
||||||
|
pthread_detach(pthread_self());
|
||||||
|
|
||||||
|
KTimerNode* knode = NULL;
|
||||||
|
int read_fds = 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
|
||||||
|
pthread_testcancel();
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE , NULL);
|
||||||
|
|
||||||
|
struct epoll_event activeEvs[KTIMER_MAXTIMERFD];
|
||||||
|
read_fds = epoll_wait(epollfd , activeEvs , KTIMER_MAXTIMERFD , -1);
|
||||||
|
if (read_fds < 0)
|
||||||
|
{
|
||||||
|
printf("epoll wait error , %s\n" , strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0 ; i < read_fds ; i ++)
|
||||||
|
{
|
||||||
|
knode = activeEvs[i].data.ptr;
|
||||||
|
pthread_mutex_lock(&knode->lock);
|
||||||
|
if (!testNodeInList(knode))
|
||||||
|
continue;
|
||||||
|
uint64_t dep;
|
||||||
|
size_t rd = read(knode->fd , &dep , sizeof(uint64_t));
|
||||||
|
if (rd <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (knode && knode->attr != KTIMER_NEVER && knode->callback)
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
|
||||||
|
pthread_create(&tid , &attr , knode->callback , knode->userdata);
|
||||||
|
|
||||||
|
knode->freeOnDelete = 0;
|
||||||
|
#else
|
||||||
|
knode->callback(knode->userdata);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (knode->attr == KTIMER_SINGLESHOT) //单次触发的在触发后就从链表中移除
|
||||||
|
{
|
||||||
|
// FixMe: 如果设置了freeOnDelete,并且采用上面的线程执行模式,
|
||||||
|
// 则会发生段错误。因此只能在启用线程执行模式的时候强制禁止
|
||||||
|
// freeOnDelete。
|
||||||
|
deleteFromList(knode , 1);
|
||||||
|
}
|
||||||
|
if (testNodeInList(knode))
|
||||||
|
pthread_mutex_unlock(&knode->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KTimerNode* findNodeByFD(unsigned int fd)
|
||||||
|
{
|
||||||
|
KTimerNode* tmp = g_list;
|
||||||
|
while (tmp)
|
||||||
|
{
|
||||||
|
if (tmp->fd == fd)
|
||||||
|
return tmp;
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int testNodeInList(KTimerNode* node)
|
||||||
|
{
|
||||||
|
KTimerNode* pos = g_list;
|
||||||
|
while (pos)
|
||||||
|
{
|
||||||
|
if (pos == node)
|
||||||
|
return 1;
|
||||||
|
pos = pos->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insertIntoList(KTimerNode* node)
|
||||||
|
{
|
||||||
|
curTimerCounts ++;
|
||||||
|
if (!g_list)
|
||||||
|
{
|
||||||
|
g_list = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (g_list->intervalms > node->intervalms)
|
||||||
|
{
|
||||||
|
node->next = g_list;
|
||||||
|
g_list = node;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KTimerNode* cur = g_list->next;
|
||||||
|
KTimerNode* prev = g_list;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (cur->intervalms > node->intervalms)
|
||||||
|
{
|
||||||
|
node->next = cur;
|
||||||
|
prev->next = node;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev->next = node;
|
||||||
|
node->next = NULL;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deleteFromList(KTimerNode* node , int locked)
|
||||||
|
{
|
||||||
|
if (!g_list)
|
||||||
|
return;
|
||||||
|
curTimerCounts --;
|
||||||
|
if (!locked)
|
||||||
|
pthread_mutex_lock(&node->lock);
|
||||||
|
if (node == g_list)
|
||||||
|
{
|
||||||
|
g_list = g_list->next;
|
||||||
|
freeTimerNode(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KTimerNode* pos = g_list;
|
||||||
|
while (pos)
|
||||||
|
{
|
||||||
|
if (pos->next && pos->next == node)
|
||||||
|
{
|
||||||
|
pos->next = pos->next->next;
|
||||||
|
freeTimerNode(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos = pos->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KTimerNode* newTimerNode()
|
||||||
|
{
|
||||||
|
KTimerNode *node = (KTimerNode*)calloc(1 , sizeof(KTimerNode));
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
pthread_mutex_init(&node->lock , NULL);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeTimerNode(KTimerNode *node)
|
||||||
|
{
|
||||||
|
if (! node)
|
||||||
|
return;
|
||||||
|
if (node->fd > 0)
|
||||||
|
close(node->fd);
|
||||||
|
if (node->userdata && node->freeOnDelete)
|
||||||
|
free(node->userdata);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_timer_reset(size_t timerfd , unsigned int intervalms)
|
||||||
|
{
|
||||||
|
KTimerNode* node = findNodeByFD(timerfd);
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
node->intervalms = intervalms;
|
||||||
|
struct itimerspec tval;
|
||||||
|
tval.it_value.tv_sec = intervalms / 1000;
|
||||||
|
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
|
||||||
|
|
||||||
|
if (node->attr == KTIMER_PERIODIC) //多次触发时设置interval项
|
||||||
|
{
|
||||||
|
tval.it_interval.tv_sec = tval.it_value.tv_sec;
|
||||||
|
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tval.it_interval.tv_sec = 0;
|
||||||
|
tval.it_interval.tv_nsec = 0;
|
||||||
|
}
|
||||||
|
timerfd_settime(timerfd , 0 , &tval , NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdk_timer_destroy() //停止所有定时器,并销毁定时器核心
|
||||||
|
{
|
||||||
|
//终止线程运行
|
||||||
|
if (g_coreThreadID > 0)
|
||||||
|
pthread_cancel(g_coreThreadID);
|
||||||
|
|
||||||
|
//销毁链表
|
||||||
|
KTimerNode* node = g_list;
|
||||||
|
while (node)
|
||||||
|
{
|
||||||
|
deleteFromList(node , 0);
|
||||||
|
node = g_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//关闭epoll池
|
||||||
|
if (epollfd > 0)
|
||||||
|
close(epollfd);
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
#ifndef KYSDK_BASE_TIMER_H__
|
||||||
|
#define KYSDK_BASE_TIMER_H__
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup 定时器模块
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file libkytimer.h
|
||||||
|
* @author liuyunhe (liuyunhe@kylinos.cn)
|
||||||
|
* @brief KYSDK C语言定时器模块
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2021-10-28
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
KTIMER_SINGLESHOT = 0,
|
||||||
|
KTIMER_PERIODIC = 1,
|
||||||
|
KTIMER_NEVER = 2
|
||||||
|
}KTimerAttribute;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
KTIMER_ABSOLUTE = 0,
|
||||||
|
KTIMER_RELATIVE
|
||||||
|
}KTimerType;
|
||||||
|
|
||||||
|
#define KTIMER_MAXTIMERFD 1000 //最多支持注册1000个定时器
|
||||||
|
|
||||||
|
typedef void* (*time_handler)(void* user_data); //回调函数规则
|
||||||
|
|
||||||
|
typedef struct _KTimerNode{
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
size_t fd; //序列号
|
||||||
|
time_handler callback; //到期后执行的函数
|
||||||
|
int freeOnDelete; //删除定时器时自动释放userdata
|
||||||
|
void* userdata; //callback使用的参数,外部使用alloc分配内存,在delete定时器的时候会根据配置自动释放
|
||||||
|
unsigned int intervalms; //定时器间隔时间,单位毫秒
|
||||||
|
KTimerAttribute attr; //触发类型,单次触发或多次触发
|
||||||
|
KTimerType type; //时钟类型,绝对时间还是相对时间
|
||||||
|
struct _KTimerNode* next;
|
||||||
|
}KTimerNode;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* @brief 初始化定时器核心组件
|
||||||
|
*
|
||||||
|
* @return int 成功返回0,失败返回错误码
|
||||||
|
*/
|
||||||
|
extern int kdk_timer_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 启动一个定时器
|
||||||
|
*
|
||||||
|
* @param intervalms 定时器时间,以毫秒为单位
|
||||||
|
* @param callback 定时器到期后触发的回调函数指针
|
||||||
|
* @param attr 定时器属性,KTIMER_SINGLESHOT表示一次性定时器;KTIMER_PERIODIC表示周期性定时器;KTIMER_NEVER表示不会被触发的定时器
|
||||||
|
* @param type 定时器类型,KTIMER_ABSOLUTE表示绝对时间定时器,修改系统时间不会影响定时器的时间;KTIMER_RELATIVE表示相对时间定时器,修改系统时间会影响定时器时间
|
||||||
|
* @param userdata 指向用户数据的指针
|
||||||
|
* @param freeOnDelete [未启用]
|
||||||
|
* @return size_t 定时器的ID
|
||||||
|
*/
|
||||||
|
extern size_t kdk_timer_start(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 停止给定的定时器
|
||||||
|
*
|
||||||
|
* @param timerfd 由kdk_timer_start返回的定时器ID
|
||||||
|
*/
|
||||||
|
extern void kdk_timer_stop(size_t timerfd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置定时器时间至intervalms,以毫秒为单位
|
||||||
|
*
|
||||||
|
* @param timerfd 由kdk_timer_start返回的定时器ID
|
||||||
|
* @param intervalms 需要调整的时间间隔,以ms为单位
|
||||||
|
*/
|
||||||
|
extern void kdk_timer_reset(size_t timerfd , unsigned int intervalms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 销毁定时器核心
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern void kdk_timer_destroy();
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //KYSDK_BASE_TIMER_H__
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \example kysdk-base/src/timer/test/test-kytimer.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
|
@ -0,0 +1,5 @@
|
||||||
|
all:
|
||||||
|
gcc -g -O0 -o test-kytimer test-kytimer.c -lkytimer -lpthread
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm demo
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <libkytimer.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void func(char* comment)
|
||||||
|
{
|
||||||
|
char date[32] = {0};
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
if (ctime_r(&now , date))
|
||||||
|
date[strlen(date) - 1] = '\0';
|
||||||
|
printf("[%s]这是%s\n" , date , comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop(size_t* fdpoll)
|
||||||
|
{
|
||||||
|
printf("开始停止定时器\n");
|
||||||
|
for (int i = 0 ; i < 10 ; i ++)
|
||||||
|
{
|
||||||
|
kdk_timer_stop(fdpoll[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
assert(kdk_timer_init() == 0);
|
||||||
|
|
||||||
|
//测试1 -- 基本功能
|
||||||
|
size_t fdpoll[10] = {0};
|
||||||
|
for (int i = 1 ; i <= 10 ; i ++)
|
||||||
|
{
|
||||||
|
char* data = (char*)malloc(10);
|
||||||
|
assert(data);
|
||||||
|
sprintf(data , "%d号" , i);
|
||||||
|
fdpoll[i - 1] = kdk_timer_start(i * 1000 , (time_handler)func , KTIMER_PERIODIC , KTIMER_ABSOLUTE, (void*)data , 1);
|
||||||
|
assert(fdpoll[i - 1]);
|
||||||
|
}
|
||||||
|
kdk_timer_start(10000 , (time_handler)stop , KTIMER_SINGLESHOT , KTIMER_ABSOLUTE, (void*)fdpoll , 0);
|
||||||
|
sleep(11);
|
||||||
|
|
||||||
|
//测试3 -- 单次触发
|
||||||
|
printf("单次触发测试:\n");
|
||||||
|
kdk_timer_start(2000 , (time_handler)func , KTIMER_SINGLESHOT , KTIMER_ABSOLUTE, "2号" , 0);
|
||||||
|
sleep(5);
|
||||||
|
|
||||||
|
//测试2 -- 重置定时器时间
|
||||||
|
printf("修改时间测试:\n");
|
||||||
|
int persec = kdk_timer_start(1000 , (time_handler)func , KTIMER_PERIODIC , KTIMER_ABSOLUTE, "1号" , 0);
|
||||||
|
int sec3 = kdk_timer_start(3000 , (time_handler)func , KTIMER_SINGLESHOT , KTIMER_ABSOLUTE, "3号" , 0);
|
||||||
|
sleep(2);
|
||||||
|
kdk_timer_reset(sec3 , 4000);
|
||||||
|
printf("sec3 时钟已被重置为4000ms\n");
|
||||||
|
sleep(10);
|
||||||
|
|
||||||
|
printf("正在销毁定时器核心...\n");
|
||||||
|
kdk_timer_destroy();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
add_subdirectory(data-structure)
|
||||||
|
|
||||||
|
# install(FILES sdkmarcos.h kerr.h cstring-extension.h
|
||||||
|
# DESTINATION include/kysdk/kysdk-base)
|
|
@ -0,0 +1,322 @@
|
||||||
|
#ifndef KYSDK_BASE_UTILS_STREXT_H__
|
||||||
|
#define KYSDK_BASE_UTILS_STREXT_H__
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup C语言字符串扩展模块
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file cstring-extension.h
|
||||||
|
* @author liuyunhe (liuyunhe@kylinos.cn)
|
||||||
|
* @brief KYSDK C语言字符串操作扩展
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2021-10-28
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* @brief 对给定的字符串进行strip操作,删减字符串前后的指定字符;注意该操作会修改原字符串
|
||||||
|
*
|
||||||
|
* @param str 需要进行strip的字符串指针
|
||||||
|
* @param ch 需要删除的字符
|
||||||
|
*/
|
||||||
|
static inline void strstrip(char *str, char ch)
|
||||||
|
{
|
||||||
|
if (strlen(str) == 0)
|
||||||
|
return;
|
||||||
|
char *startPos = str;
|
||||||
|
while (*startPos != '\0' && *startPos == ch)
|
||||||
|
startPos++;
|
||||||
|
if (*startPos == '\0')
|
||||||
|
{
|
||||||
|
str[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *endPos = str + strlen(str) - 1;
|
||||||
|
while (endPos != str && *endPos == ch)
|
||||||
|
endPos --;
|
||||||
|
|
||||||
|
memmove(str, startPos, endPos - startPos + 1);
|
||||||
|
*(str + (endPos - startPos) + 1) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 删除给定字符串前后的空格、制表符、换行符,注意该操作会修改原字符串
|
||||||
|
*
|
||||||
|
* @param str 需要进行strip操作的字符串指针
|
||||||
|
*/
|
||||||
|
static inline void strstripspace(char *str)
|
||||||
|
{
|
||||||
|
if (strlen(str) == 0)
|
||||||
|
return;
|
||||||
|
char *startPos = str;
|
||||||
|
while (*startPos != '\0' && isspace(*startPos))
|
||||||
|
startPos++;
|
||||||
|
if (*startPos == '\0')
|
||||||
|
{
|
||||||
|
str[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *endPos = str + strlen(str) - 1;
|
||||||
|
while (endPos != str && isspace(*endPos))
|
||||||
|
endPos --;
|
||||||
|
|
||||||
|
memmove(str, startPos, endPos - startPos + 1);
|
||||||
|
*(str + (endPos - startPos) + 1) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 删除给定字符串前后的空格和水平制表符(tab),注意该操作会修改原字符串
|
||||||
|
*
|
||||||
|
* @param str 需要修改的字符串指针
|
||||||
|
*/
|
||||||
|
static inline void strstripblank(char *str)
|
||||||
|
{
|
||||||
|
if (strlen(str) == 0)
|
||||||
|
return;
|
||||||
|
char *startPos = str;
|
||||||
|
while (*startPos != '\0' && isblank(*startPos))
|
||||||
|
startPos++;
|
||||||
|
if (*startPos == '\0')
|
||||||
|
{
|
||||||
|
str[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *endPos = str + strlen(str) - 1;
|
||||||
|
while (endPos != str && isblank(*endPos))
|
||||||
|
endPos --;
|
||||||
|
|
||||||
|
memmove(str, startPos, endPos - startPos + 1);
|
||||||
|
*(str + (endPos - startPos) + 1) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 跳过字符串前的所有空格、制表符、换行符;该操作不会修改原字符串
|
||||||
|
*
|
||||||
|
* @param p 指向原字符串的指针
|
||||||
|
* @return const char* 指向跳过space后的字符串指针
|
||||||
|
*/
|
||||||
|
static inline const char *strskipspace(const char *p)
|
||||||
|
{
|
||||||
|
while (isspace(*p))
|
||||||
|
++p;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 跳过字符串前的所有空格和水平制表符(tab);该操作不会修改原字符串
|
||||||
|
*
|
||||||
|
* @param p 指向原字符串的指针
|
||||||
|
* @return const char* 指向跳过space后的字符串指针
|
||||||
|
*/
|
||||||
|
static inline const char *strskipblank(const char *p)
|
||||||
|
{
|
||||||
|
while (isblank(*p))
|
||||||
|
++p;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将字符串中的所有小写字母转化为大写字母;注意该操作会修改原字符串
|
||||||
|
*
|
||||||
|
* @param str 需要操作的字符串指针
|
||||||
|
*/
|
||||||
|
static inline void str2upper(char *str)
|
||||||
|
{
|
||||||
|
char *pos = str;
|
||||||
|
while (*pos)
|
||||||
|
{
|
||||||
|
if (isalpha(*pos))
|
||||||
|
*pos &= 0xDF;
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将字符串中的所有大写字母转化为小写字母;注意该操作会修改原字符串
|
||||||
|
*
|
||||||
|
* @param str 需要操作的字符串指针
|
||||||
|
*/
|
||||||
|
static inline void str2lower(char *str)
|
||||||
|
{
|
||||||
|
char *pos = str;
|
||||||
|
while (*pos)
|
||||||
|
{
|
||||||
|
if (isalpha(*pos))
|
||||||
|
*pos |= 0x20;
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在给定的字符串中查找给定字符第一次出现的位置;计数从0开始
|
||||||
|
*
|
||||||
|
* @param str 原字符串
|
||||||
|
* @param ch 需要查找的字符
|
||||||
|
* @return int 第一次出现的位置,若未找到给定的字符,则返回-1
|
||||||
|
*/
|
||||||
|
static inline int strfirstof(const char* str, char ch)
|
||||||
|
{
|
||||||
|
const char* pos = str;
|
||||||
|
while (*pos)
|
||||||
|
{
|
||||||
|
if (*pos == ch)
|
||||||
|
return pos - str;
|
||||||
|
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在给定的字符串中查找给定字符最后一次出现的位置;计数从0开始
|
||||||
|
*
|
||||||
|
* @param str 原字符串
|
||||||
|
* @param ch 需要查找的字符
|
||||||
|
* @return int 最后一次出现的位置,若未找到给定的字符,则返回-1
|
||||||
|
*/
|
||||||
|
static inline int strlastof(const char* str, char ch)
|
||||||
|
{
|
||||||
|
const char* pos = str;
|
||||||
|
while (*pos)
|
||||||
|
pos ++;
|
||||||
|
pos --;
|
||||||
|
while (pos != str)
|
||||||
|
{
|
||||||
|
if (*pos == ch)
|
||||||
|
return pos - str + 1;
|
||||||
|
pos --;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断str是否以prefix开头;注意该函数区分大小写
|
||||||
|
*
|
||||||
|
* @param str 原字符串
|
||||||
|
* @param prefix 需要匹配的字符串前缀
|
||||||
|
* @return int 若str以prefix开头,则返回0;否则返回1
|
||||||
|
*/
|
||||||
|
static inline int strstartswith(const char *str, const char *prefix)
|
||||||
|
{
|
||||||
|
size_t sz = prefix ? strlen(prefix) : 0;
|
||||||
|
|
||||||
|
if (str && sz && strncmp(str, prefix, sz) == 0)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断str是否以prefix开头;不区分大小写
|
||||||
|
*
|
||||||
|
* @param str 原字符串
|
||||||
|
* @param prefix 需要匹配的字符串前缀
|
||||||
|
* @return int 若str以prefix开头,则返回0;否则返回1
|
||||||
|
*/
|
||||||
|
static inline int strstartswith_nocase(const char *str, const char *prefix)
|
||||||
|
{
|
||||||
|
size_t sz = prefix ? strlen(prefix) : 0;
|
||||||
|
|
||||||
|
if (str && sz && strncasecmp(str, prefix, sz) == 0)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断str是否以postfix结尾;注意该函数区分大小写
|
||||||
|
*
|
||||||
|
* @param str 原字符串
|
||||||
|
* @param postfix 需要匹配的字符串后缀
|
||||||
|
* @return int 若str以postfix结尾,则返回0;否则返回1
|
||||||
|
*/
|
||||||
|
static inline int strendwith(const char *str, const char *postfix)
|
||||||
|
{
|
||||||
|
size_t sl = str ? strlen(str) : 0;
|
||||||
|
size_t pl = postfix ? strlen(postfix) : 0;
|
||||||
|
|
||||||
|
if (pl == 0)
|
||||||
|
return 0;
|
||||||
|
if (sl < pl)
|
||||||
|
return 1;
|
||||||
|
if (memcmp(str + sl - pl, postfix, pl) != 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strendwith_nocase(const char *str, const char *postfix)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 统计给定字符在字符串中出现的次数
|
||||||
|
*
|
||||||
|
* @param str 原字符串
|
||||||
|
* @param ch 需要统计的字符
|
||||||
|
* @return size_t 字符出现的次数
|
||||||
|
*/
|
||||||
|
static inline size_t strcounts(const char *str, char ch)
|
||||||
|
{
|
||||||
|
const char *p = str;
|
||||||
|
int counts = 0;
|
||||||
|
while (*p)
|
||||||
|
{
|
||||||
|
if (*p == ch)
|
||||||
|
counts ++;
|
||||||
|
p ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 对原字符串以给定的分隔符进行分割,注意该函数会修改原字符串
|
||||||
|
*
|
||||||
|
* @param str 需要分割的字符串
|
||||||
|
* @param delim 分隔符
|
||||||
|
* @return char** 分割后的字符串列表,以NULL结尾。存储分割后所有字符串的字符串列表本身是由alloc申请的内存,因此当使用
|
||||||
|
* 完成后应当被free;而分割出来的各个字符串不是申请的内存,而是分别指向了原字符串中的特定位置,因此他们不需要被分别free
|
||||||
|
*/
|
||||||
|
static inline char** strsplit(char *str, char delim)
|
||||||
|
{
|
||||||
|
size_t size = strcounts(str, delim) + 1;
|
||||||
|
char **res = (char **)calloc(1, sizeof(char *) * (size + 1));
|
||||||
|
if (!res)
|
||||||
|
return NULL;
|
||||||
|
if (size < 2)
|
||||||
|
{
|
||||||
|
res[0] = str;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
char *leftstr;
|
||||||
|
res[0] = strtok_r(str, &delim, &leftstr);
|
||||||
|
for (size_t i = 1; i < size; i ++)
|
||||||
|
{
|
||||||
|
res[i] = strtok_r(NULL, &delim, &leftstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
|
@ -0,0 +1,17 @@
|
||||||
|
aux_source_directory(linklist/skip_linklist SOURCESCODE)
|
||||||
|
add_library(kydatastruct SHARED ${SOURCESCODE})
|
||||||
|
|
||||||
|
add_subdirectory(linklist)
|
||||||
|
|
||||||
|
include_directories(linklist)
|
||||||
|
include_directories(linklist/skip_linklist)
|
||||||
|
|
||||||
|
add_executable(test-delete linklist/skip_linklist/test/delete_test.c)
|
||||||
|
add_executable(test-insert linklist/skip_linklist/test/insert_test.c)
|
||||||
|
add_executable(test-search linklist/skip_linklist/test/search_test.c)
|
||||||
|
target_link_libraries(test-delete kydatastruct)
|
||||||
|
target_link_libraries(test-insert kydatastruct)
|
||||||
|
target_link_libraries(test-search kydatastruct)
|
||||||
|
|
||||||
|
install(TARGETS kydatastruct
|
||||||
|
DESTINATION lib/kysdk/kysdk-base)
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"editor.tokenColorCustomizations": {
|
||||||
|
"textMateRules": [
|
||||||
|
{
|
||||||
|
"scope": "kunpeng.func",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#28a745"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "kunpeng.intrinsics",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#28a745"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
install(FILES listdata.h skip_linklist/skip_linklist.h
|
||||||
|
DESTINATION include/kysdk/kysdk-base)
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef KYSDK_BASE_UTILS_LIST_DATA_H__
|
||||||
|
#define KYSDK_BASE_UTILS_LIST_DATA_H__
|
||||||
|
|
||||||
|
typedef union _kysdk_listdata
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
int num;
|
||||||
|
}kysdk_listdata;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,228 @@
|
||||||
|
#include <sdkmarcos.h>
|
||||||
|
#include "skip_linklist.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static kysdk_skiplist_node* malloc_skiplist_node(unsigned int maxlvl)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *res = malloc(sizeof(kysdk_skiplist_node));
|
||||||
|
ASSERT_NOT_NULL(res, NULL);
|
||||||
|
|
||||||
|
res->data.num = 0;
|
||||||
|
res->key = 0;
|
||||||
|
|
||||||
|
res->next = malloc(sizeof(kysdk_skiplist_node*) * maxlvl);
|
||||||
|
if (! res->next)
|
||||||
|
{
|
||||||
|
free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < maxlvl; i ++)
|
||||||
|
res->next[i] = NULL;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
kysdk_skiplist* kysdk_create_skiplist()
|
||||||
|
{
|
||||||
|
kysdk_skiplist *res = malloc(sizeof(kysdk_skiplist));
|
||||||
|
ASSERT_NOT_NULL(res, NULL);
|
||||||
|
|
||||||
|
res->counts = 0;
|
||||||
|
res->max_levels = 3;
|
||||||
|
res->children = calloc(0, sizeof(kysdk_skiplist_node *) * res->max_levels);
|
||||||
|
if (!res->children)
|
||||||
|
{
|
||||||
|
free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kysdk_destroy_skiplist(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
// delete all node
|
||||||
|
kysdk_skiplist_node *curnode = list->children[0];
|
||||||
|
while (curnode)
|
||||||
|
{
|
||||||
|
list->children[0] = curnode->next[0];
|
||||||
|
|
||||||
|
SAFE_FREE(curnode->next);
|
||||||
|
SAFE_FREE(curnode);
|
||||||
|
|
||||||
|
curnode = list->children[0];
|
||||||
|
list->counts --;
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_FREE(list->children);
|
||||||
|
SAFE_FREE(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kysdk_skiplist_setmaxlevels(kysdk_skiplist *list, unsigned int maxlevels)
|
||||||
|
{
|
||||||
|
if (!list || list->counts)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
list->max_levels = maxlevels;
|
||||||
|
kysdk_skiplist_node **tmp = list->children;
|
||||||
|
list->children = realloc(list->children, sizeof(kysdk_skiplist_node *) * list->max_levels);
|
||||||
|
if (!list->children)
|
||||||
|
{
|
||||||
|
list->children = tmp;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < maxlevels; i ++)
|
||||||
|
list->children[i] = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kysdk_skiplist_insert(kysdk_skiplist *list, int key, kysdk_listdata data)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *node = malloc_skiplist_node(list->max_levels);
|
||||||
|
ASSERT_NOT_NULL(node, -1);
|
||||||
|
|
||||||
|
node->key = key;
|
||||||
|
node->data = data;
|
||||||
|
|
||||||
|
if (! list->children[0] || list->children[0]->key > key)
|
||||||
|
{
|
||||||
|
node->next[0] = list->children[0];
|
||||||
|
list->children[0] = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *prevnode = NULL, *curnode = NULL;
|
||||||
|
// 找到插入位置
|
||||||
|
for (int i = list->max_levels - 1; i >= 0; i --)
|
||||||
|
{
|
||||||
|
if (prevnode)
|
||||||
|
curnode = prevnode;
|
||||||
|
else
|
||||||
|
curnode = list->children[i];
|
||||||
|
|
||||||
|
if (! curnode || curnode->key > key)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (curnode->next[i] && curnode->next[i]->key <= key)
|
||||||
|
curnode = curnode->next[i];
|
||||||
|
|
||||||
|
prevnode = curnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevnode)
|
||||||
|
{
|
||||||
|
node->next[0] = prevnode->next[0];
|
||||||
|
prevnode->next[0] = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node->next[0] = list->children[0];
|
||||||
|
list->children[0] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 决定是否升层
|
||||||
|
srand(time(NULL));
|
||||||
|
for (int i = 1; i < list->max_levels; i ++)
|
||||||
|
{
|
||||||
|
if (random() & 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (! list->children[i] || list->children[i]->key > key)
|
||||||
|
{
|
||||||
|
node->next[i] = list->children[i];
|
||||||
|
list->children[i] = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *prevnode = list->children[i];
|
||||||
|
while (prevnode->next[i] && prevnode->next[i]->key <= key)
|
||||||
|
prevnode = prevnode->next[i];
|
||||||
|
|
||||||
|
node->next[i] = prevnode->next[i];
|
||||||
|
prevnode->next[i] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list->counts ++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kysdk_skiplist_delete(kysdk_skiplist *list, int key)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *node = NULL;
|
||||||
|
|
||||||
|
if (! list->children[0] || list->children[0]->key > key)
|
||||||
|
{
|
||||||
|
// no target found
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *prevnode = NULL, *curnode = NULL;
|
||||||
|
// 找到删除位置
|
||||||
|
for (int i = list->max_levels - 1; i >= 0; i --)
|
||||||
|
{
|
||||||
|
if (prevnode)
|
||||||
|
curnode = prevnode;
|
||||||
|
else
|
||||||
|
curnode = list->children[i];
|
||||||
|
|
||||||
|
if (! curnode || curnode->key > key)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (curnode->next[i] && curnode->next[i]->key < key)
|
||||||
|
curnode = curnode->next[i];
|
||||||
|
|
||||||
|
prevnode = curnode;
|
||||||
|
|
||||||
|
if (prevnode->next[i] && prevnode->next[i]->key == key)
|
||||||
|
{
|
||||||
|
node = prevnode->next[i];
|
||||||
|
prevnode->next[i] = prevnode->next[i]->next[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
SAFE_FREE(node->next);
|
||||||
|
SAFE_FREE(node);
|
||||||
|
list->counts --;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
kysdk_listdata kysdk_skiplist_search(kysdk_skiplist *list, int key)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *curnode = NULL, *res = NULL;
|
||||||
|
|
||||||
|
for (int i = list->max_levels - 1; i >= 0; i --)
|
||||||
|
{
|
||||||
|
if (!list->children[i] || list->children[i]->key > key)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (! curnode)
|
||||||
|
curnode = list->children[i];
|
||||||
|
|
||||||
|
while (curnode->next[i] && curnode->next[i]->key <= key)
|
||||||
|
curnode = curnode->next[i];
|
||||||
|
|
||||||
|
if (curnode && curnode->key == key)
|
||||||
|
{
|
||||||
|
res = curnode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
return res->data;
|
||||||
|
|
||||||
|
return (kysdk_listdata)-1;
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
#ifndef KYSDK_BASE_UTILS_SKIPLISK_H__
|
||||||
|
#define KYSDK_BASE_UTILS_SKIPLISK_H__
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup 链表模块
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file skip_linklist.h
|
||||||
|
* @author liuyunhe (liuyunhe@kylinos.cn)
|
||||||
|
* @brief 时间复杂度:
|
||||||
|
* 插入(平均): O(log(n))
|
||||||
|
* 删除(平均): O(log(n))
|
||||||
|
* 查找(平均): O(log(n))
|
||||||
|
* 空间复杂度:
|
||||||
|
* O(n)
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2021-09-07
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <listdata.h>
|
||||||
|
|
||||||
|
typedef struct _kysdk_skiplist_node{
|
||||||
|
int key;
|
||||||
|
kysdk_listdata data;
|
||||||
|
|
||||||
|
struct _kysdk_skiplist_node **next;
|
||||||
|
}kysdk_skiplist_node;
|
||||||
|
|
||||||
|
typedef struct _kysdk_skiplist{
|
||||||
|
unsigned int counts; // 节点个数
|
||||||
|
unsigned int max_levels; // 最高层数
|
||||||
|
|
||||||
|
kysdk_skiplist_node **children;
|
||||||
|
}kysdk_skiplist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建跳表
|
||||||
|
*
|
||||||
|
* @return kysdk_skiplist*
|
||||||
|
*/
|
||||||
|
extern kysdk_skiplist* kysdk_create_skiplist();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 销毁跳表,并回收所有分配的内存;注意data.ptr指向的内存(若存在)不会被释放
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
*/
|
||||||
|
extern void kysdk_destroy_skiplist(kysdk_skiplist *list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置跳表最高层数,该选项必须在跳表为空时使用
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @param maxlevels
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
extern int kysdk_skiplist_setmaxlevels(kysdk_skiplist *list, unsigned int maxlevels);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插入节点
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @param key
|
||||||
|
* @param data
|
||||||
|
* @return int, 成功插入返回0,失败返回-1
|
||||||
|
*/
|
||||||
|
extern int kysdk_skiplist_insert(kysdk_skiplist *list, int key, kysdk_listdata data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 删除key值对应的节点
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @param key
|
||||||
|
* @return int, 成功删除返回0,失败返回-1
|
||||||
|
*/
|
||||||
|
extern int kysdk_skiplist_delete(kysdk_skiplist *list, int key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据给定的key搜索data内容
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @param key
|
||||||
|
* @return kysdk_listdata, 当搜索的key不存在时,data.num值为-1
|
||||||
|
*/
|
||||||
|
extern kysdk_listdata kysdk_skiplist_search(kysdk_skiplist *list, int key);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
gcc -o search_test search_test.c ../skip_linklist.c -I/data/git-local-storage/kysdk/kysdk-base/src/utils/ -I/data/git-local-storage/kysdk/kysdk-base/src/utils/data-structure/linklist/skip_linklist -I/data/git-local-storage/kysdk/kysdk-base/src/utils/data-structure/linklist/ -g -O0
|
||||||
|
gcc -o insert_test insert_test.c ../skip_linklist.c -I/data/git-local-storage/kysdk/kysdk-base/src/utils/ -I/data/git-local-storage/kysdk/kysdk-base/src/utils/data-structure/linklist/skip_linklist -I/data/git-local-storage/kysdk/kysdk-base/src/utils/data-structure/linklist/ -g -O0
|
||||||
|
gcc -o delete_test delete_test.c ../skip_linklist.c -I/data/git-local-storage/kysdk/kysdk-base/src/utils/ -I/data/git-local-storage/kysdk/kysdk-base/src/utils/data-structure/linklist/skip_linklist -I/data/git-local-storage/kysdk/kysdk-base/src/utils/data-structure/linklist/ -g -O0
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <skip_linklist.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define ARRAY_SIZE 20
|
||||||
|
|
||||||
|
int array[ARRAY_SIZE];
|
||||||
|
int del_flag[ARRAY_SIZE];
|
||||||
|
|
||||||
|
void print_list(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
for (int i = list->max_levels - 1; i >= 0; i --)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *curnode = list->children[i];
|
||||||
|
printf("Level[%d]: ", i);
|
||||||
|
while (curnode)
|
||||||
|
{
|
||||||
|
printf("%d->", curnode->key);
|
||||||
|
curnode = curnode->next[i];
|
||||||
|
}
|
||||||
|
printf("NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_list_delete(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ARRAY_SIZE; i ++)
|
||||||
|
{
|
||||||
|
if (random() % 2)
|
||||||
|
{
|
||||||
|
del_flag[i] = 1;
|
||||||
|
kysdk_skiplist_delete(list, array[i]);
|
||||||
|
printf("Deleted %d\n", array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE; i ++)
|
||||||
|
{
|
||||||
|
if (del_flag[i] && kysdk_skiplist_search(list, array[i]).num != -1)
|
||||||
|
{
|
||||||
|
printf("Skiplist delete test failed.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Skiplist order test pass.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
kysdk_skiplist *list = kysdk_create_skiplist();
|
||||||
|
|
||||||
|
kysdk_skiplist_setmaxlevels(list, 5);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE; i ++)
|
||||||
|
{
|
||||||
|
array[i] = i;
|
||||||
|
kysdk_skiplist_insert(list, i, (kysdk_listdata)i);
|
||||||
|
printf("%d has been insert.\n", i);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_list(list);
|
||||||
|
|
||||||
|
int res = test_list_delete(list);
|
||||||
|
|
||||||
|
print_list(list);
|
||||||
|
|
||||||
|
kysdk_destroy_skiplist(list);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include <skip_linklist.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void print_list(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
for (int i = list->max_levels - 1; i >= 0; i --)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *curnode = list->children[i];
|
||||||
|
printf("Level[%d]: ", i);
|
||||||
|
while (curnode)
|
||||||
|
{
|
||||||
|
printf("%d->", curnode->key);
|
||||||
|
curnode = curnode->next[i];
|
||||||
|
}
|
||||||
|
printf("NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_list_order(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *node = list->children[0];
|
||||||
|
while (node)
|
||||||
|
{
|
||||||
|
if (node->next[0])
|
||||||
|
{
|
||||||
|
if (node->key > node->next[0]->key)
|
||||||
|
{
|
||||||
|
printf("Skiplist order test failed.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = node->next[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Skiplist order test pass.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
kysdk_skiplist *list = kysdk_create_skiplist();
|
||||||
|
|
||||||
|
kysdk_skiplist_setmaxlevels(list, 5);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
for (int i = 0; i < 100; i ++)
|
||||||
|
{
|
||||||
|
int num = random() % 500 + 1;
|
||||||
|
kysdk_skiplist_insert(list, num, (kysdk_listdata)i);
|
||||||
|
printf("%d has been insert.\n", num);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_list(list);
|
||||||
|
|
||||||
|
int res = test_list_order(list);
|
||||||
|
|
||||||
|
kysdk_destroy_skiplist(list);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <skip_linklist.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void print_list(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
for (int i = list->max_levels - 1; i >= 0; i --)
|
||||||
|
{
|
||||||
|
kysdk_skiplist_node *curnode = list->children[i];
|
||||||
|
printf("Level[%d]: ", i);
|
||||||
|
while (curnode)
|
||||||
|
{
|
||||||
|
printf("%d->", curnode->key);
|
||||||
|
curnode = curnode->next[i];
|
||||||
|
}
|
||||||
|
printf("NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_list_search(kysdk_skiplist *list)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; i ++)
|
||||||
|
{
|
||||||
|
int target = random() % 50 + 1;
|
||||||
|
kysdk_listdata data = kysdk_skiplist_search(list, target);
|
||||||
|
printf("Searching for %d, data is %d\n", target, data.num);
|
||||||
|
|
||||||
|
kysdk_skiplist_node *curnode = list->children[0];
|
||||||
|
short finded = 0;
|
||||||
|
while (curnode)
|
||||||
|
{
|
||||||
|
if (curnode->key == target)
|
||||||
|
{
|
||||||
|
finded = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
curnode = curnode->next[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finded && data.num == -1)
|
||||||
|
{
|
||||||
|
printf("Skiplist search test failed.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Skiplist search test pass.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
kysdk_skiplist *list = kysdk_create_skiplist();
|
||||||
|
|
||||||
|
kysdk_skiplist_setmaxlevels(list, 5);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
for (int i = 0; i < 20; i ++)
|
||||||
|
{
|
||||||
|
int num = random() % 50 + 1;
|
||||||
|
kysdk_listdata data;
|
||||||
|
data.num = i;
|
||||||
|
kysdk_skiplist_insert(list, num, data);
|
||||||
|
printf("[%d] %d has been insert.\n", i, num);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_list(list);
|
||||||
|
|
||||||
|
int res = test_list_search(list);
|
||||||
|
|
||||||
|
kysdk_destroy_skiplist(list);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef KYSDK_BASE_UTILS_ERR_H__
|
||||||
|
#define KYSDK_BASE_UTILS_ERR_H__
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <errno.h>
|
||||||
|
#define KDK_EINVALIDARGS EINVAL
|
||||||
|
#define KDK_ENOMEM ENOMEM
|
||||||
|
#define KDK_PERM EPERM
|
||||||
|
#define KDK_EUNKNOW 65535
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,354 @@
|
||||||
|
/**
|
||||||
|
* @file kyutils.c
|
||||||
|
* @brief
|
||||||
|
* @author liuyang <liuyang@kylinos.cn>
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021-09-01
|
||||||
|
*
|
||||||
|
* @copyright Copyright: 2021,KylinSoft Co.,Ltd.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kyutils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef __uint8_t uint8_t;
|
||||||
|
typedef __uint16_t uint16_t;
|
||||||
|
typedef __uint32_t uint32_t;
|
||||||
|
typedef __uint64_t uint64_t;
|
||||||
|
|
||||||
|
typedef __intmax_t intmax_t;
|
||||||
|
typedef __uintmax_t uintmax_t;
|
||||||
|
|
||||||
|
#define PRIu64 __PRI64_PREFIX "u"
|
||||||
|
|
||||||
|
# if __WORDSIZE == 64
|
||||||
|
# define __PRI64_PREFIX "l"
|
||||||
|
# define __PRIPTR_PREFIX "l"
|
||||||
|
# else
|
||||||
|
# define __PRI64_PREFIX "ll"
|
||||||
|
# define __PRIPTR_PREFIX
|
||||||
|
# endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SIZE_SUFFIX_1LETTER = 0, // 进制单位(保留一个字母,如M)
|
||||||
|
SIZE_SUFFIX_3LETTER = (1 << 0), // 进制单位(保留三个字母,如MiB)
|
||||||
|
SIZE_SUFFIX_SPACE = (1 << 1), // 数据与单位间保留空格
|
||||||
|
SIZE_DECIMAL_2DIGITS = (1 << 2) // 保留两位小数
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int kdkVolumeBaseCharacterConvert(const char* origin_data, KDKVolumeBaseType result_base, char* result_data);
|
||||||
|
int kdkVolumeBaseNumericalConvert(double origin_numerical, KDKVolumeBaseType origin_base, KDKVolumeBaseType result_base, double* result_numerical);
|
||||||
|
|
||||||
|
|
||||||
|
static int do_scale_by_power (uintmax_t *x, int base, int power)
|
||||||
|
{
|
||||||
|
while (power--) {
|
||||||
|
if (UINTMAX_MAX / base < *x)
|
||||||
|
return -ERANGE;
|
||||||
|
*x *= base;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_exp2(uint64_t n, KDKVolumeBaseType base)
|
||||||
|
{
|
||||||
|
int shft;
|
||||||
|
|
||||||
|
for (shft = 10; shft <= base * 10; shft += 10) {
|
||||||
|
if (n < (1ULL << shft))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return shft - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_size(const char *str, uintmax_t *res, int *power)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
char *end;
|
||||||
|
uintmax_t x, frac = 0;
|
||||||
|
int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
|
||||||
|
|
||||||
|
static const char *suf = "KMGTPEZY";
|
||||||
|
static const char *suf2 = "kmgtpezy";
|
||||||
|
const char *sp;
|
||||||
|
|
||||||
|
*res = 0;
|
||||||
|
|
||||||
|
if (!str || !*str) {
|
||||||
|
//rc = -EINVAL;
|
||||||
|
rc = -KDK_INVAILD_ARGUMENT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only positive numbers are acceptable
|
||||||
|
*
|
||||||
|
* Note that this check is not perfect, it would be better to
|
||||||
|
* use lconv->negative_sign. But coreutils use the same solution,
|
||||||
|
* so it's probably good enough...
|
||||||
|
*/
|
||||||
|
p = str;
|
||||||
|
while (isspace((unsigned char) *p))
|
||||||
|
p++;
|
||||||
|
if (*p == '-') {
|
||||||
|
//rc = -EINVAL;
|
||||||
|
rc = -KDK_INVAILD_ARGUMENT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0, end = NULL;
|
||||||
|
x = strtoumax(str, &end, 0); //强制类型转换
|
||||||
|
|
||||||
|
if (end == str ||
|
||||||
|
(errno != 0 && (x == UINTMAX_MAX || x == 0))) {
|
||||||
|
rc = errno ? -errno : -KDK_INVAILD_ARGUMENT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (!end || !*end)
|
||||||
|
goto done; /* without suffix */
|
||||||
|
p = end;
|
||||||
|
|
||||||
|
check_suffix:
|
||||||
|
if (*(p + 1) == 'i' && (*(p + 2) == 'B' || *(p + 2) == 'b') && !*(p + 3))
|
||||||
|
base = 1024; /* XiB, 2^N */
|
||||||
|
else if ((*(p + 1) == 'B' || *(p + 1) == 'b') && !*(p + 2))
|
||||||
|
base = 1024; /* XB, 10^N */
|
||||||
|
// 消除 ‘iB’和‘B’的区别
|
||||||
|
else if (*(p + 1)) {
|
||||||
|
struct lconv const *l = localeconv();
|
||||||
|
const char *dp = l ? l->decimal_point : NULL;
|
||||||
|
size_t dpsz = dp ? strlen(dp) : 0;
|
||||||
|
|
||||||
|
if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
|
||||||
|
const char *fstr = p + dpsz;
|
||||||
|
for (p = fstr; *p == '0'; p++)
|
||||||
|
frac_zeros++;
|
||||||
|
fstr = p;
|
||||||
|
if (isdigit(*fstr)) {
|
||||||
|
errno = 0, end = NULL;
|
||||||
|
frac = strtoumax(fstr, &end, 0);
|
||||||
|
if (end == fstr ||
|
||||||
|
(errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
|
||||||
|
//rc = errno ? -errno : -EINVAL;
|
||||||
|
rc = errno ? -errno : -KDK_INVAILD_ARGUMENT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
end = (char *) p;
|
||||||
|
|
||||||
|
if (frac && (!end || !*end)) {
|
||||||
|
rc = -KDK_INVAILD_ARGUMENT;
|
||||||
|
goto err; /* without suffix, but with frac */
|
||||||
|
}
|
||||||
|
p = end;
|
||||||
|
goto check_suffix;
|
||||||
|
}
|
||||||
|
rc = -KDK_INVAILD_ARGUMENT;
|
||||||
|
goto err; /* unexpected suffix */
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = strchr(suf, *p);
|
||||||
|
if (sp)
|
||||||
|
pwr = (sp - suf) + 1;
|
||||||
|
else {
|
||||||
|
sp = strchr(suf2, *p);
|
||||||
|
if (sp)
|
||||||
|
pwr = (sp - suf2) + 1;
|
||||||
|
else {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc = do_scale_by_power(&x, base, pwr);
|
||||||
|
if (power)
|
||||||
|
*power = pwr;
|
||||||
|
if (frac && pwr) {
|
||||||
|
int i;
|
||||||
|
uintmax_t frac_div = 10, frac_poz = 1, frac_base = 1;
|
||||||
|
|
||||||
|
/* mega, giga, ... */
|
||||||
|
do_scale_by_power(&frac_base, base, pwr);
|
||||||
|
|
||||||
|
/* maximal divisor for last digit (e.g. for 0.05 is
|
||||||
|
* frac_div=100, for 0.054 is frac_div=1000, etc.)
|
||||||
|
*/
|
||||||
|
while (frac_div < frac)
|
||||||
|
frac_div *= 10;
|
||||||
|
|
||||||
|
/* 'frac' is without zeros (5 means 0.5 as well as 0.05) */
|
||||||
|
for (i = 0; i < frac_zeros; i++)
|
||||||
|
frac_div *= 10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go backwardly from last digit and add to result what the
|
||||||
|
* digit represents in the frac_base. For example 0.25G
|
||||||
|
*
|
||||||
|
* 5 means 1GiB / (100/5)
|
||||||
|
* 2 means 1GiB / (10/2)
|
||||||
|
*/
|
||||||
|
|
||||||
|
do {
|
||||||
|
unsigned int seg = frac % 10; /* last digit of the frac */
|
||||||
|
uintmax_t seg_div = frac_div / frac_poz; /* what represents the segment 1000, 100, .. */
|
||||||
|
|
||||||
|
frac /= 10; /* remove last digit from frac */
|
||||||
|
frac_poz *= 10;
|
||||||
|
if (seg)
|
||||||
|
x += frac_base / (seg_div / seg);
|
||||||
|
} while (frac);
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
*res = x;
|
||||||
|
err:
|
||||||
|
if (rc < 0)
|
||||||
|
errno = -rc;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *size_to_human_string(int options, uint64_t bytes, int base)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
int dec, exp;
|
||||||
|
uint64_t frac;
|
||||||
|
const char *letters = "BKMGTPE";
|
||||||
|
char suffix[sizeof(" KiB")], *psuf = suffix;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (options & SIZE_SUFFIX_SPACE)
|
||||||
|
*psuf++ = ' ';
|
||||||
|
|
||||||
|
exp = get_exp2(bytes, base);
|
||||||
|
c = *(letters + (exp ? exp / 10 : 0));
|
||||||
|
dec = exp ? bytes / (1ULL << exp) : bytes;
|
||||||
|
frac = exp ? bytes % (1ULL << exp) : 0;
|
||||||
|
|
||||||
|
*psuf++ = c;
|
||||||
|
|
||||||
|
if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
|
||||||
|
*psuf++ = 'i';
|
||||||
|
*psuf++ = 'B';
|
||||||
|
}
|
||||||
|
|
||||||
|
*psuf = '\0';
|
||||||
|
|
||||||
|
if (frac) {
|
||||||
|
if (options & SIZE_DECIMAL_2DIGITS) {
|
||||||
|
frac = (frac / (1ULL << (exp - 10)) + 5) / 10;
|
||||||
|
if (frac % 10 == 0)
|
||||||
|
frac /= 10; /* convert N.90 to N.9 */
|
||||||
|
} else {
|
||||||
|
frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
|
||||||
|
if (frac == 10)
|
||||||
|
dec++, frac = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frac) {
|
||||||
|
struct lconv const *l = localeconv();
|
||||||
|
char *dp = l ? l->decimal_point : NULL;
|
||||||
|
|
||||||
|
if (!dp || !*dp)
|
||||||
|
dp = ".";
|
||||||
|
snprintf(buf, sizeof(buf), "%d%s%" PRIu64 "%s", dec, dp, frac, suffix);
|
||||||
|
} else
|
||||||
|
snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
|
||||||
|
|
||||||
|
return strdup(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdkVolumeBaseCharacterConvert(const char* origin_data, KDKVolumeBaseType result_base, char* result_data)
|
||||||
|
{
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
uintmax_t basic_data;
|
||||||
|
int power;
|
||||||
|
// 入参检查
|
||||||
|
if (origin_data == NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
syslog(LOG_ERR, "[KYSDK:utils:%s] in parameter error, please check usage!",__FUNCTION__);
|
||||||
|
return KDK_INVAILD_ARGUMENT;
|
||||||
|
}
|
||||||
|
// 出参检查
|
||||||
|
if (result_data == NULL)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "[KYSDK:utils:%s] out parameter error, please check memory application situation!",__FUNCTION__);
|
||||||
|
return KDK_INVAILD_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = parse_size(origin_data, &basic_data, &power);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "[KYSDK:utils:%s] parse origin data failed, please contact developer, origin data = %s, errcode = %d\n", __FUNCTION__, origin_data ,ret);
|
||||||
|
return KDK_INVAILD_DATA_FORMAT;
|
||||||
|
}
|
||||||
|
printf("%lu\n", basic_data);
|
||||||
|
char *temp;
|
||||||
|
temp = size_to_human_string(SIZE_SUFFIX_1LETTER | SIZE_DECIMAL_2DIGITS, basic_data, result_base);
|
||||||
|
strcpy(result_data, temp);
|
||||||
|
if (temp != NULL)
|
||||||
|
free(temp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int kdkVolumeBaseNumericalConvert(double origin_numerical, KDKVolumeBaseType origin_base, KDKVolumeBaseType result_base, double* result_numerical)
|
||||||
|
{
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
//将数据转化构建成字符串
|
||||||
|
char unit[4] = {0};
|
||||||
|
switch (origin_base)
|
||||||
|
{
|
||||||
|
case KDK_KILOBYTE:
|
||||||
|
/* code */
|
||||||
|
strcpy(unit, "KB");
|
||||||
|
break;
|
||||||
|
case KDK_MEGABYTE:
|
||||||
|
strcpy(unit, "MB");
|
||||||
|
break;
|
||||||
|
case KDK_GIGABYTE:
|
||||||
|
strcpy(unit, "GB");
|
||||||
|
break;
|
||||||
|
case KDK_TERABYTE:
|
||||||
|
strcpy(unit, "TB");
|
||||||
|
break;
|
||||||
|
case KDK_PETABYTE:
|
||||||
|
strcpy(unit, "PB");
|
||||||
|
break;
|
||||||
|
case KDK_EXABYTE:
|
||||||
|
strcpy(unit, "EB");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strcpy(unit, "B");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char origin_string[200] = {0};
|
||||||
|
sprintf(origin_string, "%.2f%s",origin_numerical, unit);
|
||||||
|
|
||||||
|
// 后续重复解析过程
|
||||||
|
uintmax_t basic_data;
|
||||||
|
int power;
|
||||||
|
ret = parse_size(origin_string, &basic_data, &power);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "[KYSDK:utils:%s] parse origin data failed, please contact developer, origin data = %s, errcode = %d\n", __FUNCTION__, origin_string ,ret);
|
||||||
|
return KDK_INVAILD_DATA_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *temp;
|
||||||
|
temp = size_to_human_string(SIZE_DECIMAL_2DIGITS, basic_data, result_base);
|
||||||
|
|
||||||
|
*result_numerical = atof(temp);
|
||||||
|
|
||||||
|
if(temp != NULL)
|
||||||
|
free(temp);
|
||||||
|
return ret;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,87 @@
|
||||||
|
/**
|
||||||
|
* @file kyutils.h
|
||||||
|
* @brief
|
||||||
|
* @author liuyang <liuyang@kylinos.cn>
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021-09-01
|
||||||
|
* @example kysdk-base/src/utils/sample/kyutils_sample.c
|
||||||
|
*
|
||||||
|
* @copyright Copyright: 2021,KylinSoft Co.,Ltd.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KYSDK_UTILS_H__
|
||||||
|
#define KYSDK_UTILS_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <bits/types.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup 单位进制转换组件
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** KDKUtilsErrCode */
|
||||||
|
typedef enum{
|
||||||
|
KDK_NOERR, /**< 无异常 */
|
||||||
|
KDK_INVAILD_BASE, /**< 非法进制 */
|
||||||
|
KDK_INVAILD_ARGUMENT, /**< 非法参数 */
|
||||||
|
KDK_INVAILD_DATA_FORMAT, /**< 非法数据格式 */
|
||||||
|
KDK_SYSTEM_UNKOWNERR, /**< 系统运行异常引发的未知错误 */
|
||||||
|
}KDKUtilsErrCode;
|
||||||
|
|
||||||
|
/** KDKVolumeBaseType */
|
||||||
|
typedef enum{
|
||||||
|
KDK_KILOBYTE = 1, /**< KB */
|
||||||
|
KDK_MEGABYTE, /**< MB */
|
||||||
|
KDK_GIGABYTE, /**< GB */
|
||||||
|
KDK_TERABYTE, /**< TB */
|
||||||
|
KDK_PETABYTE, /**< PB */
|
||||||
|
KDK_EXABYTE, /**< EB */
|
||||||
|
}KDKVolumeBaseType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 字符格式单位进制转换
|
||||||
|
* @param[in] origin_data 原始字符类型的数据,以具体进制单位结束,如果没有,缺省为‘B’
|
||||||
|
* @param[in] result_base 期望的结果进制单位
|
||||||
|
* @param[out] result_data 转化进制后的字符数据,带进制单位
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int kdkVolumeBaseCharacterConvert(const char* origin_data, KDKVolumeBaseType result_base, char* result_data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 数字格式单位进制转换
|
||||||
|
* @param[in] origin_numerical 原始数字类型数据
|
||||||
|
* @param[in] origin_base 原始的进制单位
|
||||||
|
* @param[in] result_base 期望的进制单位
|
||||||
|
* @param[out] result_numerical 期望进制下的数字类型数据
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int kdkVolumeBaseNumericalConvert(double origin_numerical, KDKVolumeBaseType origin_base, KDKVolumeBaseType result_base, double* result_numerical);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* @file kyutils_sample.c
|
||||||
|
* @brief
|
||||||
|
* @author Fnoily <liuyang@kylinos.cn>
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021-10-29
|
||||||
|
* Copyright: 2021,KylinSoft Co.,Ltd.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "../kyutils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// 调用字符型数据单位转换接口
|
||||||
|
char origin_data [20] = "10000.24MB";
|
||||||
|
char result_data [50] = {0};
|
||||||
|
ret = kdkVolumeBaseCharacterConvert(origin_data, KDK_GIGABYTE, result_data);
|
||||||
|
printf("%s\n", result_data);
|
||||||
|
|
||||||
|
|
||||||
|
// 调用数字型数据单位转换接口
|
||||||
|
// 此例亦可说明,在低进制不足以向高进制转换时,进制保持不变
|
||||||
|
double origin_numberical = 100.24;
|
||||||
|
double result_numberical;
|
||||||
|
ret = kdkVolumeBaseNumericalConvert(origin_numberical, KDK_MEGABYTE, KDK_GIGABYTE, &result_numberical);
|
||||||
|
printf("%.2f\n", result_numberical);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef KYSDK_BASE_UTILS_MARCOS_H__
|
||||||
|
#define KYSDK_BASE_UTILS_MARCOS_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
GNU Attribute
|
||||||
|
*/
|
||||||
|
#define DEPRECATED __attribute__((deprecated))
|
||||||
|
#define CONSTRUCTOR __attribute__((constructor))
|
||||||
|
#define DESTRUCTOR __attribute__((destructor))
|
||||||
|
#define FORMAT(type, fmtindex, argindex) __attribute__((format(type, fmtindex, argindex)))
|
||||||
|
#define WARNUNSEDRET __attribute__((warn_unused_result))
|
||||||
|
#define WEAK __attribute__((weak))
|
||||||
|
#define ALWAYSINLINE __attribute__((always_inline))
|
||||||
|
#define CHECK_FMT(x,y) __attribute__((format(printf,x,y)))
|
||||||
|
#define NOINLINE __attribute__((noinline))
|
||||||
|
#define NOTNULL(...) __attribute__((nonnull(__VA_ARGS__)))
|
||||||
|
#define MUSTCHECKRESULT __attribute__((warn_unused_result))
|
||||||
|
|
||||||
|
/*
|
||||||
|
Customize
|
||||||
|
*/
|
||||||
|
#define ASSERT_NOT_NULL(x, ret) if (!x) {return ret;}
|
||||||
|
#define SAFE_FREE(x) if (x) {free(x); x = NULL;}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue