call_site_XXX moved to C language, added my oldest hash_map

implementation
This commit is contained in:
2020-02-01 21:19:55 +04:00
parent dbd8e31129
commit 0064239418
6 changed files with 664 additions and 164 deletions
+167 -3
View File
@@ -9,6 +9,8 @@
#include "dwarf_call_site.h" #include "dwarf_call_site.h"
#include "dwarf_utils.h" #include "dwarf_utils.h"
#include "hash_multimap.h"
#include "dwarf_utils.h"
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// pst_call_site_param // pst_call_site_param
@@ -100,8 +102,7 @@ bool site_handle_dwarf(pst_call_site* site, Dwarf_Die* child)
Dwarf_Attribute* attr; Dwarf_Attribute* attr;
do { do {
switch(dwarf_tag(child)) switch(dwarf_tag(child)) {
{
case DW_TAG_GNU_call_site_parameter: { case DW_TAG_GNU_call_site_parameter: {
Dwarf_Addr pc; Dwarf_Addr pc;
unw_get_reg(site->ctx->curr_frame, UNW_REG_IP, &pc); unw_get_reg(site->ctx->curr_frame, UNW_REG_IP, &pc);
@@ -144,7 +145,8 @@ bool site_handle_dwarf(pst_call_site* site, Dwarf_Die* child)
return true; return true;
} }
void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn) { void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn)
{
// methods // methods
@@ -186,3 +188,165 @@ void pst_call_site_fini(pst_call_site* site)
} }
} }
// -----------------------------------------------------------------------------------
// pst_call_site_storage
// -----------------------------------------------------------------------------------
// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually.
// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it.
// probably by invoke by:
// Dwarf_Die *scopes;
// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes
// if (n <= 0) -> FAILURE
// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars()
// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it
// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee
// get DIE of return type
bool storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result)
{
Dwarf_Die origin;
Dwarf_Attribute attr_mem;
Dwarf_Attribute* attr;
pst_log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:");
// reference to DIE which represents callee's parameter if compiler knows where it is at compile time
const char* oname = NULL;
if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) {
oname = dwarf_diename(&origin);
pst_log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname);
}
// The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at
// compile time which subprogram will be called the expression computes the address of the subprogram that will be called.
uint64_t target = 0;
if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) {
attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem);
if(attr) {
pst_dwarf_expr expr;
pst_dwarf_expr_init(&expr);
if(handle_location(storage->ctx, &attr_mem, expr, pc, this)) {
target = expr.value;
pst_log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target);
}
pst_dwarf_expr_fini(&expr);
}
}
if(target == 0 && oname == NULL) {
pst_log(SEVERITY_ERROR, "Cannot determine both call-site target and origin");
return false;
}
Dwarf_Die child;
if(dwarf_child (result, &child) == 0) {
pst_call_site* st = storage->add_call_site(storage, target, oname);
if(!st->handle_dwarf(st, &child)) {
storage->del_call_site(storage, st);
return false;
}
}
return true;
}
pst_call_site* storage_call_site_by_origin(pst_call_site_storage* storage, const char* origin)
{
pst_call_site* ret = NULL;
hash_node* node = hash_find(&storage->cs_to_origin, origin, strlen(origin));
if(node) {
ret = hash_entry(node, pst_call_site, node);
}
return ret;
}
pst_call_site* storage_call_site_by_target(pst_call_site_storage* storage, uint64_t target)
{
pst_call_site* ret = NULL;
hash_node* node = hash_find(&storage->cs_to_target, (char*)&target, sizeof(target));
if(node) {
ret = hash_entry(node, pst_call_site, node);
}
return ret;
}
pst_call_site* storage_add_call_site(pst_call_site_storage* storage, uint64_t target, const char* origin)
{
pst_new(pst_call_site, st, storage->ctx, target, origin);
list_add_bottom(&storage->call_sites, &st->node);
if(target) {
hash_add(&storage->cs_to_target, &st->tgt_node, &target, sizeof(target));
} else if(origin) {
hash_add(&storage->cs_to_origin, &st->org_node, (char*)origin, strlen(origin));
}
return st;
}
void storage_del_call_site(pst_call_site_storage* storage, pst_call_site* st)
{
hash_node* node = NULL;
list_del(&st->node);
if(st->target) {
node = hash_find(&storage->cs_to_target, &st->target, sizeof(st->target));
} else if(st->origin) {
node = hash_find(&storage->cs_to_origin, st->origin, strlen(st->origin));
}
if(node) {
hash_del(node);
}
pst_free(st);
}
pst_call_site* storage_next_call_site(pst_call_site_storage* storage, pst_call_site* st)
{
struct list_node* n = (st == NULL) ? list_first(&storage->call_sites) : list_next(&st->node);
pst_call_site* ret = NULL;
if(n) {
ret = list_entry(n, pst_call_site, node);
}
return ret;
}
pst_call_site* storage_find_call_site(pst_call_site_storage* storage, pst_function* callee)
{
uint64_t start_pc = storage->ctx->base_addr + callee->lowpc;
pst_call_site* cs = storage_call_site_by_target(storage, start_pc);
if(!cs) {
cs = storage_call_site_by_origin(storage, callee->name.c_str());
}
return cs;
}
void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx)
{
storage->ctx = ctx;
list_head_init(&storage->call_sites);
hash_head_init(&storage->cs_to_target);
hash_head_init(&storage->cs_to_origin);
storage->allocated = false;
}
pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx)
{
pst_call_site_storage* ns = pst_alloc(pst_call_site_storage);
if(ns) {
pst_call_site_storage_init(ns, ctx);
ns->allocated = true;
}
return ns;
}
void pst_call_site_storage_fini(pst_call_site_storage* storage)
{
}
+28
View File
@@ -14,6 +14,7 @@
#include "list_head.h" #include "list_head.h"
#include "context.h" #include "context.h"
#include "dwarf_expression.h" #include "dwarf_expression.h"
#include "hash_multimap.h"
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
@@ -37,11 +38,14 @@ void pst_call_site_param_init(pst_call_site_param* param);
pst_call_site_param* pst_call_site_param_new(); pst_call_site_param* pst_call_site_param_new();
void pst_call_site_param_fini(pst_call_site_param* param); void pst_call_site_param_fini(pst_call_site_param* param);
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// DW_TAG_call_site // DW_TAG_call_site
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
typedef struct pst_call_site { typedef struct pst_call_site {
list_node node; // uplink. !!! must be first !!! list_node node; // uplink. !!! must be first !!!
hash_node tgt_node;
hash_node org_node;
// methods // methods
pst_call_site_param* (*add_param) (pst_call_site* site); pst_call_site_param* (*add_param) (pst_call_site* site);
@@ -58,6 +62,30 @@ typedef struct pst_call_site {
pst_context* ctx; // execution context pst_context* ctx; // execution context
bool allocated; // whether this object was allocated or not bool allocated; // whether this object was allocated or not
} pst_call_site; } pst_call_site;
void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn);
pst_call_site* pst_call_site_new(pst_context* c, uint64_t tgt, const char* orn);
void pst_call_site_fini(pst_call_site* site);
// -----------------------------------------------------------------------------------
// storage for all of function's call sites
// -----------------------------------------------------------------------------------
typedef struct pst_call_site_storage {
// methods
bool (*handle_dwarf) (pst_call_site_storage* storage, Dwarf_Die* result);
pst_call_site* (*add_call_site) (pst_call_site_storage* storage, uint64_t target, const char* origin);
void (*del_call_site) (pst_call_site_storage* storage, pst_call_site* site);
pst_call_site* (*find_call_site) (pst_call_site_storage* storage, pst_function* callee);
pst_call_site* (*next_call_site) (pst_call_site_storage* storage, pst_call_site* st);
// fields
pst_context* ctx;
list_head call_sites; // Call-Site definitions
hash_head cs_to_target; // map pointer to caller to call-site
hash_head cs_to_origin; // map caller name to call-site
bool allocated; // whether this object was allocated or not
} pst_call_site_storage;
void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx);
pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx);
#endif /* FRAMEWORK_DWARF_CALL_SITE_H_ */ #endif /* FRAMEWORK_DWARF_CALL_SITE_H_ */
+26 -115
View File
@@ -5,6 +5,8 @@
* Author: nnosov * Author: nnosov
*/ */
#include <dwarf.h>
#include "dwarf_function.h" #include "dwarf_function.h"
@@ -53,66 +55,6 @@ pst_parameter* __pst_function::next_param(pst_parameter* p)
return next; return next;
} }
pst_call_site* __pst_function::add_call_site(uint64_t target, const char* origin)
{
pst_call_site* st = new pst_call_site(ctx, target, origin);
call_sites.InsertLast(st);
if(target) {
mCallStToTarget.Insert(&target, sizeof(target), st);
}
if(origin) {
mCallStToOrigin.Insert(origin, strlen(origin), st);
}
return st;
}
void __pst_function::del_call_site(pst_call_site* st)
{
call_sites.Remove(st);
if(st->target && mCallStToTarget.Lookup(&st->target, sizeof(st->target))) {
mCallStToTarget.Remove();
}
if(st->origin && mCallStToTarget.Lookup(st->origin, strlen(st->origin))) {
mCallStToTarget.Remove();
}
delete st;
}
pst_call_site* __pst_function::next_call_site(pst_call_site* st)
{
pst_call_site* next = NULL;
if(st) {
next = (pst_call_site*)call_sites.First();
} else {
next = (pst_call_site*)call_sites.Next(st);
}
return next;
}
pst_call_site* __pst_function::call_site_by_origin(const char* origin)
{
return (pst_call_site*)mCallStToOrigin.Lookup(origin, strlen(origin));
}
pst_call_site* __pst_function::call_site_by_target(uint64_t target)
{
return (pst_call_site*)mCallStToTarget.Lookup(&target, sizeof(target));
}
pst_call_site* __pst_function::find_call_site(pst_function* callee)
{
uint64_t start_pc = ctx->base_addr + callee->lowpc;
pst_call_site* cs = (pst_call_site*)mCallStToTarget.Lookup(&start_pc, sizeof(start_pc));
if(!cs) {
cs = (pst_call_site*)mCallStToOrigin.Lookup(callee->name.c_str(), strlen(callee->name.c_str()));
}
return cs;
}
bool __pst_function::print_dwarf() bool __pst_function::print_dwarf()
{ {
char* at = NULL; char* at = NULL;
@@ -241,61 +183,6 @@ bool __pst_function::handle_lexical_block(Dwarf_Die* result)
return true; return true;
} }
// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually.
// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it.
// probably by invoke by:
// Dwarf_Die *scopes;
// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes
// if (n <= 0) -> FAILURE
// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars()
// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it
// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee
// get DIE of return type
bool __pst_function::handle_call_site(Dwarf_Die* result)
{
Dwarf_Die origin;
Dwarf_Attribute attr_mem;
Dwarf_Attribute* attr;
pst_log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:");
// reference to DIE which represents callee's parameter if compiler knows where it is at compile time
const char* oname = NULL;
if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) {
oname = dwarf_diename(&origin);
pst_log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname);
}
// The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at
// compile time which subprogram will be called the expression computes the address of the subprogram that will be called.
uint64_t target = 0;
if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) {
attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem);
if(attr) {
pst_dwarf_expr expr(ctx->alloc);
if(handle_location(ctx, &attr_mem, expr, pc, this)) {
target = expr.value;
pst_log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target);
}
}
}
if(target == 0 && oname == NULL) {
pst_log(SEVERITY_ERROR, "Cannot determine both call-site target and origin");
return false;
}
Dwarf_Die child;
if(dwarf_child (result, &child) == 0) {
pst_call_site* st = add_call_site(target, oname);
if(!st->handle_dwarf(&child)) {
del_call_site(st);
return false;
}
}
return true;
}
bool __pst_function::handle_dwarf(Dwarf_Die* d) bool __pst_function::handle_dwarf(Dwarf_Die* d)
{ {
die = d; die = d;
@@ -537,3 +424,27 @@ bool __pst_function::get_frame()
return true; return true;
} }
void pst_function_init(pst_function* fun, pst_context* _ctx, __pst_function* _parent)
{
list_node_init(&fun->node);
fun->pc = 0;
fun->line = -1;
fun->die = NULL;
fun->lowpc = 0;
fun->highpc = 0;
memcpy(&fun->cursor, _ctx->curr_frame, sizeof(fun->cursor));
pst_call_site_storage_init(&fun->call_sites, fun->ctx);
fun->parent = _parent;
fun->sp = 0;
fun->cfa = 0;
fun->frame = NULL;
fun->name = NULL;
}
void pst_function_fini(pst_function* fun)
{
clear();
if(frame) {
free(frame);
}
}
+18 -46
View File
@@ -17,67 +17,39 @@
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// pst_function // pst_function
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
typedef struct __pst_function : public SC_ListNode { typedef struct __pst_function {
__pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) { list_node node; // uplink. !!! must be first !!!
pc = 0;
line = -1;
die = NULL;
lowpc = 0;
highpc = 0;
memcpy(&cursor, _ctx->curr_frame, sizeof(cursor));
parent = _parent;
sp = 0;
cfa = 0;
frame = NULL;
}
~__pst_function() {
clear();
if(frame) {
free(frame);
}
}
void clear(); void clear();
pst_parameter* add_param(); pst_parameter* add_param();
void del_param(pst_parameter* p); void del_param(pst_parameter* p);
pst_parameter* next_param(pst_parameter* p); pst_parameter* next_param(pst_parameter* p);
pst_call_site* add_call_site(uint64_t target, const char* origin);
void del_call_site(pst_call_site* st);
pst_call_site* next_call_site(pst_call_site* st);
pst_call_site* call_site_by_origin(const char* origin);
pst_call_site* call_site_by_target(uint64_t target);
bool unwind(Dwarf_Addr addr); bool unwind(Dwarf_Addr addr);
bool handle_dwarf(Dwarf_Die* d); bool handle_dwarf(Dwarf_Die* d);
bool print_dwarf(); bool print_dwarf();
bool handle_lexical_block(Dwarf_Die* result); bool handle_lexical_block(Dwarf_Die* result);
bool handle_call_site(Dwarf_Die* result);
pst_call_site* find_call_site(__pst_function* callee);
bool get_frame(); bool get_frame();
Dwarf_Addr lowpc; // offset to start of the function against base address Dwarf_Addr lowpc; // offset to start of the function against base address
Dwarf_Addr highpc; // offset to the next address after the end of the function against base address Dwarf_Addr highpc; // offset to the next address after the end of the function against base address
Dwarf_Die* die; // DWARF DIE containing definition of the function Dwarf_Die* die; // DWARF DIE containing definition of the function
std::string name; // function's name std::string name; // function's name
SC_ListHead params; // function's parameters SC_ListHead params; // function's parameters
// call-site pst_call_site_storage call_sites;
SC_ListHead call_sites; // Call-Site definitions
SC_Dict mCallStToTarget; // map pointer to caller to call-site
SC_Dict mCallStToOrigin; // map caller name to call-site
unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command unw_word_t pc; // address between LowPC & HighPC (plus base address offset). actually, currently executed command
unw_word_t sp; // SP register in function's frame unw_word_t sp; // SP register in function's frame
unw_word_t cfa; // CFA (Canonical Frame Adress) of the function unw_word_t cfa; // CFA (Canonical Frame Address) of the function
unw_cursor_t cursor; // copy of stack state of the function unw_cursor_t cursor; // copy of stack state of the function
int line; // line in code where function is defined int line; // line in code where function is defined
std::string file; // file name (DWARF Compilation Unit) where function is defined std::string file; // file name (DWARF Compilation Unit) where function is defined
__pst_function* parent; // parent function in call trace (caller) __pst_function* parent; // parent function in call trace (caller)
Dwarf_Frame* frame; // function's stack frame Dwarf_Frame* frame; // function's stack frame
pst_context* ctx; // context of unwinding pst_context* ctx; // context of unwinding
bool allocated; // whether this object was allocated or not
} pst_function; } pst_function;
+316
View File
@@ -0,0 +1,316 @@
/* =============================================================================
* CDL (Configuration Definition Language) validator $Revision: 1.4 $
* (C)2004-2007 Nikolai Nosov. All rights reserved.
*
* File: $RCSfile: hash_multimap.c,v $
* Purpose: Hash Multimap implementation
* Written by: Nikolai Nosov nnosov@gmail.com
* Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $.
*
* For more information please visit
* http://cdl.sourceforge.net
* ===========================================================================*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "hash_multimap.h"
/* Default hash function
@param key hash key
@param size hash key size in bytes
@return hash value, depends of the hash key
*/
static unsigned int default_hash_fn(const char *key, int size)
{
register int i, j;
unsigned int cs = 0;
unsigned int ps = 0;
if(key) {
for(i = 0, j = 0; i < size; i++, j++) {
ps |= ((unsigned int)key[i]) << (j * 8);
if(j == 3 || j == (size - 1)) {
cs ^= ps;
ps = 0;
j = 0;
}
}
}
return cs;
}
static int default_compare_fn(const char *key1, const char *key2, const int size)
{
return !memcmp(key1, key2, size);
}
/** Initialize hash node. Must be called before first usage of the node.
@param node pointer to the hash node descriptor
*/
void hash_node_init(struct hash_node *node)
{
node->key = NULL;
node->key_size = 0;
list_node_init(&node->node);
}
/** Cleanup hash node
@param node pointer to the hash node descriptor
*/
void hash_node_cleanup(struct hash_node *node)
{
if(node->key) free(node->key);
list_node_init(&node->node);
node->key = NULL;
node->key_size = 0;
}
/** Initialize hash table
@param map pointer to the hash table descriptor
@param hash_shift size of the hash table in bits. Should be greater than
HASH_MIN_SHIFT and less than HASH_MAX_SHIFT. In case of less than HASH_MIN_SHIFT,
will be followed by HASH_MIN_SHIFT, in case of greater HASH_MAX_SHIFT will be
followed by HASH_MAX_SHIFT
@param hf pointer to the user defined hash function. If NULL, then default hash
function will be used
@param cf pointer to the user defined compare function. if NULL, then the default
compare function will be used
*/
int hash_head_init(struct hash_head *map, unsigned int hash_shift, _hash_fn hf, _compare_fn cf)
{
int i;
if(map) {
if(hash_shift <= HASH_MIN_SHIFT) {
map->hash_shift = HASH_MIN_SHIFT;
} else if(hash_shift >= HASH_MAX_SHIFT) {
map->hash_shift = HASH_MAX_SHIFT;
} else {
map->hash_shift = hash_shift;
}
map->hash_size = 1UL << map->hash_shift;
map->hash_mask = (map->hash_size - 1);
map->bucket = (list_head*)malloc(sizeof(list_head) * map->hash_size);
if(map->bucket) {
for(uint16_t i = 0; i < map->hash_size; i++) {
list_head_init(map->bucket + i);
}
if(hf) {
map->hash_fn = hf;
} else {
map->hash_fn = default_hash_fn;
}
if(cf) {
map->compare_fn = cf;
} else {
map->compare_fn = default_compare_fn;
}
} else {
return ENOMEM;
}
} else {
return ENODEV;
}
return 0;
}
/** Cleanup hash table descriptor
@param map pointer to the hash table descriptor
*/
void hash_head_cleanup(struct hash_head *map)
{
struct list_node *p, *n;
struct hash_node *node;
int i;
if(map) {
if(map->bucket) {
for(i = 0; i < map->hash_size; i++) {
list_for_each_safe(p, n, map->bucket + i) {
node = list_entry(p, struct hash_node, node);
hash_node_cleanup(node);
}
}
free(map->bucket);
}
}
}
/** Find first node which key is equal to the key represented
@param map pointer to the hash table descriptor
@param key pointer to the hash key
@param key_size size of the hash key
@return pointer to the key found, or NULL in case of hash table have not nodes
with the key equals to the key represented
*/
struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size)
{
struct list_head *list;
struct list_node *n;
struct hash_node *node;
list = map->bucket + (map->hash_fn(key, key_size) & map->hash_mask);
list_for_each(n, list) {
node = list_entry(n, struct hash_node, node);
if(key_size == node->key_size) {
if(map->compare_fn(key, node->key, key_size)) {
return node;
}
}
}
return NULL;
}
/** Find next node, which key is equal to the key represented
@param hn1 pointer to the previous node found.
@return pointer to the next node with the same key, or NULL in case of the current
node is the last node with appropriate key
*/
struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *hn1)
{
struct list_node *n = &hn1->node;
struct hash_node *hn2;
while((n = list_next(n)) != NULL) {
hn2 = list_entry(n, struct hash_node, node);
if(hn1->key_size == hn2->key_size) {
if(map->compare_fn(hn1->key, hn2->key, hn2->key_size)) return hn2;
}
}
return NULL;
}
/** Add node to the hash table
@param map pointer to the hash table
@param node pointer to the node, which will be added to the table
@param key pointer to the hash key
@param key_size size of the hash key
@return zero in case of success, ENOMEM in case of error
*/
int hash_add(struct hash_head *map, struct hash_node *node, void *key, int key_size)
{
node->key = (char*)malloc(key_size);
if(node->key) {
memcpy(node->key, key, key_size);
node->key_size = key_size;
list_add_bottom(map->bucket + (map->hash_fn(key, key_size) & map->hash_mask), &node->node);
return 0;
}
return ENOMEM;
}
/** Remove node from the hash table
@param node pointer to the node descriptor
*/
void hash_del(struct hash_node *node)
{
list_del_init(&node->node);
hash_node_cleanup(node);
}
/** Initialize iterator for usage. Must be called before first iterator usage.
@param map pointer to the hash table descriptor
@param iter pointer to the iterator descriptor
*/
void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter)
{
iter->map = map;
iter->map_idx = 0;
iter->current = NULL;
}
/** Move iterator to the first node in the table, and return pointer to the node
@param iter pointer to the iterator descriptor
@return pointer to the first node in the table, or NULL in case of error
*/
struct hash_node * hash_node_first(struct hash_iterator *iter)
{
iter->current = list_first(iter->map->bucket);
iter->map_idx = 0;
if (iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
return NULL;
}
/** Move iterator to the next node in the table and return pointer to the node
@param iter pointer to the iterator descriptor
@return pointer to the next node in the table, or NULL in case of error
*/
struct hash_node * hash_node_next(struct hash_iterator *iter)
{
if(iter->map_idx >= iter->map->hash_size) {
return NULL;
}
if(iter->current) {
iter->current = list_next(iter->current);
}
if(iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
while(iter->map_idx < iter->map->hash_size) {
iter->current = list_first(iter->map->bucket + iter->map_idx);
iter->map_idx++;
if(iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
}
return NULL;
}
/** Remove current node, pointed by iterator, and move iterator to the next node
@param iter pointer to the iterator descriptor
*/
void hash_node_del(struct hash_iterator *iter)
{
struct list_node *tmp;
tmp = iter->current;
hash_node_next(iter);
list_del_init(tmp);
}
/** Return total count of elements in the hash table
@param head pointer to the hash table descriptor
*/
int hash_count(struct hash_head *head)
{
int i;
int count = 0;
for(i = 0; i < head->hash_size; i++) {
count += list_count(head->bucket + i);
}
return count;
}
+109
View File
@@ -0,0 +1,109 @@
#ifndef HASH_MULTIMAP_H
#define HASH_MULTIMAP_H
/* =============================================================================
* CDL (Configuration Definition Language) validator $Revision: 1.4 $
* (C)2004-2007 Nikolai Nosov. All rights reserved.
*
* File: $RCSfile: hash_multimap.c,v $
* Purpose: Hash Multimap implementation
* Written by: Nikolai Nosov nnosov@gmail.com
* Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $.
*
* For more information please visit
* http://cdl.sourceforge.net
* ===========================================================================*/
#include <stdint.h>
#include "list_head.h"
#define HASH_MIN_SHIFT (8)
#define HASH_MAX_SHIFT (15)
#define hash_entry(ptr, type, member) container_of(ptr, type, member)
/** Hash multimap node descriptor
@param key hash key value
@param key_size hash key size in bytes
@param node uplink to hash multimap table
*/
typedef struct hash_node
{
char* key;
int key_size;
list_node node;
} hash_node;
/** Hash function prototype
@param key hash key value
@param key_size hash key size in bytes
@return hash value
*/
typedef unsigned int (* _hash_fn)(const char *key, int key_size);
/** Compare function prototype
*
* @param key1 pointer to the first key for comparison
* @param key2 pointer to the second key for comparison
* @param size size of compared part of the keys in bytes
*
* @return zero if keys equals each other, othervize 1
*/
typedef int (*_compare_fn)(const char *key1, const char *key2, int size);
/** Hash multimap table descriptor
@param hash_shift size of hash table as power of 2
@param hash_size size of the hash table
@param hash_mask mask for the hash value
@param bucket pointer to the array of linked lists. in other words, pointer to
hash multimap table
@param count count of the nodes in the hash table
*/
typedef struct hash_head {
uint8_t hash_shift;
uint16_t hash_size;
uint32_t hash_mask;
list_head* bucket;
_hash_fn hash_fn;
_compare_fn compare_fn;
} hash_head;
/** Hash table iterator
@param map pointer to the hash table descriptor, used for iteration
@param current pointer to the current list node (bucket[map_idx])
@param map_idx current bucket index
*/
struct hash_iterator {
hash_head* map; // pointer to the hash map header
list_node* current; // pointer to current list node in the bucket[map_idx] list
int map_idx; // index of current list in the hash map bucket
};
void hash_node_init(struct hash_node *node);
void hash_node_cleanup(struct hash_node *node);
unsigned int default_hash_fn(const char *key, int size);
int default_compare_fn(const char *key1, const char *key2, const int size);
int hash_head_init(struct hash_head *map, unsigned int hash_shift = 8, _hash_fn hf = default_hash_fn, _compare_fn cf = default_compare_fn);
void hash_head_cleanup(struct hash_head *map);
struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size);
struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *node);
int hash_add(struct hash_head *map, struct hash_node *node, void *key, int key_size);
void hash_del(struct hash_node *node);
void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter);
struct hash_node * hash_node_first(struct hash_iterator *iter);
struct hash_node * hash_node_next(struct hash_iterator *iter);
void hash_node_del(struct hash_iterator *iter);
int hash_count(struct hash_head *head);
#endif // HASH_MULTIMAP_H