只要是读过MQTT broker开源实现的mosquitto代码,就需要了解其对uthash的使用。我也打算把这个只有头文件的hash库用到自己的demo里。于是就遇见了坑。
我发现只能插入一个元素,后面的元素不能插入。弄了半天发现,uthash中的keyptr参数需要的是key的指针形式。
bug1.c
int add_ele(Context *context,SocketWrapper *ele){
int ret=-1;
SocketWrapper *found=NULL;
HASH_FIND(hh_id,context->header,ele->socket,sizeof(Socket*),found);
if(!found){
HASH_ADD_KEYPTR(hh_id,context->header,ele->socket,sizeof(Socket*),ele);
ret=0;
}
printf("%d\n",HASH_CNT(hh_id,context->header));
return ret;
}
int delete_ele(Context *context,SocketWrapper *ele){
SocketWrapper *found=NULL;
int ret=-1;
HASH_FIND(hh_id,context->header,ele->socket,sizeof(Socket*),found);
if(found){
HASH_DELETE(hh_id,context->header,ele);
ret=0;
}
return ret;
}
改完之后,我发现在windows中测试的时候似乎好使,在linux下就不行。百思不得其解。插入总是可以成功,但是在调用remove_ele的时候,就找不到相应的SocketWrapper。但是在insert_ele的插入操作之后,调用 HASH_FIND就可以查到插入的元素。百思不得其解。看了mosuittoto中的使用方式,才发现HASH_ADD_KEYPTR(hh_id,context->header,&ele,sizeof(ele),wrapper);我这样的使用方式,问题极大,这里传入的keyptr的地址是一个临时变量,离开这个函数后,这个地址(很大可能是个寄存器)很可能就指向了其他的内存了。插入的keyptr指向的key就发生变化,在remove_ele找不到对应的SocketWrapper也就正常了。
bug2.c
int insert_ele(Context *context,Socket *ele){
int ret=-1;
SocketWrapper *found=NULL;
HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
if(!found){
SocketWrapper *wrapper=socket_wrapper_new();
wrapper->socket=ele;
printf("insert %p\n",ele);
HASH_ADD_KEYPTR(hh_id,context->header,&ele,sizeof(ele),wrapper);
ret=0;
}
/*
HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
if(found){
printf(found %p\n",found->socket);
}
*/
return ret;
}
int remove_ele(Context *context,Socket *ele){
SocketWrapper *found=NULL;
int ret=-1;
HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
if(found){
printf("remove %p %d\n",found->socket,sizeof(ele));
HASH_DELETE(hh_id,context->header,found);
free(found);
ret=0;
}
return ret;
}
更改就是向HASH_ADD_KEYPTR传入的keyptr,必须指向一块已经分配的内存,不能是临时变量。这个使用方式等同于HASH_ADD(hh_id,context->header,socket,sizeof(ele),wrapper)。这里的socket是一个field。
int insert_ele(Context *context,Socket *ele){
int ret=-1;
SocketWrapper *found=NULL;
HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
if(!found){
SocketWrapper *wrapper=socket_wrapper_new();
wrapper->socket=ele;
printf("insert %p\n",ele);
//the wright way to use HASH_ADD_KEYPTR
HASH_ADD_KEYPTR(hh_id,context->header,&(wrapper->socket),sizeof(ele),wrapper);
ret=0;
}
return ret;
}
normal.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "uthash.h"
typedef struct _Socket Socket;
typedef struct _SocketWrapper SocketWrapper;
typedef struct _Context Context;
struct _Socket{
int fd;
uint8_t *buf;
int buf_len;
};
struct _SocketWrapper{
Socket *socket;
UT_hash_handle hh_id;
};
struct _Context{
SocketWrapper *header;
};
Socket * socket_new();
SocketWrapper* socket_wrapper_new();
int insert_ele(Context *context,Socket *ele){
int ret=-1;
SocketWrapper *found=NULL;
HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
if(!found){
SocketWrapper *wrapper=socket_wrapper_new();
wrapper->socket=ele;
HASH_ADD(hh_id,context->header,socket,sizeof(ele),wrapper);
ret=0;
}
return ret;
}
int remove_ele(Context *context,Socket *ele){
SocketWrapper *found=NULL;
int ret=-1;
HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
if(found){
printf("remove %p %d\n",found->socket,sizeof(ele));
HASH_DELETE(hh_id,context->header,found);
free(found);
ret=0;
}
return ret;
}
Socket * socket_new(){
Socket *ins=NULL;
ins=(Socket*)malloc(sizeof(Socket));
if(ins){
memset(ins,0,sizeof(Socket));
}
return ins;
}
SocketWrapper* socket_wrapper_new(){
SocketWrapper *ins=NULL;
ins=(SocketWrapper*)malloc(sizeof(SocketWrapper));
if(ins){
memset(ins,0,sizeof(SocketWrapper));
}
return ins;
}
int main()
{
Context context;
memset(&context,0,sizeof(context));
int ret=0;
Socket *ele1;
Socket *ele2;
Socket *ele3;
ele1=socket_new();
ele2=socket_new();
ele3=socket_new();
insert_ele(&context,ele1);
insert_ele(&context,ele2);
insert_ele(&context,ele3);
ele1->fd=2;
printf("%d\n",HASH_CNT(hh_id,context.header));
printf("ele1 %p\n",ele1);
printf("ele2 %p\n",ele2);
printf("ele3 %p\n",ele3);
SocketWrapper *itor=NULL;
SocketWrapper *itor_tmp=NULL;
HASH_ITER(hh_id, context.header, itor, itor_tmp){
printf("ele %p\n",itor->socket);
//remove_ele(&context,itor);
//HASH_DELETE(hh_id,context.header,itor);
}
remove_ele(&context,ele1);
remove_ele(&context,ele2);
printf("%d\n",HASH_CNT(hh_id,context.header));
return 0;
}
uthash使用起来很方便,但是使用教程较少,一不小心,可能就因为使用方式问题,掉进坑里。
就这点问题,浪费一个上午,加上一个晚上。
[1]C开源hash代码uthash的用法总结 https://blog.csdn.net/houjixin/article/details/15809825
[2]uthash https://blog.csdn.net/shenwansangz/article/details/48729969
[3]c开源库uthash的使用 https://blog.csdn.net/qq_23091073/article/details/86485095