call_site_XXX moved to C language, added my oldest hash_map
implementation
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "dwarf_call_site.h"
|
||||
#include "dwarf_utils.h"
|
||||
#include "hash_multimap.h"
|
||||
#include "dwarf_utils.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_call_site_param
|
||||
@@ -100,8 +102,7 @@ bool site_handle_dwarf(pst_call_site* site, Dwarf_Die* child)
|
||||
Dwarf_Attribute* attr;
|
||||
|
||||
do {
|
||||
switch(dwarf_tag(child))
|
||||
{
|
||||
switch(dwarf_tag(child)) {
|
||||
case DW_TAG_GNU_call_site_parameter: {
|
||||
Dwarf_Addr 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "list_head.h"
|
||||
#include "context.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();
|
||||
void pst_call_site_param_fini(pst_call_site_param* param);
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DW_TAG_call_site
|
||||
// -----------------------------------------------------------------------------------
|
||||
typedef struct pst_call_site {
|
||||
list_node node; // uplink. !!! must be first !!!
|
||||
hash_node tgt_node;
|
||||
hash_node org_node;
|
||||
|
||||
// methods
|
||||
pst_call_site_param* (*add_param) (pst_call_site* site);
|
||||
@@ -58,6 +62,30 @@ typedef struct pst_call_site {
|
||||
pst_context* ctx; // execution context
|
||||
bool allocated; // whether this object was allocated or not
|
||||
} 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_ */
|
||||
|
||||
+26
-115
@@ -5,6 +5,8 @@
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <dwarf.h>
|
||||
|
||||
#include "dwarf_function.h"
|
||||
|
||||
|
||||
@@ -53,66 +55,6 @@ pst_parameter* __pst_function::next_param(pst_parameter* p)
|
||||
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()
|
||||
{
|
||||
char* at = NULL;
|
||||
@@ -241,61 +183,6 @@ bool __pst_function::handle_lexical_block(Dwarf_Die* result)
|
||||
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)
|
||||
{
|
||||
die = d;
|
||||
@@ -537,3 +424,27 @@ bool __pst_function::get_frame()
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,44 +17,18 @@
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_function
|
||||
// -----------------------------------------------------------------------------------
|
||||
typedef struct __pst_function : public SC_ListNode {
|
||||
__pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
typedef struct __pst_function {
|
||||
list_node node; // uplink. !!! must be first !!!
|
||||
|
||||
void clear();
|
||||
pst_parameter* add_param();
|
||||
void del_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 handle_dwarf(Dwarf_Die* d);
|
||||
bool print_dwarf();
|
||||
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();
|
||||
|
||||
@@ -64,20 +38,18 @@ typedef struct __pst_function : public SC_ListNode {
|
||||
std::string name; // function's name
|
||||
SC_ListHead params; // function's parameters
|
||||
|
||||
// call-site
|
||||
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
|
||||
pst_call_site_storage call_sites;
|
||||
|
||||
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 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
|
||||
int line; // line in code 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)
|
||||
Dwarf_Frame* frame; // function's stack frame
|
||||
pst_context* ctx; // context of unwinding
|
||||
bool allocated; // whether this object was allocated or not
|
||||
} pst_function;
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user