/* Copyright 2013 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
/* Hashtable for XRay */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xray/xray_priv.h"
#if defined(XRAY)
struct XRayHashTableEntry {
void* data;
uint32_t key;
};
struct XRayHashTable {
int capacity;
int count;
struct XRayHashTableEntry* array;
};
XRAY_NO_INSTRUMENT void XRayHashTableGrow(struct XRayHashTable* table);
XRAY_NO_INSTRUMENT uint32_t XRayHashTableHashKey(uint32_t key);
XRAY_NO_INSTRUMENT void XRayHashTableInit(struct XRayHashTable* table,
int32_t capacity);
#define HASH_HISTO 1024
int g_hash_histo[HASH_HISTO];
/* Hashes a 32bit key into a 32bit value. */
uint32_t XRayHashTableHashKey(uint32_t x) {
uint32_t y = x * 7919;
uint32_t z;
size_t c;
uint8_t* s = (uint8_t*)&y;
/* based on djb2 hash function */
uint32_t h = 5381;
for (c = 0; c < sizeof(y); ++c) {
z = s[c];
h = ((h << 5) + h) + z;
}
return h;
}
int XRayHashTableGetCapacity(struct XRayHashTable* table) {
return table->capacity;
}
int XRayHashTableGetCount(struct XRayHashTable* table) {
return table->count;
}
/* Looks up key in hashtable and returns blind data. */
void* XRayHashTableLookup(struct XRayHashTable* table, uint32_t key) {
uint32_t h = XRayHashTableHashKey(key);
uint32_t m = table->capacity - 1;
uint32_t j = h & m;
uint32_t i;
int z = 1;
for (i = 0; i < m; ++i) {
/* An empty entry means the {key, data} isn't in the table. */
if (NULL == table->array[j].data) {
++g_hash_histo[0];
return NULL;
}
/* Search for address */
if (table->array[j].key == key) {
if (z >= HASH_HISTO)
z = HASH_HISTO - 1;
++g_hash_histo[z];
return table->array[j].data;
}
j = (j + 1) & m;
++z;
}
/* Table was full, and there wasn't a match. */
return NULL;
}
/* Inserts key & data into hash table. No duplicates. */
void* XRayHashTableInsert(struct XRayHashTable* table,
void* data, uint32_t key) {
uint32_t h = XRayHashTableHashKey(key);
uint32_t m = table->capacity - 1;
uint32_t j = h & m;
uint32_t i;
for (i = 0; i < m; ++i) {
/* Take the first empty entry. */
/* (the key,data isn't already in the table) */
if (NULL == table->array[j].data) {
void* ret;
float ratio;
table->array[j].data = data;
table->array[j].key = key;
++table->count;
ret = data;
ratio = (float)table->count / (float)table->capacity;
/* Double the capacity of the symtable if we've hit the ratio. */
if (ratio > XRAY_SYMBOL_TABLE_MAX_RATIO)
XRayHashTableGrow(table);
return ret;
}
/* If the key is already present, return the data in the table. */
if (table->array[j].key == key) {
return table->array[j].data;
}
j = (j + 1) & m;
}
/* Table was full */
return NULL;
}
void* XRayHashTableAtIndex(struct XRayHashTable* table, int i) {
if ((i < 0) || (i >= table->capacity))
return NULL;
return table->array[i].data;
}
/* Grows the hash table by doubling its capacity, */
/* then re-inserts all the elements into the new table. */
void XRayHashTableGrow(struct XRayHashTable* table) {
struct XRayHashTableEntry* old_array = table->array;
int old_capacity = table->capacity;
int new_capacity = old_capacity * 2;
int i;
printf("XRay: Growing a hash table...\n");
XRayHashTableInit(table, new_capacity);
for (i = 0; i < old_capacity; ++i) {
void* data = old_array[i].data;
if (NULL != data) {
uint32_t key = old_array[i].key;
XRayHashTableInsert(table, data, key);
}
}
XRayFree(old_array);
}
void XRayHashTableInit(struct XRayHashTable* table, int32_t capacity) {
size_t bytes;
if (0 != (capacity & (capacity - 1))) {
printf("Xray: Hash table capacity should be a power of 2!\n");
/* Round capacity up to next power of 2 */
/* see http://aggregate.org/MAGIC/ */
capacity--;
capacity |= capacity >> 1;
capacity |= capacity >> 2;
capacity |= capacity >> 4;
capacity |= capacity >> 8;
capacity |= capacity >> 16;
capacity++;
}
bytes = sizeof(table->array[0]) * capacity;
table->capacity = capacity;
table->count = 0;
table->array = (struct XRayHashTableEntry*)XRayMalloc(bytes);
}
/* Creates & inializes hash table. */
struct XRayHashTable* XRayHashTableCreate(int capacity) {
struct XRayHashTable* table;
table = (struct XRayHashTable*)XRayMalloc(sizeof(*table));
XRayHashTableInit(table, capacity);
memset(&g_hash_histo[0], 0, sizeof(g_hash_histo[0]) * HASH_HISTO);
return table;
}
/* Prints hash table performance to file; for debugging. */
void XRayHashTableHisto(FILE* f) {
int i;
for (i = 0; i < HASH_HISTO; ++i) {
if (0 != g_hash_histo[i])
fprintf(f, "hash_iterations[%d] = %d\n", i, g_hash_histo[i]);
}
}
/* Frees hash table. */
/* Note: Does not free what the hash table entries point to. */
void XRayHashTableFree(struct XRayHashTable* table) {
XRayFree(table->array);
table->capacity = 0;
table->count = 0;
table->array = NULL;
XRayFree(table);
}
#endif /* XRAY */