From 0b5aa00d6ba04558b37f2b98198aaed727277711 Mon Sep 17 00:00:00 2001 From: icesky Date: Mon, 1 Aug 2016 17:42:36 +0800 Subject: [PATCH] Signed-off-by: icesky --- kmaplib/HashMap_test.c | 53 +++++ kmaplib/List_test.c | 51 +++++ kmaplib/OBJS | 3 +- kmaplib/{HashMap.c => XipHashMap.c} | 318 +++++++++++++++++++------- kmaplib/XipList.c | 331 ++++++++++++++++++++++++++++ kmaplib/hashmap.h | 82 ------- kmaplib/makefile | 30 ++- kmaplib/makefile.osc | 7 + kmaplib/mk | 4 +- kmaplib/test_hash.c | 41 ---- kmaplib/xiphashmap.h | 124 +++++++++++ kmaplib/xiplist.h | 103 +++++++++ 12 files changed, 943 insertions(+), 204 deletions(-) create mode 100644 kmaplib/HashMap_test.c create mode 100644 kmaplib/List_test.c rename kmaplib/{HashMap.c => XipHashMap.c} (57%) create mode 100644 kmaplib/XipList.c delete mode 100644 kmaplib/hashmap.h create mode 100644 kmaplib/makefile.osc delete mode 100644 kmaplib/test_hash.c create mode 100644 kmaplib/xiphashmap.h create mode 100644 kmaplib/xiplist.h diff --git a/kmaplib/HashMap_test.c b/kmaplib/HashMap_test.c new file mode 100644 index 0000000..6219864 --- /dev/null +++ b/kmaplib/HashMap_test.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +static void name_print( int idx, char * key, void * value); + +int main() +{ + char *name[] = { + "chenmanwen", "yangqinghua", "chenxin", "yanzi", "yangjian", "zhangrenfang", + "panzi", "zhangqiang", "webssky", "jcseg", "friso", "robbe", "lionsoul", + "tankwar", "jteach" + }; + + void * hashmap = XipHashmapNew(); + /* + void * hashmap = XipHashmapInit(0, 0.00, 1); + */ + + int i = 0; + int length = 15; + char tmpname[51]; + for ( i = 0; i < length ; i++) + { + strcpy( tmpname, name[i]); + printf("put(%15s, %15s);\n", tmpname, tmpname); + XipHashmapPut( hashmap, tmpname, tmpname, strlen(tmpname)+1); + } + + XipHashmapPrint(hashmap, NULL); + XipHashmapPut( hashmap, "panzi", "iceskyiceskyicesky", strlen("iceskyiceskyicesky")+1); + XipHashmapPrint(hashmap, name_print); + + for ( i = 0; i< length; i++) + printf("get(%15s): %15s\n", name[i], (char *)XipHashmapGet(hashmap, name[i])); + + XipHashmapPrint(hashmap, NULL); + + printf("remove(%15s): %d\n", "lionsoul", XipHashmapRemove(hashmap, "lionsoul")); + + XipHashmapPrint(hashmap, NULL); + + XipHashmapDestory(hashmap); + + return 0; +} + +static void name_print( int idx, char * key, void * value) +{ + printf("aaaaaaaaaaaaa-idx[%d], key[%s], value[%s]\n", idx, key, value); + return; +} diff --git a/kmaplib/List_test.c b/kmaplib/List_test.c new file mode 100644 index 0000000..3bc7d3e --- /dev/null +++ b/kmaplib/List_test.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +void name_printf( void * element); +int main() +{ + char *name[] = { + "chenmanwen", "yangqinghua", "chenxin", "yanzi", "yangjian", "zhangrenfang", + "panzi", "zhangqiang", "webssky", "jcseg", "friso", "robbe", "lionsoul", + "tankwar", "jteach" + }; + + char tmp_name[51]; + + void * list = XipListNew(); + /* + void * list = XipListInit(1); + */ + int ret = 0; + + int i = 0; + int length = 15; + for ( i = 0; i < length ; i++) + { + strcpy( tmp_name, name[i]); + printf("Add(value:[%s]--[%s]);\n", name[i], XipListAdd( list, tmp_name,strlen(tmp_name)+1)); + } + printf("Len--[%d],threshold[%d]\n", XipListLen(list), XipListThreshold(list)); + + XipListPrint(list, name_printf); + XipListPrint(list, NULL); + + for ( i = 0; i< length; i++) + printf("get(%d): [%s], [%x]\n", i, (char *)XipListGet(list, i), XipListGet(list, i)); + + XipListDestory(list); + list = NULL; + + XipListPrint(list, name_printf); + + return 0; +} + +void name_printf( void * element) +{ + printf("aaaaaaaaaaa---[%s]\n", element); +} + + diff --git a/kmaplib/OBJS b/kmaplib/OBJS index 5ae14b1..dc6df2c 100644 --- a/kmaplib/OBJS +++ b/kmaplib/OBJS @@ -1,3 +1,4 @@ TARGET = libkmaplib.so -OBJ += HashMap.o +OBJ += XipHashMap.o +OBJ += XipList.o diff --git a/kmaplib/HashMap.c b/kmaplib/XipHashMap.c similarity index 57% rename from kmaplib/HashMap.c rename to kmaplib/XipHashMap.c index e776434..03c3e15 100644 --- a/kmaplib/HashMap.c +++ b/kmaplib/XipHashMap.c @@ -1,5 +1,5 @@ /************************************************** - * 程序名称 : HashMap.c + * 程序名称 : XipHashMap.c * 程序作者 : icesky * 程序版本 : 1.0.0 * 创建日期 : 2016.07.28 @@ -13,6 +13,7 @@ * 5.删除 * 6.判断是否存在 * 7.打印 + * 注意事项: * * 修改记录 : *************************************************/ @@ -20,16 +21,17 @@ #include #include #include -#include "hashmap.h" +#include "xiphashmap.h" #define hmap_malloc(size) malloc(size) #define HMAPFREE(x) free(x);x=NULL; -#define XIP_HASHMAP_REBUILD_ERR (void *)-1 #define MAXIMUM_CAPACITY 1 << 30 /*1 073 741 824 */ #define DEFAULT_CAPACITY 1 << 4 /*16*/ #define DEFAULT_FACTOR 0.75f #define INTER_MAX_VALUE 1<<31 /*int的最大值*/ +#define MALLOC_FLAG_NO 1 /*不分配内存*/ + /*hashmap node结构 */ typedef struct st_xip_hashmap_node @@ -51,6 +53,7 @@ typedef struct unsigned int length; /*桶大小*/ unsigned int size; /*实际大小*/ unsigned int threshold; /*加载临界值*/ + int malloc_flag; /*是否为value分配内存*/ float factor; /*加载因子,默认为0.75(0-1)*/ } TxipHashmap; @@ -78,20 +81,20 @@ static void AppLog(char *file, long line, char *level, char *fmtstr, ...) /*新建T表*/ static TxipHashmapNode ** create_T_table( int opacity); /*新建hashmap节点*/ -static TxipHashmapNode * create_hashmap_node( char * key, void * value, TxipHashmapNode * next); +static TxipHashmapNode * create_hashmap_node( char * key, void * value, TxipHashmapNode * next, int malloc_flag, int size); /*重建hashmap*/ static int rebuild_hash( TxipHashmap * hashmap); /*hash算法*/ static unsigned int XipHash(char * key); -/********************************************************************* - * 入口参数: hashmap的无类型指针 - * 出口参数: 无 - * 返回值 : int 永远返回0,可以不判断 +/******************************************************************** + * 函数名称: XipHashmapPrint * 函数功能: 打印hashmap中所有的元素,主要用于调试和检查散列分布 - * + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ -int XipHashmapPrint( void * hashmap) +int XipHashmapPrint( void * hashmap, void (*ref_func)(int,char *, void *)) { TxipHashmap * map = ( TxipHashmap *)hashmap; @@ -103,13 +106,22 @@ int XipHashmapPrint( void * hashmap) /** 打印日志 **/ if( map != NULL) { - HMAPLOG("I", "HashMap:length(%d),size(%d),threshold(%d)", map->length, map->size, map->threshold); + HMAPLOG("I", "HashMap:length(%d),size(%d),threshold(%d),malloc_flag(%d)", + map->length, map->size, map->threshold, map->malloc_flag); for ( idx = 0; idx < map->length; idx++) { for( e = map->table[idx]; e != NULL; ) { next = e->next; - HMAPLOG("I", "Node[%d]:hash[%d],key[%s],value[%s]", idx, e->hash, e->key, e->value.ptr); + if( ref_func == NULL) + { + HMAPLOG("I", "Node[%d]:hash[%d],key[%s],value[%x][%s]", + idx, e->hash, e->key, e->value.ptr, (char *)e->value.ptr); + } + else + { + ref_func(idx, e->key, e->value.ptr); + } e = next; } } @@ -135,29 +147,34 @@ int XipHashmapPrint( void * hashmap) return 0; } -/********************************************************************* - * 入口参数: 无 - * 出口参数: 无 - * 返回值 : 创建的hashmap的指针,强制转换成了void *类型,方便调用 - * 函数功能: 以默认方式创建一个hashmap,默认的初始容量是16,加载因子是0.75f - * + /******************************************************************** + * 函数名称: XipHashmapNew + * 函数功能: 以所有的默认参数,创建一个hashmap + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ void * XipHashmapNew() { - return XipHashmapInit( 0, 0.00); + return XipHashmapInit( 0, 0.00, 0); /*全默认选项*/ } -/********************************************************************* - * 入口参数: int opacity 初始容量, float factor 加载因子 - * 出口参数: 无 - * 返回值 : 创建的hashmap的指针,强制转换成了void *类型,方便调用 - * 函数功能: 按照传入的信息进行初始hashmap创建, - * 如果opacity为0,则为默认值16,如果factor为0,则默认为0.75f - * +/******************************************************************** + * 函数名称: XipHashmapInit + * 函数功能: 指定初始容量,加载因子和value存储方式,创建一个hashmap + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ -void * XipHashmapInit( int opacity, float factor) +void * XipHashmapInit( int opacity, float factor, int malloc_flag) { TxipHashmap * map = ( TxipHashmap *)hmap_malloc(sizeof( TxipHashmap)); + if( map == NULL) + { + HMAPLOG("E","初始化hashmap内存失败!!!"); + return NULL; + } + memset( map, 0x00, sizeof(TxipHashmap)); if ( opacity < DEFAULT_CAPACITY ) opacity = DEFAULT_CAPACITY; @@ -194,21 +211,22 @@ void * XipHashmapInit( int opacity, float factor) map->size = 0; map->factor = factor; map->threshold = (unsigned int)( factor * opacity); + map->malloc_flag = malloc_flag; return (void *)map; } - -/********************************************************************* - * 入口参数: hashmap指针 - * 出口参数: 无 - * 返回值 : int , 永远返回0可以不判断 - * 函数功能: 按照传入的信息进行初始hashmap创建, - * 如果opacity为0,则为默认值16,如果factor为0,则默认为0.75f - * + + /******************************************************************** + * 函数名称: XipHashmapDestory + * 函数功能: 释放hashmap的内存资源 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapDestory( void * hashmap) { TxipHashmap * map = (TxipHashmap *) hashmap; + TxipHashmapNode * e = NULL; TxipHashmapNode * next = NULL; register unsigned int idx; @@ -216,39 +234,72 @@ int XipHashmapDestory( void * hashmap) /** 释放hashmap node 内存 **/ if( map != NULL) { + /** 释放hashmap的node空间 **/ for ( idx = 0; idx < map->length; idx++) { for( e = map->table[idx]; e != NULL; ) { next = e->next; - HMAPFREE(e); + /* 释放node的value空间*/ + if( map->malloc_flag == MALLOC_FLAG_NO) + { + HMAPFREE(e); + } + else + { + HMAPFREE(e->value.ptr); + e->value.ptr = NULL; + } e = next; } } - /** 释放hashmap的T表 **/ HMAPFREE( map->table); map->table = NULL; + + /** 释放hashmap本身的空间 **/ + HMAPFREE( map); + map = NULL; } return 0; } -/********************************************************************* - * 入口参数: hashmap指针, char * key, void * value - * 出口参数: 无 - * 返回值 : 返回 void * oldvalue 的指针,如果为新增则返回NULL - * 如果put失败则返回XIP_HASHMAP_PUT_ERR - * 函数功能: 根据key和value放入到hashmap中,如果map中已经存在该key的值 - * 则替换成最新的value,同时返回旧value指针 - * 如果put之后,达到了临界值,则重新创建hashmap + /******************************************************************** + * 函数名称: XipHashmapDestory + * 函数功能: 将key和value放入hashmap中,如果key已存在,相当于Set命令 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ -void * XipHashmapPut( void * hashmap, char * key, void * value) +void * XipHashmapPut( void * hashmap, char * key, void * value, int size) { TxipHashmap * map = (TxipHashmap *)hashmap; + if( map == NULL) + { + HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); + return NULL; + } + + if ( key == NULL || strlen(key) == 0) + { + HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); + return NULL; + } + if ( value == NULL) + { + HMAPLOG("E","value不可以为空(NULL)"); + return NULL; + } + if ( size <= 0) + { + HMAPLOG("E","值的大小size不可以为0"); + return NULL; + } + void * oldvalue = NULL; TxipHashmapNode *e = NULL; unsigned int hcode = XipHash(key); @@ -261,17 +312,34 @@ void * XipHashmapPut( void * hashmap, char * key, void * value) if ( hcode == e->hash && (key == e->key || strcmp( key, e->key) == 0)) { oldvalue = e->value.ptr; - e->value.ptr = value; + if( map->malloc_flag == 1) /*不分配内存*/ + { + e->value.ptr = value; + } + else + { + HMAPFREE(e->value.ptr); + e->value.ptr = NULL; + e->value.ptr = hmap_malloc(size); + if( e->value.ptr == NULL) + { + HMAPLOG("E","为value分配内存时异常!!!"); + return NULL; + } + memset( e->value.ptr, 0x00, size); + memcpy( e->value.ptr, value, size); + return e->value.ptr; + } return oldvalue; /*将原value返回给调用者*/ } } /*新建node*/ - map->table[idx] = create_hashmap_node( key, value, map->table[idx]); + map->table[idx] = create_hashmap_node( key, value, map->table[idx], map->malloc_flag, size); if( map->table[idx] == NULL) { - HMAPLOG("E","新建hashnode节点异常!!!"); - return XIP_HASHMAP_PUT_ERR; /*返回固定值*/ + HMAPLOG("E","新建hashnode节点异常malloc_flag[%d],size[%d]!!!", map->malloc_flag, size); + return NULL; /*返回固定值*/ } /*修改hashmap*/ @@ -285,22 +353,35 @@ void * XipHashmapPut( void * hashmap, char * key, void * value) if( rebuild_hash(map) != 0) { HMAPLOG("E","重建hash表错误!!!"); - return XIP_HASHMAP_PUT_ERR; /*返回固定值*/ + return NULL; /*返回固定值*/ } } - return NULL; + return map->table[idx]->value.ptr; /*返回内存地址*/ } -/********************************************************************* - * 入口参数: hashmap指针, char * key - * 出口参数: 无 - * 返回值 : 返回 void * value 的指针 - * 函数功能: 根据key值从hashmap中取得value的指针返回 + /******************************************************************** + * 函数名称: XipHashmapGet + * 函数功能: 根据Key值获取对应的value值 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ void * XipHashmapGet( void * hashmap, char *key) { TxipHashmap * map = (TxipHashmap *) hashmap; + if( map == NULL) + { + HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); + return NULL; + } + + if ( key == NULL || strlen(key) == 0) + { + HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); + return NULL; + } + unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; @@ -317,15 +398,27 @@ void * XipHashmapGet( void * hashmap, char *key) return NULL; } -/********************************************************************* - * 入口参数: hashmap指针, char * key - * 出口参数: 无 - * 返回值 : int 返回值XIP_HASHMAP_EXIST_TURE(1), XIP_HASHMAP_EXIST_FALSE(0) - * 函数功能: 根据key值从hashmap中查找是否存在,存在返回真,不存在返回假 + /******************************************************************** + * 函数名称: XipHashmapExists + * 函数功能: 判断Key是否在hashmap中存在 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapExists( void * hashmap, char *key) { TxipHashmap * map = (TxipHashmap *) hashmap; + if( map == NULL) + { + HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); + return -5; + } + if ( key == NULL || strlen(key) == 0) + { + HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); + return -10; + } + unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; @@ -343,20 +436,30 @@ int XipHashmapExists( void * hashmap, char *key) return 0; } -/********************************************************************* - * 入口参数: hashmap指针, char * key - * 出口参数: 无 - * 返回值 : void * value - * 函数功能: 根据key值从hashmap中删除key对应的node节点,如果删除成功, - * 则返回删除节点的value的地址,未找到节点则返回NULL + /******************************************************************** + * 函数名称: XipHashmapDestory + * 函数功能: 删除map中对应的Key和VALUE的节点 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ -void * XipHashmapRemove( void * hashmap, char *key) +int XipHashmapRemove( void * hashmap, char *key) { TxipHashmap * map = (TxipHashmap *) hashmap; + if( map == NULL) + { + HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); + return -5; + } + if ( key == NULL || strlen(key) == 0) + { + HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); + return -10; + } + unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; - void * oldvalue = NULL; TxipHashmapNode * e = NULL; TxipHashmapNode * prev = NULL; @@ -370,17 +473,32 @@ void * XipHashmapRemove( void * hashmap, char *key) else prev->next = e->next; - oldvalue = e->value.ptr; + if( map->malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ + { + ; + } + else + { + HMAPFREE(e->value.ptr); + e->value.ptr = NULL; + } HMAPFREE(e); map->size--; - return oldvalue; + return 0; } } - return NULL; + return 0; } +/******************************************************************** + * 函数名称: create_T_table + * 函数功能: 申请指向node的连续内存指针,在《算法导论》关于散列表中,所处位置为T表 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 内部调用函数.注意返回值是指向TxipHashmapNode的指针的指针 + *********************************************************************/ static TxipHashmapNode ** create_T_table( int opacity) { register unsigned int i=0; @@ -390,6 +508,7 @@ static TxipHashmapNode ** create_T_table( int opacity) { return NULL; } + memset( table, 0x00, sizeof(TxipHashmapNode*) * opacity); /*初始化*/ for ( i = 0; i < opacity; i++) @@ -398,20 +517,60 @@ static TxipHashmapNode ** create_T_table( int opacity) return table; } -static TxipHashmapNode * create_hashmap_node( char * key, void * value, TxipHashmapNode * next) +/******************************************************************** + * 函数名称: create_hashmap_node + * 函数功能: 创建具体的key,value的node节点 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 内部调用函数. 如果malloc_flag是1的话,则size无效,因为不需要 + 为value申请内存。 + *********************************************************************/ +static TxipHashmapNode * create_hashmap_node( char * key, void * value, TxipHashmapNode * next, int malloc_flag, int size) { TxipHashmapNode * node = ( TxipHashmapNode *) hmap_malloc( sizeof(TxipHashmapNode)); if ( node == NULL) { return NULL; } + memset( node, 0x00, sizeof( TxipHashmapNode)); - node->key = key; - node->value.ptr = value; + if( malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ + { + node->key = key; + node->value.ptr = value; + } + else + { + /*限定KEY必须是字符串类型*/ + node->key = hmap_malloc(strlen(key)+1); + if( node->key == NULL) + { + return NULL; + } + memset(node->key, 0x00, strlen(key)+1); + memcpy( node->key, key, strlen(key)+1); + + node->value.ptr = hmap_malloc(size); + if( node->value.ptr == NULL) + { + return NULL; + } + memset(node->value.ptr, 0x00, size); + memcpy( node->value.ptr, value, size); + } node->next = next; return node; } + +/******************************************************************** + * 函数名称: rebuild_hash + * 函数功能: 当前hashmap中的size达到了临界值的时候,需要重新创建更大的hashmap + 来进行存储。 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 内部调用函数. 注意重建失败了要判断返回值 + *********************************************************************/ static int rebuild_hash( TxipHashmap * map) { register unsigned int i = 0; @@ -469,10 +628,15 @@ static int rebuild_hash( TxipHashmap * map) } /******************************************************** + * 函数名称: XipHash + * 函数功能: 通过key来计算hash值的算法,算法越好,散列的一致分布性越好 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 内部调用函数. * hash算法,此处使用的是 simple BKDR hash algorithm * 为JAVA的JDK中使用的算法 * 也可以使用PHP等使用的time33算法(DJP hash algorithm) - * 还可以使用暴雪公司的的One-Way-Hash,号称最快的hash算法 + * 还可以使用暴雪公司的的One-Way-Hash(号称最快的hash算法) * * 之所以没有使用我推荐的算法,是因为我把机会留给了你.如果 * 你看到了这个地方,并且试图优化算法的话,可以使用我建议的 diff --git a/kmaplib/XipList.c b/kmaplib/XipList.c new file mode 100644 index 0000000..af6cb8d --- /dev/null +++ b/kmaplib/XipList.c @@ -0,0 +1,331 @@ +/************************************************** + * 程序名称 : List.c + * 程序作者 : icesky + * 程序版本 : 1.0.0 + * 创建日期 : 2016.07.29 + * 程序功能 : + * 参考java的List实现机制,实现了C基本的 + * list实现,支持几种功能: + * 1.新建 + * 2.销毁 + * 3:.加入 + * 4.获取 + * 5.打印 + * 注意事项 : + * + * 修改记录 : + *************************************************/ +#include +#include +#include +#include +#include "xiplist.h" + +#define list_malloc(size) malloc(size) +#define LISTFREE(x) free(x);x=NULL; +#define XIP_LIST_DEFAULT_THRESHOLD 1<<4 +#define XIP_LIST_MAX_THRESHOLD 1<<30 +#define MALLOC_FLAG_NO 1 /*不分配内存*/ + +/*list 结构*/ +typedef struct +{ + unsigned int length; /*实际大小*/ + unsigned int threshold; /*临界值*/ + int malloc_flag; /*不分配内存*/ + void ** ele_table; /*element数组*/ + +} TxipList; + +#define LISTLOG(...) AppLog(__FILE__,__LINE__,__VA_ARGS__) +static void AppLog(char *file, long line, char *level, char *fmtstr, ...) +{ + va_list ap; + char tmpstr[501]; + + memset(tmpstr, 0x0, sizeof(tmpstr)); + + va_start(ap, fmtstr); + + vsprintf(tmpstr, fmtstr, ap); + va_end(ap); + + printf("[%s][%s][%03ld]", level, file, line); + printf("[%s]\n", tmpstr); + + return ; +} + +/*** static 声明 ***/ +/*重建ele_table表*/ +static int rebuild_list( TxipList * lst); + +/******************************************************************** + * 函数名称: XipListPrint + * 函数功能: 打印List中所有的元素,只能显示出字符串的值 + * 函数作者: icesky + * 创建日期: 2016.07.29 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +int XipListPrint( void * list, void (*ref_func)(void *)) +{ + TxipList * lst = ( TxipList *)list; + + unsigned int idx = 0; + + /** 打印日志 **/ + if( lst != NULL) + { + for ( idx = 0; idx < lst->length; idx++) + { + if( ref_func == NULL) + { + LISTLOG("I", "Length[%d], Element[%d]:[%x][%s]", + lst->length, idx, lst->ele_table[idx], (char * )lst->ele_table[idx]); + } + else + { + ref_func(lst->ele_table[idx]); + } + } + } + + return 0; +} + + /******************************************************************** + * 函数名称: XipListNew + * 函数功能: 以所有的默认参数,创建一个list + * 函数作者: icesky + * 创建日期: 2016.07.29 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +void * XipListNew() +{ + return XipListInit(0); +} + +/******************************************************************** + * 函数名称: XipListInit + * 函数功能: 指定value存储方式,创建一个list + * 函数作者: icesky + * 创建日期: 2016.07.29 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +void * XipListInit(int malloc_flag) +{ + TxipList * lst = ( TxipList *)list_malloc(sizeof( TxipList)); + memset( lst, 0x00, sizeof( TxipList)); + + lst->length = 0; + lst->threshold = XIP_LIST_DEFAULT_THRESHOLD; + lst->ele_table = (void * *)list_malloc(sizeof(void *) * lst->threshold); + lst->malloc_flag = malloc_flag; + memset( lst->ele_table, 0x00, sizeof(void *)* lst->threshold); + + return (void *)lst; +} + + /******************************************************************** + * 函数名称: XipListDestory + * 函数功能: 释放list的内存资源 + * 函数作者: icesky + * 创建日期: 2016.07.29 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +int XipListDestory( void * list) +{ + TxipList * lst = (TxipList *) list; + register unsigned int idx; + + /** 释放list element 内存 **/ + if( lst != NULL) + { + if( lst->malloc_flag != MALLOC_FLAG_NO) /*不分配内存*/ + { + for ( idx = 0; idx < lst->length; idx++) + { + /* 释放element 空间*/ + LISTFREE(lst->ele_table[idx]); + lst->ele_table[idx] = NULL; + + } + } + + /* 释放指向element table的空间 */ + LISTFREE(lst->ele_table); + lst->ele_table = NULL; + + /** 释放List空间 **/ + LISTFREE( lst); + lst = NULL; + } + + + return 0; +} + +/******************************************************************** + * 函数名称: XipListAdd + * 函数功能: 将element放入list中 + * 函数作者: icesky + * 创建日期: 2016.07.29 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +void * XipListAdd( void * list, void * element, unsigned int size) +{ + TxipList * lst = (TxipList *)list; + void * e = NULL; + + if( lst == NULL) + { + LISTLOG("E","list内存未初始化,请建调用创建函数!!"); + return NULL; + } + + e = lst->ele_table[lst->length]; + if( e != NULL) + { + LISTLOG("E","要放入的指针不为空,请检查列表状态![%x],长度[%d],临界值[%d]", e, lst->length, lst->threshold); + return NULL; + } + + if( element == NULL) + { + LISTLOG("E","要放入的element不能为空!!!"); + return NULL; + } + + if( lst->malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ + { + lst->ele_table[lst->length] = element; + } + else + { + /*新建element->value*/ + e = (void *)list_malloc(size); + if( e == NULL) + { + LISTLOG("E","分配value内存失败!!!"); + return NULL; + } + memset( e, 0x00, size); + memcpy( e, element, size); + lst->ele_table[lst->length] = e; + } + + + lst->length++; /*增加当前长度*/ + + /*如果触发临界值,则重建ele_table*/ + if( lst->length >= lst->threshold) + { + if( rebuild_list(lst) != 0) + { + LISTLOG("E","重建list错误!!!"); + return NULL; + } + } + + return lst->ele_table[lst->length]; +} + +/******************************************************************** + * 函数名称: XipListGet + * 函数功能: 根据顺序号idx,获取List中的值 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +void * XipListGet( void * list, int idx) +{ + TxipList * lst = (TxipList *) list; + + if( lst != NULL) + { + if( idx <= lst->length) + { + return lst->ele_table[idx]; + } + } + + return NULL; +} +/******************************************************************** + * 函数名称: XipListLen + * 函数功能: 查看当前list的大小 + * 函数作者: icesky + * 创建日期: 2016.08.01 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +int XipListLen( void * list) +{ + TxipList * lst = (TxipList *) list; + if( lst != NULL) + { + return lst->length; + } + + return 0; +} +/******************************************************************** + * 函数名称: XipListThreshold + * 函数功能: 查看的当前临界值 + * 函数作者: icesky + * 创建日期: 2016.07.28 + * 调用说明: 外部调用函数,参见xiplist.h中描述 + *********************************************************************/ +int XipListThreshold( void * list) +{ + TxipList * lst = (TxipList *) list; + + if( lst != NULL) + { + return lst->threshold; + } + return 0; +} +/******************************************************************** + * 函数名称: rebuild_list + * 函数功能: 当前list中的length达到了临界值的时候,需要重新创建更大的listtable + 来进行存储。 + * 函数作者: icesky + * 创建日期: 2016.07.29 + * 调用说明: 内部调用函数. 注意重建失败了要判断返回值 + *********************************************************************/ +static int rebuild_list( TxipList * lst) +{ + register unsigned int i = 0; + void * * newtable=NULL; + + /*如果达到最大了,则不在rebuild*/ + if( lst->threshold == XIP_LIST_MAX_THRESHOLD) + { + LISTLOG("E","已达到list最大值[%d]", lst->threshold); + return -5; + } + + /*扩容*/ + unsigned int threshold = lst->threshold * 2; + if( threshold > XIP_LIST_MAX_THRESHOLD) + threshold = XIP_LIST_MAX_THRESHOLD; + + /*创建新的ele_table*/ + newtable = (void * *)list_malloc(sizeof(void *) * threshold); + + /*赋值和转移list->ele_table*/ + for( i = 0; i < lst->length; i++) + { + /*遍历*/ + newtable[i] = lst->ele_table[i]; + } + + /*释放oldtable*/ + LISTFREE(lst->ele_table); + + lst->ele_table = newtable; + lst->threshold = threshold; + + return 0; +} + diff --git a/kmaplib/hashmap.h b/kmaplib/hashmap.h deleted file mode 100644 index cbeddc0..0000000 --- a/kmaplib/hashmap.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __XIPHASHMAP__ -#define __XIPHASHMAP__ - -#define XIP_HASHMAP_EXIST_TURE 1 /*存在- 真*/ -#define XIP_HASHMAP_EXIST_FALSE 0 /*不存在- 假*/ -#define XIP_HASHMAP_PUT_ERR (void *)1 /*put异常*/ - -/********************************************************************* - * 入口参数: hashmap的无类型指针 - * 出口参数: 无 - * 返回值 : int 永远返回0,可以不判断 - * 函数功能: 打印hashmap中所有的元素,主要用于调试和检查散列分布 - * - *********************************************************************/ -int XipHashmapPrint( void * hashmap); - -/********************************************************************* - * 入口参数: 无 - * 出口参数: 无 - * 返回值 : 创建的hashmap的指针,强制转换成了void *类型,方便调用 - * 函数功能: 以默认方式创建一个hashmap,默认的初始容量是16,加载因子是0.75f - * - *********************************************************************/ -void * XipHashmapNew(); - -/********************************************************************* - * 入口参数: int opacity 初始容量, float factor 加载因子 - * 出口参数: 无 - * 返回值 : 创建的hashmap的指针,强制转换成了void *类型,方便调用 - * 函数功能: 按照传入的信息进行初始hashmap创建, - * 如果opacity为0,则为默认值16,如果factor为0,则默认为0.75f - * - *********************************************************************/ -void * XipHashmapInit( int opacity , float factor); - -/********************************************************************* - * 入口参数: hashmap指针 - * 出口参数: 无 - * 返回值 : int , 永远返回0可以不判断 - * 函数功能: 按照传入的信息进行初始hashmap创建, - * 如果opacity为0,则为默认值16,如果factor为0,则默认为0.75f - * - *********************************************************************/ -int XipHashmapDestory( void * in_map); - -/********************************************************************* - * 入口参数: hashmap指针, char * key, void * value - * 出口参数: 无 - * 返回值 : 返回 void * oldvalue 的指针,如果为新增则返回NULL - * 如果put失败则返回XIP_HASHMAP_PUT_ERR - * 函数功能: 根据key和value放入到hashmap中,如果map中已经存在该key的值 - * 则替换成最新的value,同时返回旧value指针 - * 如果put之后,达到了临界值,则重新创建hashmap - *********************************************************************/ -void * XipHashmapPut( void * in_map, char * key, void * value); - -/********************************************************************* - * 入口参数: hashmap指针, char * key - * 出口参数: 无 - * 返回值 : 返回 void * value 的指针 - * 函数功能: 根据key值从hashmap中取得value的指针返回 - *********************************************************************/ -void * XipHashmapGet( void * TxipHashmap, char * key); - -/********************************************************************* - * 入口参数: hashmap指针, char * key - * 出口参数: 无 - * 返回值 : int 返回值XIP_HASHMAP_EXIST_TURE(1), XIP_HASHMAP_EXIST_FALSE(0) - * 函数功能: 根据key值从hashmap中查找是否存在,存在返回真,不存在返回假 - *********************************************************************/ -int XipHashmapExists( void * TxipHashmap, char * key); - -/********************************************************************* - * 入口参数: hashmap指针, char * key - * 出口参数: 无 - * 返回值 : void * value - * 函数功能: 根据key值从hashmap中删除key对应的node节点,如果删除成功, - * 则返回删除节点的value的地址,未找到节点则返回NULL - *********************************************************************/ -void * XipHashmapRemove( void * TxipHashmap, char * key); - -#endif diff --git a/kmaplib/makefile b/kmaplib/makefile index b862ea6..bd83130 100644 --- a/kmaplib/makefile +++ b/kmaplib/makefile @@ -1,7 +1,33 @@ +# +# NAME: makefile +# AUTHOR: yangwanchun +# VERSION: 1.0 +# DATE: 2007/08/25 +# + +include $(HOME)/etc/makefile.xip + +CMP = $(CC) $(SOFLAGS) + +LIBS = + include OBJS -$(TARGET):$(OBJ) - cc -shared -o $@ $< +all : $(TARGET) clean + +$(TARGET) : $(OBJ) + $(CMP) $@ $(OBJ) -L$(APLIBPATH) $(LIBS) + #$(MV) $@ $(APLIBPATH)/ + +.SUFFIXES:.ec + +.ec.o : + $(RM) -f $*.c + $(EC) -I$(INCLPATH) $(ECFLAGS) -L$(APLIBPATH) $< + $(RM) -f $*.c + +.c.o: + $(CC) -I$(INCLPATH) $(CCFLAGS) -L$(APLIBPATH) $< clean: rm -f *.o diff --git a/kmaplib/makefile.osc b/kmaplib/makefile.osc new file mode 100644 index 0000000..b862ea6 --- /dev/null +++ b/kmaplib/makefile.osc @@ -0,0 +1,7 @@ +include OBJS + +$(TARGET):$(OBJ) + cc -shared -o $@ $< + +clean: + rm -f *.o diff --git a/kmaplib/mk b/kmaplib/mk index 270ae4e..ef5bdc6 100644 --- a/kmaplib/mk +++ b/kmaplib/mk @@ -1 +1,3 @@ -cc -o test_hash test_hash.c -I./ ./libkmaplib.so +rm -f HashMap_test List_test +cc -o HashMap_test HashMap_test.c -I./ ./libkmaplib.so +cc -o List_test List_test.c -I./ ./libkmaplib.so diff --git a/kmaplib/test_hash.c b/kmaplib/test_hash.c deleted file mode 100644 index bd78429..0000000 --- a/kmaplib/test_hash.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include - -int main() -{ - char *name[] = { - "chenmanwen", "yangqinghua", "chenxin", "yanzi", "yangjian", "zhangrenfang", - "panzi", "zhangqiang", "webssky", "jcseg", "friso", "robbe", "lionsoul", - "tankwar", "jteach" - }; - - void * hashmap = XipHashmapNew(); - - int i = 0; - int length = 15; - for ( i = 0; i < length ; i++) - { - printf("put(%15s, %15s);\n", name[i], name[i]); - XipHashmapPut( hashmap, name[i], name[i]); - } - - XipHashmapPrint(hashmap); - XipHashmapPut( hashmap, "panzi", "iceskyiceskyicesky"); - XipHashmapPrint(hashmap); - - for ( i = 0; i< length; i++) - printf("get(%15s): %15s\n", name[i], (char *)XipHashmapGet(hashmap, name[i])); - - XipHashmapPrint(hashmap); - - printf("remove(%15s): %15s\n", "lionsoul", (char *)XipHashmapRemove(hashmap, "lionsoul")); - - XipHashmapPrint(hashmap); - - XipHashmapDestory(hashmap); - - return 0; -} - diff --git a/kmaplib/xiphashmap.h b/kmaplib/xiphashmap.h new file mode 100644 index 0000000..9293ddd --- /dev/null +++ b/kmaplib/xiphashmap.h @@ -0,0 +1,124 @@ +#ifndef __XIPHASHMAP__ +#define __XIPHASHMAP__ + +/********************************************************************* + * 函数功能: 以默认方式创建一个hashmap + * 入口参数: 无 + * 出口参数: 无 + * 返回值 : 成功,创建的hashmap的指针,强制转换成了void *类型,需要外部void*指针 + 失败,返回NULL + * 调用说明: 以默认的方式创建的hashmap,默认值为XipHashmapInit的默认值 + 也就说XipHashmapInit的所有参数都传递为0 + 大部分情况都可以使用该函数进行一个新的hashmap的创建,如果想进行 + 特殊功能,比如不分配内存,或者修改初始容量之类的,则可以调用 + XipHashmapInit, 该函数也对外暴露。 + *********************************************************************/ +void * XipHashmapNew(); + +/********************************************************************* + * 函数功能: 创建一个hashmap,有更多的参数供特殊情况使用 + * 入口参数: int opacity 初始容量, 默认为16 + * float factor 加载因子, 一般为0.75f,不建议修改 + * int malloc_flag 是否分配内存给value: + 1不分配,0和其他分配,具体参见XipHashmapPut的说明 + * 出口参数: 无 + * 返回值 : 成功,创建的hashmap的指针,强制转换成了void *类型,方便调用 + 失败,返回NULL + * 调用说明: 按照传入的信息进行定制的初始hashmap创建, + * 如果opacity为0,则为默认值16,如果factor为0,则默认为0.75f + * 如果你没有特殊的要求或特殊的场景,建议更多的使用XipHashmapNew + *********************************************************************/ +void * XipHashmapInit( int opacity , float factor, int malloc_flag); + +/********************************************************************* + * 函数功能: 销毁和释放hashmap,以及它所占用的空间 + * 入口参数: hashmap指针 + * 出口参数: 无 + * 返回值 : 永远返回0 + * 调用说明: 销毁hashmap结构和占用的空间,该函数可以多次调用. + * 特别注意: 调用该函数之后,需要将hashmap的值置为NULL,防止野指针, + * 原因同free,fclose等相同。 + *********************************************************************/ +int XipHashmapDestory( void * hashmap); + +/********************************************************************* + * 函数功能: 将key和value存入hashmap中 + * 入口参数: void * hashmap + * char * key 必须是字符串类型 + * void * value 任何类型,可以是整形,数组或者结构体 + * int size 在malloc_flag不为1的情况下,用于帮助hasmap创建内存保存value + * 出口参数: 无 + * 返回值 : 成功,返回保存value的地址 + 失败,返回NULL + * 调用说明: 该函数的实现与XipHashmapInit的参数malloc_flag有关(默认为0) + 设置为1的情况: 则该散列表不保存实际value,只保留指向value的指针, + 这意味着你需要在自己为所有的value,此时size参数无效 + 设置为0或其他的情况:则该散列表会在创建节点的时候,额外 + 分配内存用于对value数据的保存,调用方在使用Put之后, + 不需要在保留原value的数据和空间 + *********************************************************************/ +void * XipHashmapPut( void * in_map, char * key, void * value, int size); + +/********************************************************************* + * 函数功能: 根据key找到对应的value的地址 + * 入口参数: void * hashmap + * char * key 必须是字符串类型 + * 出口参数: 无 + * 返回值 : 成功,返回保存value的地址 + 未找到,返回NULL + 失败,返回NULL + * 调用说明: 该函数的实现与XipHashmapInit的参数malloc_flag有关(默认为0) + *********************************************************************/ +void * XipHashmapGet( void * TxipHashmap, char * key); + +/********************************************************************* + * 函数功能: 根据key值从hashmap中查找是否存在,存在返回真,不存在返回假 + * 入口参数: void * hashmap + * char * key 必须是字符串类型 + * 出口参数: 无 + * 返回值 : int + 成功且为真,返回XIP_HASHMAP_EXIST_TURE + 成功且为假,返回XIP_HASHMAP_EXIST_FALSE + 失败,返回<0的负数 + * 调用说明: + *********************************************************************/ + /***判断是否存在时的返回值***/ +#define XIP_HASHMAP_EXIST_TURE 1 /*存在- 真*/ +#define XIP_HASHMAP_EXIST_FALSE 0 /*不存在- 假*/ +int XipHashmapExists( void * TxipHashmap, char * key); + +/********************************************************************* + * 调用功能: 根据key的值,删除散列表中对应的node + * 入口参数: void * hashmap + * char * key 必须是字符串类型 + * 出口参数: 无 + * 返回值 : int + 成功,返回0 + 失败,返回<0的负数 + * 出口参数: 无 + * 调用说明: + *********************************************************************/ +int XipHashmapRemove( void * TxipHashmap, char * key); + +/********************************************************************* + * 调用功能: 打印hashmap中所有的元素,主要用于调试和检查散列分布 + * 入口参数: hashmap的指针 + * void (ref_func)(int idx, char * key, void * value) + * 回调函数原型,如果需要自己输出日志等,则可定义该回调函数,如果 + * 不使用,则传值NULL + * 出口参数: 无 + * 返回值 : 整型,不会失败,一定会返回0,所以不用判断 + * 调用说明: 打印出来的散列分布是竖列的,样式如下,map的分布会根据次数向后空格 + [I][XipHashMap.c][136][00000000:1] + [I][XipHashMap.c][136][00000001:0] + [I][XipHashMap.c][136][00000002:1] + [I][XipHashMap.c][136][00000003: 3] + [I][XipHashMap.c][136][00000004:1] + [I][XipHashMap.c][136][00000005:1] + [I][XipHashMap.c][136][00000006: 2] + [I][XipHashMap.c][136][00000007:0] + 另外,值的显示目前只能显示出字符串类型,其他类型无法显示 + *********************************************************************/ +int XipHashmapPrint( void * hashmap, void (*ref_func)(int,char *,void *)); + +#endif diff --git a/kmaplib/xiplist.h b/kmaplib/xiplist.h new file mode 100644 index 0000000..016f672 --- /dev/null +++ b/kmaplib/xiplist.h @@ -0,0 +1,103 @@ +#ifndef __XIPLIST_ +#define __XIPLIST_ + +/********************************************************************* + * 函数功能: 以默认方式创建一个list + * 入口参数: 无 + * 出口参数: 无 + * 返回值 : 成功,创建的xiplist,强制转换成了void *类型,需要外部定义void*指针 + 失败,返回NULL + * 调用说明: 以默认的方式创建的list,默认值为XipListInit的默认值 + 也就说XipListInit的所有参数都传递为0 + 大部分情况都可以使用该函数进行一个新的hashmap的创建,如果想进行 + 特殊功能,比如不分配内存,则可以调用 + XipListInit(该函数也对外暴露) + *********************************************************************/ +void * XipListNew(); + +/********************************************************************* + * 函数功能: 创建一个list,有参数供特殊情况使用 + * 入口参数: int malloc_flag 是否分配内存给value: + 1不分配,0和其他分配,具体参见XipListPut的说明 + * 出口参数: 无 + * 返回值 : 成功,创建的的st指针,强制转换成了void *类型,方便调用 + 失败,返回NULL + * 调用说明: 按照传入的信息进行定制的初始hashmap创建, + * 如果你没有特殊的要求或特殊的场景,建议更多的使用XipHashmapNew + *********************************************************************/ +void * XipListInit(int malloc_flag); + +/********************************************************************* + * 函数功能: 将element存入list中 + * 入口参数: void * list + * void * element 任何类型,可以是整形,数组或者结构体 + * unsigned int size 在malloc_flag不为1的情况下, + 用于帮助hasmap创建内存保存value + * 出口参数: 无 + * 返回值 : 成功,返回保存value的地址 + 失败,返回NULL + * 调用说明: 该函数的实现与XipListInit的参数malloc_flag有关(默认为0) + 设置为1的情况: 则该List不保存实际value,只保留指向value的指针, + 这意味着你需要在自己为所有的value,此时size参数无效 + 设置为0或其他的情况:则该List在创建节点的时候,额外 + 分配内存用于对value数据的保存,调用方在使用Put之后, + 不需要在保留原value的数据和空间 + *********************************************************************/ +void * XipListAdd(void * list, void * element, unsigned int size); + +/********************************************************************* + * 函数功能: 根据序号找到对应的value的地址 + * 入口参数: void * list + * int idx 序号 + * 出口参数: 无 + * 返回值 : 成功,返回保存value的地址 + 未找到,返回NULL + 失败,返回NULL + * 调用说明: 该函数的实现与XipHashmapInit的参数malloc_flag有关(默认为0) + *********************************************************************/ +void * XipListGet( void * list, int idx); + +/********************************************************************* + * 函数功能: 销毁和释放list,以及它所占用的空间 + * 入口参数: list指针 + * 出口参数: 无 + * 返回值 : 永远返回0 + * 调用说明: 销毁list结构和占用的空间,该函数可以多次调用. + * 特别注意: 调用该函数之后,需要将list的值置为NULL,防止野指针, + * 原因同free,fclose等相同。 + *********************************************************************/ +int XipListDestory(void * list); + +/********************************************************************* + * 调用功能: 打印list中所有元素 + * 入口参数: list的指针, + ref_func(void * value) 回调函数指针 + 如果不需要特殊打印,则传值为NULL + * 出口参数: 无 + * 返回值 : 整型,不会失败,一定会返回0,所以不用判断 + * 调用说明: 值的显示目前只能显示出字符串类型,其他类型无法显示 + *********************************************************************/ +/* +int XipListPrint( void * list, ref_func); +*/ +int XipListPrint( void * list, void(*ref_func)(void *)); + +/********************************************************************* + * 调用功能: 获取list当前长度 + * 入口参数: list的指针 + * 出口参数: 无 + * 返回值 : 返回长度,失败返回0 + * 调用说明: + *********************************************************************/ +int XipListLen( void * list); + +/********************************************************************* + * 调用功能: 获取list当前临界值 + * 入口参数: list的指针 + * 出口参数: 无 + * 返回值 : 返回临界值,失败返回0 + * 调用说明: + *********************************************************************/ +int XipListThreshold( void * list); + +#endif