1 /** 2 * @file cstl_hash.h 3 * @copyright Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 16 * @brief cstl_hash 对外头文件 17 * @details 1、遍历过程中,迭代器指向冲突链中节点的位置;但end迭代器指向最后一个保留的hash桶,该桶无数据。 18 * 2、关于(key, value)中的value 19 * a) 如果key、value是整型数据,且长度 <= sizeof(uintptr_t),则直接记录在node后面紧邻位置; 20 * b)其它场景,key或value位置记录的是指针,指向真正的用户key或用户数据; 21 * 此时用户必须注册duplicate函数和free函数对。 22 * 3、duplicate函数:用户需要先根据源数据长度申请内存,在把用户数拷贝到申请的内存中去,最后返回分配的内存地址。 23 * +--------+ 24 * | 控制块 | 25 * +------- + iter--->+------+ iter-->+------+ 26 * | 0 | <-冲突链-> | prev | | prev | 27 * +--------+ | next | 场景1: | next | 场景2: 28 * | 1 | +------+ key长度<=sizeof(uintptr_t) +------+ key长度<sizeof(uintptr_t) 29 * |--------+ | key | value长度<=sizeof(uintptr_t) | key | value长度>sizeof(uintptr_t) 30 * | + +------+ +------+ 或字符串或自定义数据结构 31 * | ... | | value| | data |--+ 32 * +--------+ +------+ | 指针 | + 33 * +------+ + 34 * + 35 * +----->+----------+ 36 * | userdata | 37 * | area | 38 * +----------+ 39 * iter-->+------+ iter-->+------+ 场景4: 40 * | prev | | prev | key长度>sizeof(uintptr_t) 41 * | next | 场景3: | next | 或字符串或自定义数据结构 42 * +------+ key长度>sizeof(uintptr_t)或字符串或自定义数据结构 +------+ 43 * | key |---+ value长度<=sizeof(uintptr_t) | key |---+ value长度>sizeof(uintptr_t) 44 * | 指针 | + | 指针 | + 或字符串或自定义数据结构 45 * +------+ +---->+----------+ +------+ +---->+----------+ 46 * | data | | userkey | | data |--+ | userkey | 47 * +------+ | area | | 指针 | + | area | 48 * +----------+ +------+ + +----------+ 49 * + 50 * +----->+----------+ 51 * | userdata | 52 * | area | 53 * +----------+ 54 * @date 2021-05-14 55 * @version v0.1.0 56 * ******************************************************************************************* 57 * @par 修改日志: 58 * <table> 59 * <tr><th>Date <th>Version <th>Description 60 * <tr><td>2021-05-14 <td>0.1.0 <td>创建初始版本 61 * </table> 62 * ******************************************************************************************* 63 */ 64 65 /** 66 * @defgroup cstl_hash 哈希表 67 * @ingroup cstl 68 */ 69 70 #ifndef CSTL_HASH_H 71 #define CSTL_HASH_H 72 73 #include <stdlib.h> 74 #include <stdbool.h> 75 #include <string.h> 76 #include "cstl_public.h" 77 #include "securec.h" 78 79 #ifdef __cplusplus 80 extern "C" { 81 #endif 82 83 /** 84 * @ingroup cstl_hash 85 * @brief hash表句柄 86 */ 87 typedef struct CstlHashInfo CstlHash; 88 89 /** 90 * @ingroup cstl_hash 91 * @brief hash表迭代器定义,指向hash节点 92 */ 93 typedef struct TagHashNode *CstlHashIterator; 94 95 /** 96 * @ingroup cstl_hash 97 * @brief 根据输入的Key生成hash表索引。 98 * @param key [IN] hash key 99 * @param bktSize [IN] hash桶大小 100 */ 101 typedef size_t (*CstlHashCodeCalcFunc)(uintptr_t key, size_t bktSize); 102 103 /** 104 * @ingroup cstl_hash 105 * @brief 该函数把输入数据与Key进行匹配比较。第一个哈希表中保存的key,第二个是用户需要匹配的Key。如果不匹配,返回值为false。 106 * @param key1 [IN] 哈希表中保存的key 107 * @param key2 [IN] 待匹配的key 108 * @retval #true key1和key2匹配。 109 * @retval #false key1和key2不匹配。 110 */ 111 typedef bool (*CstlHashMatchFunc)(uintptr_t key1, uintptr_t key2); 112 113 /** 114 * @ingroup cstl_hash 115 * @brief 默认整型哈希函数。 116 * @par 默认整型哈希函数。 117 * @attention \n 118 * 1.key为用户调用其他接口时的入参。\n 119 * 2.若key为整型,用户可在创建hash时将该函数作为hashFunc参数。\n 120 * @param key [IN] 待计算的key。 121 * @param bktSize [IN] 哈希桶大小。 122 * @retval #哈希值 根据用户key计算出的哈希值。 123 * @par 依赖:无 124 * <ul><li>cstl_hash.h:该接口声明所在的头文件。</li></ul> 125 */ 126 size_t CstlHashCodeCalcInt(uintptr_t key, size_t bktSize); 127 128 /** 129 * @ingroup cstl_hash 130 * @brief 默认字符串哈希函数。 131 * @par 默认字符串哈希函数。 132 * @attention \n 133 * 1.key为用户调用其他接口时的入参,用户必须保证传入的key为合法的字符串首地址。\n 134 * 2.若key为字符串,用户可在创建hash时将该函数作为hashFunc参数。\n 135 * @param key [IN] 待计算的key。 136 * @param bktSize [IN] 哈希桶大小。 137 * @retval #哈希值 根据用户key计算出的哈希值。 138 * @par 依赖:无 139 * @li cstl_hash.h:该接口声明所在的头文件. 140 */ 141 size_t CstlHashCodeCalcStr(uintptr_t key, size_t bktSize); 142 143 /** 144 * @ingroup cstl_hash 145 * @brief 默认整型匹配函数。 146 * @par 默认整型匹配函数。 147 * @attention \n 148 * 1.key为用户调用其他接口时的入参。\n 149 * 2.若key为整型,用户可在创建hash时将该函数作为matchFunc参数。\n 150 * @param key1 [IN] 待匹配的key。 151 * @param key2 [IN] 待匹配的key。 152 * @retval #true key1和key2匹配。 153 * @retval #false key1和key2不匹配。 154 * @par 依赖:无 155 * @li cstl_hash.h:该接口声明所在的头文件. 156 */ 157 bool CstlHashMatchInt(uintptr_t key1, uintptr_t key2); 158 159 /** 160 * @ingroup cstl_hash 161 * @brief 默认字符串匹配函数。 162 * @par 默认字符串匹配函数。 163 * @attention \n 164 * 1.key1为用户调用其他接口时的入参,用户必须保证传入的key1为合法的字符串首地址。\n 165 * 2.若key为字符串,用户可在创建hash时将该函数作为matchFunc参数。\n 166 * @param key1 [IN] 待匹配的key。 167 * @param key2 [IN] 待匹配的key。 168 * @retval #true key1和key2匹配。 169 * @retval #false key1和key2不匹配。 170 * @par 依赖:无 171 * @li cstl_hash.h:该接口声明所在的头文件. 172 */ 173 bool CstlHashMatchStr(uintptr_t key1, uintptr_t key2); 174 175 /** 176 * @ingroup cstl_hash 177 * @brief 创建一个Hash表,返回Hash表的句柄 178 * @attention \n 179 * 1、关于key和data的拷贝函数:\n 180 * 如下场景不需要注册拷贝函数:如果是int型数据,且长度<=sizeof(uintptr_t)。\n 181 * 其它场景必须注册拷贝函数:\n 182 * a)是int型数据,但长度 >sizeof(uintptr_t);\n 183 * b)字符串;\n 184 * c)用户自定义数据结构;\n 185 * 2、关于free函数:简单来说,如果注册了duplicate函数,就必须注册相应的free函数。 \n 186 * 3、提供默认的整型、字符串哈希函数:#CstlHashCodeCalcInt、#CstlHashCodeCalcStr。 \n 187 * 4、提供默认的整型、字符串匹配函数:#CstlHashMatchInt、#CstlHashMatchStr。 \n 188 * @param bktSize [IN] hash桶的个数 189 * @param hashCalcFunc [IN] hash值计算函数。如果为NULL,则默认key为整型,使用#CstlHashCodeCalcInt。 190 * @param matchFunc [IN] hash key匹配函数。如为NULL,则默认key为整型,使用#CstlHashMatchInt。 191 * @param keyFunc [IN] hash key拷贝及释放函数对,如果用户未注册keyFunc->dupFunc,则默认为key为整型。 192 * @param valueFunc [IN] hash data拷贝及释放函数对。如果用户未注册valueFunc->dupFunc,则默认为data为整型。 193 * @retval hash表句柄。NULL表示创建失败。 194 * @par 依赖:无 195 * @see #CstlHashCodeCalcInt、#CstlHashCodeCalcStr、#CstlHashMatchInt、#CstlHashMatchStr。 196 * @li cstl_hash.h:该接口声明所在的头文件. 197 */ 198 CstlHash *CstlHashCreate(size_t bktSize, 199 CstlHashCodeCalcFunc hashFunc, 200 CstlHashMatchFunc matchFunc, 201 CstlDupFreeFuncPair *keyFunc, 202 CstlDupFreeFuncPair *valueFunc); 203 204 /** 205 * @ingroup cstl_hash 206 * @brief 插入hash数据 207 * @par 描述:创建节点,把数据(key、value)插入hash表 208 * @attention 209 * 1.不支持重复key。\n 210 * 2.key和value为整型值或指向用户key或value的地址。\n 211 * 3.如果扩展数据的生命周期小于节点的生命周期,则需要在创建哈希表时注册拷贝函数及释放函数。\n 212 * @param hash [IN] hash表的句柄。 213 * @param key [IN] key或保存key的地址。 214 * @param keySize [IN] key拷贝长度,若用户未注册dupFunc,该参数将不被使用 215 * @param value [IN] value或保存value的地址。 216 * @param valueSize [IN] value拷贝长度,若用户未注册dupFunc,该参数将不被使用 217 * @retval #CSTL_OK 插入节点成功。 218 * @retval #CSTL_ERROR 插入失败。 219 * @par 依赖:无 220 * @li cstl_hash.h:该接口声明所在的头文件. 221 */ 222 int32_t CstlHashInsert(CstlHash *hash, uintptr_t key, size_t keySize, uintptr_t value, size_t valueSize); 223 224 /** 225 * @ingroup cstl_hash 226 * @brief 插入或更新hash数据 227 * @par 描述:该接口用于将不存在的key插入到哈希表或更新已存在的key对应的value。 228 * @attention 229 * 1.支持重复key。\n 230 * 2.当key不存在时,该接口的使用与#CstlHashInsert保持一致。\n 231 * 3.当key存在时,该接口会更新value。\n 232 * @param hash [IN] hash表的句柄。 233 * @param key [IN] key或保存key的地址。 234 * @param keySize [IN] key拷贝长度,若用户未注册dupFunc,该参数将不被使用 235 * @param value [IN] value或保存value的地址。 236 * @param valueSize [IN] value拷贝长度,若用户未注册dupFunc,该参数将不被使用 237 * @retval #CSTL_OK 插入或更新节点成功。 238 * @retval #CSTL_ERROR 插入或更新节点失败。 239 * @par 依赖:无 240 * @li cstl_hash.h:该接口声明所在的头文件. 241 */ 242 int32_t CstlHashPut(CstlHash *hash, uintptr_t key, size_t keySize, uintptr_t value, size_t valueSize); 243 244 /** 245 * @ingroup cstl_hash 246 * @brief 查找节点,返回节点数据。 247 * @par 描述: 根据key查找并返回节点数据。 248 * @param hash [IN] hash表的句柄。 249 * @param key [IN] key或保存key的地址。 250 * @param value [OUT] 查到的数据。 251 * @retval #CSTL_OK 查找成功。 252 * @retval #CSTL_ERROR 查找失败。 253 * @par 依赖:无 254 * @li cstl_hash.h:该接口声明所在的头文件. 255 */ 256 int32_t CstlHashAt(const CstlHash *hash, uintptr_t key, uintptr_t *value); 257 258 /** 259 * @ingroup cstl_hash 260 * @brief 查找key所在迭代器。 261 * @par 描述: 根据key查找并返回key所在的迭代器。 262 * @param hash [IN] hash表的句柄。 263 * @param key [IN] key或保存key的地址。 264 * @retval key存在时,返回key所在迭代器(指向Node所在地址),其他情况返回#CstlHashIterEnd()。 265 * @par 依赖:无 266 * @li cstl_hash.h:该接口声明所在的头文件. 267 */ 268 CstlHashIterator CstlHashFind(const CstlHash *hash, uintptr_t key); 269 270 /** 271 * @ingroup cstl_hash 272 * @brief 判断当前HASH表是否为空。 273 * @par 描述: 判断当前HASH表是否为空,为空返回true,否则返回false。 274 * @param hash [IN] hash表句柄。取值范围为有效指针。 275 * @retval #true, 表示HASH表为空。 276 * @retval #false,表示HASH表非空。 277 * @see #CstlHashSize 278 * @par 依赖:无 279 * @li cstl_hash.h:该接口声明所在的头文件. 280 */ 281 bool CstlHashEmpty(const CstlHash *hash); 282 283 /** 284 * @ingroup cstl_hash 285 * @brief 获取HASH表的节点数量。 286 * @par 描述: 用于获取HASH表的节点数量,返回节点个数。 287 * @param hash [IN] hash表句柄。取值范围为有效指针。 288 * @retval hash节点数。 289 * @par 依赖:无 290 * @li cstl_hash.h:该接口声明所在的头文件. 291 */ 292 size_t CstlHashSize(const CstlHash *hash); 293 294 /** 295 * @ingroup cstl_hash 296 * @brief 从hash表中移除指定结点。 297 * @par 描述: 根据key查找到节点并删除(释放内存),同时释放相应的节点内存。 298 * @param hash [IN] hash表句柄。取值范围为有效指针。 299 * @param key [IN] 移除节点key。 300 * @retval key存在时,返回key所在迭代器的下一个迭代器(指向Node所在地址),其他则返回#CstlHashIterEnd()。 301 * @par 依赖:无 302 * @li cstl_hash.h:该接口声明所在的头文件. 303 */ 304 CstlHashIterator CstlHashErase(CstlHash *hash, uintptr_t key); 305 306 /** 307 * @ingroup cstl_hash 308 * @brief 删除hash表所有节点。 309 * @par 描述:删除所有节点,回收节点内存(hash表还在,只是没有成员)。 310 * @attention 注意:如果用户数据中有私有资源,则需要在创建时注册free钩子函数。 311 * @param hash [IN] hash表句柄。 312 * @retval 无。 313 * @par 依赖:无 314 * @li cstl_hash.h:该接口声明所在的头文件. 315 */ 316 void CstlHashClear(CstlHash *hash); 317 318 /** 319 * @ingroup cstl_hash 320 * @brief 删除hash表 321 * @par 描述:删除hash表,如里面有节点先删除节点,回收内存。 322 * @attention 注意:如果用户数据中有私有资源,则需要在创建时注册free钩子函数。 323 * @param hash [IN] hash表句柄。 324 * @retval 无。 325 * @par 依赖:无 326 * @li cstl_hash.h:该接口声明所在的头文件. 327 */ 328 void CstlHashDestory(CstlHash *hash); 329 330 /** 331 * @ingroup cstl_hash 332 * @brief 获取hash表中的第一个节点的迭代器。 333 * @par 描述:获取hash表中的第一个节点所在的迭代器。 334 * @param hash [IN] hash表的句柄。 335 * @retval 第一个节点迭代器,若hash为空则返回#CstlHashIterEnd()。 336 * @par 依赖:无 337 * @li cstl_hash.h:该接口声明所在的头文件. 338 */ 339 CstlHashIterator CstlHashIterBegin(const CstlHash *hash); 340 341 /** 342 * @ingroup cstl_hash 343 * @brief 获取hash表中最后一个节点之后预留的迭代器。 344 * @par 描述:获取hash表中的最后一个节点之后预留的迭代器。该节点指向最后保留的hash桶,该桶没有任何成员。 345 * @param hash [IN] hash表的句柄。 346 * @retval 最后一个节点之后预留的迭代器。 347 * @par 依赖:无 348 * @li cstl_hash.h:该接口声明所在的头文件. 349 */ 350 CstlHashIterator CstlHashIterEnd(const CstlHash *hash); 351 352 /** 353 * @ingroup cstl_hash 354 * @brief 获取hash表的下一个节点迭代器。 355 * @par 描述:获取hash表下一个节点迭代器。 356 * @param hash [IN] hash表的句柄。 357 * @param it [IN] 当前迭代器。 358 * @retval 下一个节点迭代器。若当前节点已是最后一个迭代器则返回#CstlHashIterEnd()。 359 * @par 依赖:无 360 * @li cstl_hash.h:该接口声明所在的头文件. 361 */ 362 CstlHashIterator CstlHashIterNext(const CstlHash *hash, CstlHashIterator it); 363 364 /** 365 * @ingroup cstl_hash 366 * @brief 获取迭代器的key。 367 * @par 描述:获取hash表中迭代器当前key。 368 * @attention \n 369 * 1.当hash为空指针或it等于#CstlHashIterEnd()时,接口返回0,该接口无法区分是错误码还是用户数据, 370 * 用户调用该接口时必须保证hash为合法指针,并且it不等于#CstlHashIterEnd() 371 * @param hash [IN] hash表的句柄。 372 * @param it [IN] 当前迭代器。 373 * @retval 迭代器对应的key。it等于#CstlHashIterEnd()时则返回0。 374 * @par 依赖:无 375 * @li cstl_hash.h:该接口声明所在的头文件. 376 */ 377 uintptr_t CstlHashIterKey(const CstlHash *hash, CstlHashIterator it); 378 379 /** 380 * @ingroup cstl_hash 381 * @brief 获取迭代器的value。 382 * @par 描述:获取hash表中迭代器当前key。 383 * @attention \n 384 * 1.当hash为空指针或it等于#CstlHashIterEnd()时,接口返回0,该接口无法区分是错误码还是用户数据, 385 * 用户调用该接口时必须保证hash为合法指针,并且it不等于#CstlHashIterEnd() 386 * @param hash [IN] hash表的句柄。 387 * @param it [IN] 当前迭代器。 388 * @retval 迭代器对应的value。it等于#CstlHashIterEnd()时则返回0。 389 * @par 依赖:无 390 * @li cstl_hash.h:该接口声明所在的头文件. 391 */ 392 uintptr_t CstlHashIterValue(const CstlHash *hash, CstlHashIterator it); 393 394 #ifdef __cplusplus 395 } 396 #endif 397 398 #endif /* CSTL_HASH_H */ 399 400