Added utility functions to skygw_utils and cleaned up tee filter.

This commit is contained in:
Markus Makela 2015-04-15 12:42:28 +03:00
parent 786f34cf49
commit 051d891680
3 changed files with 406 additions and 282 deletions

View File

@ -179,13 +179,10 @@ typedef struct {
int residual; /* Any outstanding SQL text */
GWBUF* tee_replybuf; /* Buffer for reply */
GWBUF* tee_partials[2];
GWBUF* querybuf;
GWBUF* queue;
SPINLOCK tee_lock;
DCB* client_dcb;
int statements; /*< Number of statements in the query,
* used to identify and track multi-statement
* queries and that both the parent and the child
* branch are in sync. */
#ifdef SS_DEBUG
long d_id;
#endif
@ -209,30 +206,12 @@ static SPINLOCK orphanLock;
static int packet_is_required(GWBUF *queue);
static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session);
int internal_route(DCB* dcb);
static int hkfn(
void* key)
{
if(key == NULL){
return 0;
}
unsigned int hash = 0,c = 0;
char* ptr = (char*)key;
while((c = *ptr++)){
hash = c + (hash << 6) + (hash << 16) - hash;
}
return *(int *)key;
}
static int hcfn(
void* v1,
void* v2)
{
char* i1 = (char*) v1;
char* i2 = (char*) v2;
return strcmp(i1,i2);
}
GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer);
int route_single_query(TEE_INSTANCE* my_instance,
TEE_SESSION* my_session,
GWBUF* buffer,
GWBUF* clone);
int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer);
static void
orphan_free(void* data)
@ -492,7 +471,7 @@ char *remote, *userName;
goto retblock;
}
HASHTABLE* ht = hashtable_alloc(100,hkfn,hcfn);
HASHTABLE* ht = hashtable_alloc(100,simple_str_hash,strcmp);
bool is_loop = detect_loops(my_instance,ht,session->service);
hashtable_free(ht);
@ -509,12 +488,11 @@ char *remote, *userName;
{
my_session->active = 1;
my_session->residual = 0;
my_session->statements = 0;
my_session->tee_replybuf = NULL;
my_session->client_dcb = session->client;
my_session->instance = my_instance;
my_session->client_multistatement = false;
my_session->queue = NULL;
spinlock_init(&my_session->tee_lock);
if (my_instance->source &&
(remote = session_get_remote(session)) != NULL)
@ -812,155 +790,61 @@ setUpstream(FILTER *instance, void *session, UPSTREAM *upstream)
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
TEE_SESSION *my_session = (TEE_SESSION *)session;
char *ptr;
int length, rval, residual = 0;
GWBUF *clone = NULL;
unsigned char command = *((unsigned char*)queue->start + 4);
TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
TEE_SESSION *my_session = (TEE_SESSION *)session;
char *ptr;
int rval;
GWBUF *buffer = NULL, *clone = NULL;
unsigned char command = gwbuf_length(queue) >= 5 ?
*((unsigned char*)queue->start + 4) : 1;
#ifdef SS_DEBUG
skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
atomic_add(&debug_seq,1),
((char*)queue->start + 5));
skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
atomic_add(&debug_seq,1),
((char*)queue->start + 5));
#endif
spinlock_acquire(&my_session->tee_lock);
if(!my_session->active)
{
skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
gwbuf_free(queue);
rval = 0;
goto retblock;
}
if (my_session->branch_session &&
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
{
if (my_session->residual)
{
clone = gwbuf_clone_all(queue);
if (my_session->residual < GWBUF_LENGTH(clone))
{
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
}
my_session->residual -= GWBUF_LENGTH(clone);
if (my_session->residual < 0)
{
my_session->residual = 0;
}
}
else if (my_session->active && (ptr = modutil_get_SQL(queue)) != NULL)
{
if ((my_instance->match == NULL ||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
(my_instance->nomatch == NULL ||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
{
length = modutil_MySQL_query_len(queue, &residual);
clone = gwbuf_clone_all(queue);
my_session->residual = residual;
}
free(ptr);
}
else if (packet_is_required(queue))
{
clone = gwbuf_clone_all(queue);
}
}
spinlock_acquire(&my_session->tee_lock);
if(!my_session->active)
{
skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
gwbuf_free(queue);
spinlock_release(&my_session->tee_lock);
return 0;
}
/* Pass the query downstream */
if(my_session->queue)
{
my_session->queue = gwbuf_append(my_session->queue,queue);
buffer = modutil_get_next_MySQL_packet(&my_session->queue);
}
else
{
buffer = modutil_get_next_MySQL_packet(&queue);
my_session->queue = queue;
}
switch(command)
{
case 0x1b:
my_session->client_multistatement = *((unsigned char*) queue->start + 5);
case 0x03:
case 0x16:
case 0x17:
case 0x04:
case 0x0a:
memset(my_session->multipacket,(char)true,2*sizeof(bool));
break;
default:
memset(my_session->multipacket,(char)false,2*sizeof(bool));
break;
}
memset(my_session->replies,0,2*sizeof(int));
memset(my_session->reply_packets,0,2*sizeof(int));
memset(my_session->eof,0,2*sizeof(int));
memset(my_session->waiting,1,2*sizeof(bool));
my_session->statements = modutil_count_statements(queue);
my_session->command = command;
#ifdef SS_DEBUG
spinlock_acquire(&debug_lock);
my_session->d_id = ++debug_id;
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] command [%x]",
my_session->d_id,
my_session->command);
if(command == 0x03)
{
char* tmpstr = modutil_get_SQL(queue);
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c query: '%s'",
tmpstr);
free(tmpstr);
}
spinlock_release(&debug_lock);
#endif
spinlock_acquire(&my_session->tee_lock);
if(buffer == NULL)
{
spinlock_release(&my_session->tee_lock);
return 1;
}
clone = clone_query(my_instance, my_session,buffer);
spinlock_release(&my_session->tee_lock);
if(!my_session->active ||
my_session->branch_session == NULL ||
my_session->branch_session->state != SESSION_STATE_ROUTER_READY)
{
rval = 0;
my_session->active = 0;
goto retblock;
}
rval = my_session->down.routeQuery(my_session->down.instance,
my_session->down.session,
queue);
if (clone)
{
my_session->n_duped++;
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
{
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
}
else
{
/** Close tee session */
my_session->active = 0;
rval = 0;
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Closed tee filter session: Child session in invalid state.")));
gwbuf_free(clone);
}
}
else
{
if (my_session->active)
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Closed tee filter session: Child session is NULL.")));
my_session->active = 0;
rval = 0;
}
my_session->n_rejected++;
}
retblock:
spinlock_release(&my_session->tee_lock);
return rval;
/* Reset session state */
if(!reset_session_state(my_session,buffer))
return 0;
/** Route query downstream */
spinlock_acquire(&my_session->tee_lock);
rval = route_single_query(my_instance,my_session,buffer,clone);
spinlock_release(&my_session->tee_lock);
return rval;
}
int count_replies(GWBUF* buffer)
@ -1043,41 +927,42 @@ uint16_t get_response_flags(uint8_t* datastart, bool ok_packet)
static int
clientReply (FILTER* instance, void *session, GWBUF *reply)
{
int rc, branch, eof;
TEE_SESSION *my_session = (TEE_SESSION *) session;
bool route = false,mpkt;
GWBUF *complete = NULL;
unsigned char *ptr;
uint16_t flags = 0;
int min_eof = my_session->command != 0x04 ? 2 : 1;
int more_results = 0;
int rc, branch, eof;
TEE_SESSION *my_session = (TEE_SESSION *) session;
bool route = false,mpkt;
GWBUF *complete = NULL;
unsigned char *ptr;
uint16_t flags = 0;
int min_eof = my_session->command != 0x04 ? 2 : 1;
int more_results = 0;
#ifdef SS_DEBUG
ptr = (unsigned char*) reply->start;
skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d",
instance ? "parent":"child",
my_session->active ? "open" : "closed",
PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
atomic_add(&debug_seq,1));
ptr = (unsigned char*) reply->start;
skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d",
instance ? "parent":"child",
my_session->active ? "open" : "closed",
PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
atomic_add(&debug_seq,1));
#endif
spinlock_acquire(&my_session->tee_lock);
if(!my_session->active)
{
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
gwbuf_free(reply);
rc = 0;
if(my_session->waiting[PARENT])
{
GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"0000","Session closed.");
my_session->waiting[PARENT] = false;
my_session->up.clientReply (my_session->up.instance,
my_session->up.session,
errbuf);
}
goto retblock;
}
spinlock_acquire(&my_session->tee_lock);
branch = instance == NULL ? CHILD : PARENT;
if(!my_session->active)
{
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
gwbuf_free(reply);
rc = 0;
if(my_session->waiting[PARENT])
{
GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"0000","Session closed.");
my_session->waiting[PARENT] = false;
my_session->up.clientReply (my_session->up.instance,
my_session->up.session,
errbuf);
}
goto retblock;
}
branch = instance == NULL ? CHILD : PARENT;
my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply);
my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]);
@ -1106,131 +991,146 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
if(my_session->replies[branch] == 0)
{
skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD");
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
* Otherwise the reply is a result set and the amount of packets is unknown.
*/
if(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
* Otherwise the reply is a result set and the amount of packets is unknown.
*/
if(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
PTR_IS_OK(ptr) || !my_session->multipacket[branch] )
{
my_session->waiting[branch] = false;
my_session->multipacket[branch] = false;
if(PTR_IS_OK(ptr))
{
flags = get_response_flags(ptr,true);
more_results = (flags & 0x08) && my_session->client_multistatement;
if(more_results)
{
skygw_log_write(LOGFILE_TRACE,
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
}
}
my_session->waiting[branch] = false;
my_session->multipacket[branch] = false;
if(PTR_IS_OK(ptr))
{
flags = get_response_flags(ptr,true);
more_results = (flags & 0x08) && my_session->client_multistatement;
if(more_results)
{
skygw_log_write(LOGFILE_TRACE,
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
}
}
}
#ifdef SS_DEBUG
else
else
{
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.",
my_session->d_id,
branch == PARENT?"parent":"child");
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.",
my_session->d_id,
branch == PARENT?"parent":"child");
}
#endif
}
if(my_session->waiting[branch])
if(my_session->waiting[branch])
{
eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results);
more_results &= my_session->client_multistatement;
my_session->eof[branch] += eof;
eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results);
more_results &= my_session->client_multistatement;
my_session->eof[branch] += eof;
if(my_session->eof[branch] >= min_eof)
if(my_session->eof[branch] >= min_eof)
{
#ifdef SS_DEBUG
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet",
my_session->d_id,
branch == PARENT?"parent":"child");
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet",
my_session->d_id,
branch == PARENT?"parent":"child");
#endif
my_session->waiting[branch] = more_results;
if(more_results)
{
my_session->eof[branch] = 0;
}
my_session->waiting[branch] = more_results;
if(more_results)
{
my_session->eof[branch] = 0;
}
}
}
if(branch == PARENT)
if(branch == PARENT)
{
my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete);
my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete);
}
else
else
{
if(complete)
gwbuf_free(complete);
if(complete)
gwbuf_free(complete);
}
my_session->replies[branch]++;
rc = 1;
mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];
if(my_session->tee_replybuf != NULL)
my_session->replies[branch]++;
rc = 1;
mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];
if(my_session->tee_replybuf != NULL)
{
if(my_session->branch_session == NULL)
if(my_session->branch_session == NULL)
{
rc = 0;
gwbuf_free(my_session->tee_replybuf);
my_session->tee_replybuf = NULL;
skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed.");
rc = 0;
gwbuf_free(my_session->tee_replybuf);
my_session->tee_replybuf = NULL;
skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed.");
}
if(mpkt)
if(mpkt)
{
if(my_session->waiting[PARENT])
if(my_session->waiting[PARENT])
{
route = true;
route = true;
}
else if(my_session->eof[PARENT] >= min_eof &&
my_session->eof[CHILD] >= min_eof)
else if(my_session->eof[PARENT] >= min_eof &&
my_session->eof[CHILD] >= min_eof)
{
route = true;
route = true;
#ifdef SS_DEBUG
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id);
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id);
#endif
}
}
else if(!my_session->waiting[PARENT] &&
!my_session->waiting[CHILD])
else if(!my_session->waiting[PARENT] &&
!my_session->waiting[CHILD])
{
#ifdef SS_DEBUG
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id);
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id);
#endif
route = true;
route = true;
}
}
if(route)
if(route)
{
#ifdef SS_DEBUG
skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
" child(waiting [%s] replies[%d] eof [%d])",
my_session->d_id,
my_session->tee_replybuf,
my_session->waiting[PARENT] ? "true":"false",
my_session->replies[PARENT],
my_session->eof[PARENT],
my_session->waiting[CHILD]?"true":"false",
my_session->replies[CHILD],
my_session->eof[CHILD]);
skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
" child(waiting [%s] replies[%d] eof [%d])",
my_session->d_id,
my_session->tee_replybuf,
my_session->waiting[PARENT] ? "true":"false",
my_session->replies[PARENT],
my_session->eof[PARENT],
my_session->waiting[CHILD]?"true":"false",
my_session->replies[CHILD],
my_session->eof[CHILD]);
#endif
rc = my_session->up.clientReply (my_session->up.instance,
my_session->up.session,
my_session->tee_replybuf);
my_session->tee_replybuf = NULL;
rc = my_session->up.clientReply (my_session->up.instance,
my_session->up.session,
my_session->tee_replybuf);
my_session->tee_replybuf = NULL;
}
retblock:
spinlock_release(&my_session->tee_lock);
return rc;
if(my_session->queue &&
!my_session->waiting[PARENT] &&
!my_session->waiting[CHILD])
{
GWBUF* buffer = modutil_get_next_MySQL_packet(&my_session->queue);
GWBUF* clone = clone_query(my_session->instance,my_session,buffer);
reset_session_state(my_session,buffer);
route_single_query(my_session->instance,my_session,buffer,clone);
LOGIF(LT,(skygw_log_write(LT,"tee: routing queued query")));
}
retblock:
spinlock_release(&my_session->tee_lock);
return rc;
}
/**
@ -1352,4 +1252,152 @@ int internal_route(DCB* dcb)
TEE_SESSION* session = dcb->data;
return routeQuery((FILTER*)session->instance,session,buffer);
}
/**
*
* @param my_instance
* @param my_session
* @param buffer
* @return
*/
GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer)
{
GWBUF* clone = NULL;
int length, residual;
char* ptr;
if (my_session->branch_session &&
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
{
if (my_session->residual)
{
clone = gwbuf_clone_all(buffer);
if (my_session->residual < GWBUF_LENGTH(clone))
{
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
}
my_session->residual -= GWBUF_LENGTH(clone);
if (my_session->residual < 0)
{
my_session->residual = 0;
}
}
else if (my_session->active && (ptr = modutil_get_SQL(buffer)) != NULL)
{
if ((my_instance->match == NULL ||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
(my_instance->nomatch == NULL ||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
{
length = modutil_MySQL_query_len(buffer, &residual);
clone = gwbuf_clone_all(buffer);
my_session->residual = residual;
}
free(ptr);
}
else if (packet_is_required(buffer))
{
clone = gwbuf_clone_all(buffer);
}
}
return clone;
}
/**
* Route the main query downstream along the main filter chain and possibly route
* a clone of the buffer to the branch session. If the clone buffer is NULL, nothing
* is routed to the branch session.
* @param my_instance Tee instance
* @param my_session Tee session
* @param buffer Main buffer
* @param clone Cloned buffer
* @return 1 on success, 0 on failure.
*/
int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer, GWBUF* clone)
{
int rval = 0;
if(!my_session->active ||
my_session->branch_session == NULL ||
my_session->branch_session->state != SESSION_STATE_ROUTER_READY)
{
rval = 0;
my_session->active = 0;
return rval;
}
rval = my_session->down.routeQuery(my_session->down.instance,
my_session->down.session,
buffer);
if (clone)
{
my_session->n_duped++;
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
{
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
}
else
{
/** Close tee session */
my_session->active = 0;
rval = 0;
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Closed tee filter session: Child session in invalid state.")));
gwbuf_free(clone);
}
}
else
{
if (my_session->active)
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Closed tee filter session: Child session is NULL.")));
my_session->active = 0;
rval = 0;
}
my_session->n_rejected++;
}
return rval;
}
/**
* Reset the session's internal counters.
* @param my_session Tee session
* @param buffer Buffer with the query of the main branch in it
* @return 1 on success, 0 on error
*/
int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer)
{
if(gwbuf_length(buffer) < 5)
return 0;
unsigned char command = *((unsigned char*)buffer->start + 4);
switch(command)
{
case 0x1b:
my_session->client_multistatement = *((unsigned char*) buffer->start + 5);
case 0x03:
case 0x16:
case 0x17:
case 0x04:
case 0x0a:
memset(my_session->multipacket,(char)true,2*sizeof(bool));
break;
default:
memset(my_session->multipacket,(char)false,2*sizeof(bool));
break;
}
memset(my_session->replies,0,2*sizeof(int));
memset(my_session->reply_packets,0,2*sizeof(int));
memset(my_session->eof,0,2*sizeof(int));
memset(my_session->waiting,1,2*sizeof(bool));
my_session->command = command;
return 1;
}

View File

@ -1045,6 +1045,59 @@ void slcursor_add_data(
CHK_SLIST_CURSOR(c);
}
/**
* Remove the current node in the slist. This does not delete the data in the
* node but will delete the structure pointing to that data. This is useful when
* the user wants to free the allocated memory. After node removal, the cursor
* will point to the node before the removed node.
* @param c Cursor pointing to the data node to be removed
*/
void slcursor_remove_data(slist_cursor_t* c)
{
slist_node_t* node = c->slcursor_pos;
int havemore = slist_size(c);
slcursor_move_to_begin (c);
if(node == c->slcursor_pos)
{
c->slcursor_list->slist_head = c->slcursor_list->slist_head->slnode_next;
slcursor_move_to_begin (c);
atomic_add((int*)&node->slnode_list->slist_nelems,-1);
atomic_add((int*)&node->slnode_cursor_refcount,-1);
if(node->slnode_cursor_refcount == 0)
{
free(node);
}
return;
}
while(havemore)
{
if( c->slcursor_pos->slnode_next == node)
{
c->slcursor_pos->slnode_next = node->slnode_next;
atomic_add((int*)&node->slnode_list->slist_nelems,-1);
atomic_add((int*)&node->slnode_cursor_refcount,-1);
if(node->slnode_cursor_refcount == 0)
{
free(node);
}
return;
}
havemore = slcursor_step_ahead (c);
}
}
/**
* Return the size of the slist.
* @param c slist cursor which refers to a list
* @return nummber of elements in the list
*/
size_t slist_size(slist_cursor_t* c)
{
return c->slcursor_list->slist_nelems;
}
void slist_done(
slist_cursor_t* c)
@ -2176,3 +2229,22 @@ strip_escape_chars (char* val)
}
return true;
}
/**
* Calculate a hash value for a null-terminated string.
* @param key String to hash
* @return Hash value of the string
*/
int simple_str_hash(char* key)
{
if(key == NULL){
return 0;
}
int hash = 0,c = 0;
char* ptr = key;
while((c = *ptr++)){
hash = c + (hash << 6) + (hash << 16) - hash;
}
return hash;
}

View File

@ -84,8 +84,9 @@ EXTERN_C_BLOCK_BEGIN
slist_cursor_t* slist_init(void);
void slist_done(slist_cursor_t* c);
size_t slist_size(slist_cursor_t* c);
void slcursor_add_data(slist_cursor_t* c, void* data);
void slcursor_remove_data(slist_cursor_t* c);
void* slcursor_get_data(slist_cursor_t* c);
bool slcursor_move_to_begin(slist_cursor_t* c);
@ -202,6 +203,9 @@ char* replace_literal(char* haystack,
const char* replacement);
bool is_valid_posix_path(char* path);
bool strip_escape_chars(char*);
int simple_str_hash(char* key);
EXTERN_C_BLOCK_END
#endif /* SKYGW_UTILS_H */