From d6d566539471b15b8853751576e5d46b11842ea8 Mon Sep 17 00:00:00 2001 From: Kaian Date: Mon, 30 May 2016 17:02:37 +0200 Subject: [PATCH] htable: Custom hashtable implementation --- src/Makefile.am | 2 +- src/hash.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++ src/hash.h | 76 +++++++++++++++++++++++++ src/sip.c | 33 +++++------ src/sip.h | 3 + src/vector.c | 4 ++ tests/Makefile.am | 3 +- tests/test_010.c | 104 +++++++++++++++++++++++++++++++++ 8 files changed, 345 insertions(+), 22 deletions(-) create mode 100644 src/hash.c create mode 100644 src/hash.h create mode 100644 tests/test_010.c diff --git a/src/Makefile.am b/src/Makefile.am index 11b0b82..931ee06 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..5d16344 --- /dev/null +++ b/src/hash.c @@ -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 . + ** + ****************************************************************************/ +/** + * @file hash.c + * @author Ivan Alonso [aka Kaian] + * + * @brief Source code of functions defined in hash.h + * + */ +#include "hash.h" +#include +#include + +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); +} diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..c7defd4 --- /dev/null +++ b/src/hash.h @@ -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 . + ** + ****************************************************************************/ +/** + * @file hash.h + * @author Ivan Alonso [aka Kaian] + * + * @brief Functions to manage hash tables + */ + +#ifndef __SNGREP_HASH_H_ +#define __SNGREP_HASH_H_ + +#include "config.h" +#include + +//! 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_ */ diff --git a/src/sip.c b/src/sip.c index 68ab856..32155fa 100644 --- a/src/sip.c +++ b/src/sip.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #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 diff --git a/src/sip.h b/src/sip.h index 7258356..a1e1c47 100644 --- a/src/sip.h +++ b/src/sip.h @@ -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; diff --git a/src/vector.c b/src/vector.c index 6a72a42..59215f2 100644 --- a/src/vector.c +++ b/src/vector.c @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index c65b9ed..dccee8c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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) diff --git a/tests/test_010.c b/tests/test_010.c new file mode 100644 index 0000000..019c2ce --- /dev/null +++ b/tests/test_010.c @@ -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 . + ** + ****************************************************************************/ +/** + * @file test_vector.c + * @author Ivan Alonso [aka Kaian] + * + * Basic testing of vector structures + */ + +#include "config.h" +#include +#include +#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; +}