htable: Custom hashtable implementation

This commit is contained in:
Kaian 2016-05-30 17:02:37 +02:00
parent 03a0ebc7d0
commit d6d5665394
8 changed files with 345 additions and 22 deletions

View File

@ -12,7 +12,7 @@ sngrep_SOURCES+=capture_openssl.c
endif
sngrep_SOURCES+=address.c packet.c sip.c sip_call.c sip_msg.c sip_attr.c main.c
sngrep_SOURCES+=option.c group.c filter.c keybinding.c media.c setting.c rtp.c
sngrep_SOURCES+=util.c vector.c ui_panel.c scrollbar.c
sngrep_SOURCES+=util.c hash.c vector.c ui_panel.c scrollbar.c
sngrep_SOURCES+=ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c
sngrep_SOURCES+=ui_stats.c ui_filter.c ui_save.c ui_msg_diff.c
sngrep_SOURCES+=ui_column_select.c ui_settings.c

142
src/hash.c Normal file
View File

@ -0,0 +1,142 @@
/**************************************************************************
**
** sngrep - SIP Messages flow viewer
**
** Copyright (C) 2013-2016 Ivan Alonso (Kaian)
** Copyright (C) 2013-2016 Irontec SL. All rights reserved.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
/**
* @file hash.c
* @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
*
* @brief Source code of functions defined in hash.h
*
*/
#include "hash.h"
#include <string.h>
#include <stdlib.h>
htable_t *
htable_create(size_t size)
{
htable_t *h;
// Allocate memory for this table data
if (!(h = malloc(sizeof(htable_t))))
return NULL;
h->size = size;
// Allocate memory for this table buckets
if (!(h->buckets = malloc(sizeof(hentry_t) * size))) {
free(h);
return NULL;
}
// Initialize allocated memory
memset(h->buckets, 0, sizeof(hentry_t) * size);
// Return allocated table
return h;
}
void
htable_destroy(htable_t *table)
{
free(table->buckets);
free(table);
}
int
htable_insert(htable_t *table, const char *key, void *data)
{
// Get hash position for given entry
size_t pos = htable_hash(table, key);
// Create a new entry for given key
hentry_t *entry;
if (!(entry = malloc(sizeof(hentry_t))))
return -1;
entry->key = key;
entry->data = data;
entry->next = 0;
// Check if the hash position is in use
hentry_t *exists = table->buckets[pos];
if (!exists) {
table->buckets[pos] = entry;
} else {
while (exists->next) {
exists = exists->next;
}
exists->next = entry;
}
return 0;
}
void
htable_remove(htable_t *table, const char *key)
{
// Get hash position for given entry
size_t pos = htable_hash(table, key);
// Check if the hash position is in use
hentry_t *entry, *prev = NULL;
for (entry = table->buckets[pos]; entry; prev = entry, entry = entry->next) {
if (!strcmp(entry->key, key)) {
if (prev) {
prev->next = entry->next;
} else {
table->buckets[pos] = entry->next;
}
// Remove item memory
free(entry);
}
}
}
void *
htable_find(htable_t *table, const char *key)
{
// Get hash position for given entry
size_t pos = htable_hash(table, key);
// Check if the hash position is in use
hentry_t *entry;
for (entry = table->buckets[pos]; entry; entry = entry->next) {
if (!strcmp(entry->key, key)) {
//! Found
return entry->data;
}
}
// Not found
return NULL;
}
size_t
htable_hash(htable_t *table, const char *key)
{
// dbj2 - http://www.cse.yorku.ca/~oz/hash.html
size_t hash = 5381;
while (*key++) {
hash = ((hash << 5) + hash) ^ *key;
}
return hash & (table->size - 1);
}

76
src/hash.h Normal file
View File

@ -0,0 +1,76 @@
/**************************************************************************
**
** sngrep - SIP Messages flow viewer
**
** Copyright (C) 2013-2016 Ivan Alonso (Kaian)
** Copyright (C) 2013-2016 Irontec SL. All rights reserved.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
/**
* @file hash.h
* @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
*
* @brief Functions to manage hash tables
*/
#ifndef __SNGREP_HASH_H_
#define __SNGREP_HASH_H_
#include "config.h"
#include <stdio.h>
//! Shorter declaration of hash structures
typedef struct htable htable_t;
typedef struct hentry hentry_t;
/**
* Structure to hold a Hash table entry
*/
struct hentry {
//! Key of the hash entry
const char *key;
//! Pointer to has entry data
void *data;
//! Next entry sharing the same hash value
hentry_t *next;
};
struct htable {
//! Fixed hash table limit
size_t size;
// Hash table entries
hentry_t **buckets;
};
htable_t *
htable_create(size_t size);
void
htable_destroy(htable_t *table);
int
htable_insert(htable_t *table, const char *key, void *data);
void
htable_remove(htable_t *table, const char *key);
void *
htable_find(htable_t *table, const char *key);
size_t
htable_hash(htable_t *table, const char *key);
#endif /* __SNGREP_HASH_H_ */

View File

@ -31,7 +31,6 @@
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <search.h>
#include <stdarg.h>
#include "sip.h"
#include "option.h"
@ -155,7 +154,7 @@ sip_init(int limit, int only_calls, int no_incomplete)
calls.active = vector_create(10, 10);
// Create hash table for callid search
hcreate(calls.limit);
calls.callids = htable_create(calls.limit);
// By default sort by call index ascending
calls.sort.by = SIP_ATTR_CALLINDEX;
@ -182,7 +181,7 @@ sip_deinit()
// Remove all calls
sip_calls_clear();
// Remove Call-id hash table
hdestroy();
htable_destroy(calls.callids);
// Remove calls vector
vector_destroy(calls.list);
vector_destroy(calls.active);
@ -278,7 +277,6 @@ sip_validate_packet(packet_t *packet)
sip_msg_t *
sip_check_packet(packet_t *packet)
{
ENTRY entry;
sip_msg_t *msg;
sip_call_t *call;
char callid[1024], xcallid[1024];
@ -346,10 +344,8 @@ sip_check_packet(packet_t *packet)
if (!(call = call_create(callid, xcallid)))
goto skip_message;
// Store this call in hash table
entry.key = (char *) call->callid;
entry.data = (void *) call;
hsearch(entry, ENTER);
// Add this Call-Id to hash table
htable_insert(calls.callids, call->callid, call);
// Set call index
call->index = ++calls.last_index;
@ -470,13 +466,7 @@ sip_find_by_index(int index)
sip_call_t *
sip_find_by_callid(const char *callid)
{
ENTRY entry, *eptr;
entry.key = (char *) callid;
if ((eptr = hsearch(entry, FIND)))
return eptr->data;
return NULL;
return htable_find(calls.callids, callid);
}
int
@ -655,8 +645,8 @@ void
sip_calls_clear()
{
// Create again the callid hash table
hdestroy();
hcreate(calls.limit);
htable_destroy(calls.callids);
calls.callids = htable_create(calls.limit);
// Remove all items from vector
vector_clear(calls.list);
vector_clear(calls.active);
@ -665,11 +655,14 @@ sip_calls_clear()
void
sip_calls_rotate()
{
// Remove first call from active and call lists
sip_call_t *call = vector_first(calls.list);
if (sip_call_is_active(call))
vector_remove(calls.active, call);
// Remove from callids hash
htable_remove(calls.callids, call->callid);
// Remove first call from active and call lists
vector_remove(calls.active, call);
vector_remove(calls.list, call);
}
int

View File

@ -40,6 +40,7 @@
#endif
#include "sip_call.h"
#include "vector.h"
#include "hash.h"
#define MAX_SIP_PAYLOAD 10240
@ -124,6 +125,8 @@ struct sip_call_list {
sip_sort_t sort;
//! Last created id
int last_index;
//! Call-Ids hash table
htable_t *callids;
// Max call limit
int limit;

View File

@ -201,6 +201,10 @@ vector_remove(vector_t *vector, void *item)
{
// Get item position
int idx = vector_index(vector, item);
// Not found in the vector
if (idx == -1)
return;
// Decrease item counter
vector->count--;
// Move the rest of the elements one position up

View File

@ -1,5 +1,5 @@
check_PROGRAMS=test-001 test-002 test-003 test-004 test-005
check_PROGRAMS+=test-006 test-007 test-008 test-009
check_PROGRAMS+=test-006 test-007 test-008 test-009 test-010
test_001_SOURCES=test_001.c
test_002_SOURCES=test_002.c
@ -10,5 +10,6 @@ test_006_SOURCES=test_006.c
test_007_SOURCES=test_007.c ../src/vector.c ../src/util.c
test_008_SOURCES=test_008.c
test_009_SOURCES=test_009.c
test_010_SOURCES=test_010.c ../src/hash.c
TESTS = $(check_PROGRAMS)

104
tests/test_010.c Normal file
View File

@ -0,0 +1,104 @@
/**************************************************************************
**
** sngrep - SIP Messages flow viewer
**
** Copyright (C) 2013-2016 Ivan Alonso (Kaian)
** Copyright (C) 2013-2016 Irontec SL. All rights reserved.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
/**
* @file test_vector.c
* @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
*
* Basic testing of vector structures
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#include "hash.h"
int main ()
{
htable_t *table;
table = htable_create(10);
assert(table);
// Check a key is not found
assert(htable_find(table, "notfoud") == NULL);
// Check a key can be added
htable_insert(table, "key", "data");
const char *data = htable_find(table, "key");
// And found
assert(strcmp(data, "data") == 0);
// Try filling all the buckets
htable_insert(table, "key1", "data1");
htable_insert(table, "key2", "data2");
htable_insert(table, "key3", "data3");
htable_insert(table, "key4", "data4");
htable_insert(table, "key5", "data5");
htable_insert(table, "key6", "data6");
htable_insert(table, "key7", "data7");
htable_insert(table, "key8", "data8");
htable_insert(table, "key9", "data9");
htable_insert(table, "key10", "data10");
htable_insert(table, "key11", "data11");
htable_insert(table, "key12", "data12");
htable_insert(table, "key13", "data13");
htable_insert(table, "key14", "data14");
htable_insert(table, "key15", "data15");
// Find one entry
const char *data7 = htable_find(table, "key7");
assert(strcmp(data7, "data7") == 0);
// Remove one entry
htable_remove(table, "key7");
assert(htable_find(table, "key7") == NULL);
// Find another entries
const char *data5 = htable_find(table, "key5");
assert(strcmp(data5, "data5") == 0);
const char *data10 = htable_find(table, "key10");
assert(strcmp(data10, "data10") == 0);
// Remove all entries
htable_remove(table, "key1");
htable_remove(table, "key2");
htable_remove(table, "key3");
htable_remove(table, "key4");
htable_remove(table, "key5");
htable_remove(table, "key6");
htable_remove(table, "key7");
htable_remove(table, "key8");
htable_remove(table, "key9");
htable_remove(table, "key10");
htable_remove(table, "key11");
htable_remove(table, "key12");
htable_remove(table, "key13");
htable_remove(table, "key14");
htable_remove(table, "key15");
// Search a not found entry
assert(htable_find(table, "key7") == NULL);
// Destroy the table
htable_destroy(table);
return 0;
}