1
0
mirror of https://github.com/haiwen/ccnet-server.git synced 2025-04-27 18:25:06 +00:00
ccnet-server/lib/htree.c
2016-08-19 13:54:34 +08:00

296 lines
7.2 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "htree.h"
#define MAX_HEIGHT 7
#define INDEX(hashid,depth) (depth%2 ? (hashid[depth/2] && 0x0f):(hashid[depth/2] >> 4))
#define IS_NODE(n) (n->is_node)
static const int g_index[] = {0, 1, 17, 273, 4369, 69905, 1118481, 17895697, 286331153};
static inline void hashxor(char *hashid, char *id, int len)
{
int i = 0;
for (i = 0; i < len; ++i) {
hashid[i] = hashid[i] ^ id[i];
}
}
static inline int get_right_height (int size)
{
int height = 0;
while (g_index[height+1] < size)
++height;
if (height < 1)return 1;
return height;
}
static inline unsigned int get_pos (HTree *tree, HTNode *node)
{
return (node - tree->nodes) - g_index[(int)node->depth];
}
static inline void ht_set_data (HTree *tree, HTNode *node, HTData *data)
{
tree->datas[node - tree->nodes] = data;
}
static inline HTItem *create_item (unsigned char *hashid, void *data)
{
HTItem *it = malloc (sizeof (HTItem));
it->hashid = hashid;
it->data = data;
it->next = NULL;
}
static inline void delete_item (HTree *tree, HTItem *it)
{
if (tree->item_free)
tree->item_free (it);
free (it);
}
static void *load_node (HTree *tree, HTNode *node)
{
HTData *data = ht_get_data (tree, node);
if (data) return data;
data = malloc (sizeof(HTData));
data->size = 0;
data->hashid = malloc (tree->hashid_len);
memset (data->hashid, 0, tree->hashid_len);
data->item_list = NULL;
ht_set_data (tree, node, data);
return data;
}
static void delete_data (HTree *tree, HTData *data)
{
HTItem *p = NULL;;
if (data) {
if (data->hashid)
free (data->hashid);
while (data->item_list) {
p = data->item_list;
data->item_list = p->next;
delete_item (tree, p);
}
free(data);
}
}
static int add_item (HTree *tree, HTNode *node, unsigned char *hashid, void *data)
{
if (node == NULL)
return 0;
int r = 0, i = 0;
HTData *htdata = NULL;
if (IS_NODE(node)) {
r = add_item (tree,
ht_get_child (tree, node, INDEX(hashid, node->depth)),
hashid,
data);
if (r) {
htdata = load_node(tree, node);
hashxor (htdata->hashid, hashid, tree->hashid_len);
}
return r;
}
htdata = load_node(tree, node);
HTItem *p = htdata->item_list;
for (i = 0; i < htdata->size; i++){
if (memcmp (hashid, p->hashid, tree->hashid_len) == 0) {
return 0;
}
p = p->next;
}
HTItem *it = create_item (hashid, data);
it->next = htdata->item_list;
htdata->item_list = it;
++htdata->size;
hashxor (htdata->hashid, it->hashid, tree->hashid_len);
return 1;
}
static int remove_item (HTree *tree, HTNode *node, unsigned char *hashid)
{
if (node == NULL)
return 0;
int r = 0, i = 0;
HTData *data = NULL;
if (IS_NODE(node)) {
r = remove_item (tree,
ht_get_child (tree, node, INDEX(hashid, node->depth)),
hashid);
if (r) {
data = load_node(tree, node);
hashxor (data->hashid, hashid, tree->hashid_len);
}
return r;
}
data = load_node(tree, node);
HTItem *p = data->item_list;
HTItem *pre = NULL;
for (i = 0; i < data->size; i++){
if (memcmp (hashid, p->hashid, tree->hashid_len) == 0) {
if (pre == NULL) {
data->item_list = p->next;
} else {
pre->next = p->next;
}
--data->size;
hashxor (data->hashid, hashid, tree->hashid_len);
delete_item (tree, p);
return 1;
}
pre = p;
p = p->next;
}
return 0;
}
HTree* ht_new (int size, int hashlen)
{
HTree *ht = (HTree *)malloc (sizeof(HTree));
int height = get_right_height (size);
int i = 0, j = 0;
ht->height = height;
ht->size = g_index[height];
ht->hashid_len = hashlen;
ht->item_free = NULL;
ht->nodes = malloc (ht->size *sizeof(HTNode));
ht->datas = malloc (ht->size *sizeof(HTData *));
memset (ht->nodes, 0, ht->size *sizeof(HTNode));
memset (ht->datas, 0, ht->size *sizeof(HTData *));
for (i = 0; i < height; ++i) {
for (j = g_index[i]; j < g_index[i+1]; ++j) {
(ht->nodes[j]).is_node = 1;
(ht->nodes[j]).depth = i;
}
}
for (j = g_index[height-1]; j < g_index[height]; ++j) {
(ht->nodes[j]).is_node = 0;
}
}
int ht_add (HTree *tree, unsigned char *hashid, void *data)
{
return add_item (tree, tree->nodes, hashid, data);
}
int ht_remove (HTree *tree, unsigned char *hashid)
{
return remove_item (tree, tree->nodes, hashid);
}
void ht_resize (HTree *ht, int size)
{
int height = get_right_height (size);
if (height <= ht->height) {
return;
}
/* TODO resize the hash tree */
}
void ht_clear (HTree *tree)
{
assert (tree);
int i;
for(i = 0; i < tree->size; i++){
delete_data (tree, tree->datas[i]);
}
memset (tree->datas, 0, sizeof(HTData*) * tree->size);
free (tree->nodes);
free (tree->datas);
free (tree);
}
HTData* ht_get_data (HTree *tree, HTNode *node)
{
return tree->datas[node - tree->nodes];
}
unsigned char* ht_get_node_hash (HTree *tree, HTNode *node)
{
if (tree->datas[node - tree->nodes] == NULL)
return NULL;
return (tree->datas[node - tree->nodes])->hashid;
}
HTNode *ht_get_child (HTree *tree, HTNode *node, int b)
{
assert(0 <= b && b <= 0x0f);
assert(node->depth < tree->height - 1);
int i = g_index[node->depth + 1] + (get_pos(tree, node) << 4) + b;
if (i >= tree->size){
fprintf(stderr, "get_child out of bound: %dth %d >= %d\n", b, i, tree->size);
return NULL;
}
return tree->nodes + i;
}
HTNode *ht_get_parent (HTree *tree, HTNode *node)
{
if (node->depth == 0)
return NULL;
int i = g_index[node->depth - 1] + (get_pos(tree, node) >> 4) ;
return tree->nodes + i;
}
HTNode *ht_get_brother (HTree *tree, HTNode *node)
{
if (get_pos(tree, node) & 0xff < 15)
return node + 1;
return NULL;
}
static void remove_node (HTree *tree, HTNode *node)
{
HTData* data = ht_get_data (tree, node);
if (data == NULL)
return;
if (HTNODE_IS_LEAF(node)) {
delete_data (tree, data);
} else {
int i = 0;
HTNode *child = NULL;
for (i = 0; i < 16; ++i) {
child = ht_get_child (tree, node, i);
remove_node (tree, child);
}
}
}
void ht_remove_node (HTree *tree, HTNode *node)
{
HTData *data = ht_get_data (tree, node);
if (data == NULL)
return;
char *hash = data->hashid;
HTNode *parent = ht_get_parent (tree, node);
while (parent) {
char *phash = ht_get_node_hash (tree, parent);
hashxor (phash, hash, tree->hashid_len);
parent = ht_get_parent (tree, parent);
}
remove_node (tree, node);
ht_set_data (tree, node, NULL);
}