blob: b5471f065a481c1b311f52d59b1ef79f98f280ab [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <stdio.h>
#include <string.h>
#include "alloc.h"
#include <qpid/dispatch/hash.h>
#include <qpid/dispatch/ctools.h>
typedef struct qd_hash_item_t {
DEQ_LINKS(struct qd_hash_item_t);
unsigned char *key;
union {
void *val;
const void *val_const;
} v;
} qd_hash_item_t;
ALLOC_DECLARE(qd_hash_item_t);
ALLOC_DEFINE(qd_hash_item_t);
DEQ_DECLARE(qd_hash_item_t, items_t);
typedef struct bucket_t {
items_t items;
} bucket_t;
struct qd_hash_t {
bucket_t *buckets;
unsigned int bucket_count;
unsigned int bucket_mask;
int batch_size;
size_t size;
int is_const;
};
struct qd_hash_handle_t {
bucket_t *bucket;
qd_hash_item_t *item;
};
ALLOC_DECLARE(qd_hash_handle_t);
ALLOC_DEFINE(qd_hash_handle_t);
qd_hash_t *qd_hash(int bucket_exponent, int batch_size, int value_is_const)
{
int i;
qd_hash_t *h = NEW(qd_hash_t);
if (!h)
return 0;
h->bucket_count = 1 << bucket_exponent;
h->bucket_mask = h->bucket_count - 1;
h->batch_size = batch_size;
h->size = 0;
h->is_const = value_is_const;
h->buckets = NEW_ARRAY(bucket_t, h->bucket_count);
for (i = 0; i < h->bucket_count; i++) {
DEQ_INIT(h->buckets[i].items);
}
return h;
}
void qd_hash_free(qd_hash_t *h)
{
if (!h) return;
qd_hash_item_t *item;
int idx;
for (idx = 0; idx < h->bucket_count; idx++) {
item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
free(item->key);
DEQ_REMOVE_HEAD(h->buckets[idx].items);
free_qd_hash_item_t(item);
item = DEQ_HEAD(h->buckets[idx].items);
}
}
free(h->buckets);
free(h);
}
size_t qd_hash_size(qd_hash_t *h)
{
return h ? h->size : 0;
}
static qd_hash_item_t *qd_hash_internal_insert(qd_hash_t *h, qd_field_iterator_t *key, int *exists, qd_hash_handle_t **handle)
{
unsigned long idx = qd_iterator_hash_function(key) & h->bucket_mask;
qd_hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
if (qd_field_iterator_equal(key, item->key))
break;
item = item->next;
}
if (item) {
*exists = 1;
if (handle)
*handle = 0;
return item;
}
item = new_qd_hash_item_t();
if (!item)
return 0;
DEQ_ITEM_INIT(item);
item->key = qd_field_iterator_copy(key);
DEQ_INSERT_TAIL(h->buckets[idx].items, item);
h->size++;
*exists = 0;
//
// If a pointer to a handle-pointer was supplied, create a handle for this item.
//
if (handle) {
*handle = new_qd_hash_handle_t();
(*handle)->bucket = &h->buckets[idx];
(*handle)->item = item;
}
return item;
}
qd_error_t qd_hash_insert(qd_hash_t *h, qd_field_iterator_t *key, void *val, qd_hash_handle_t **handle)
{
int exists = 0;
qd_hash_item_t *item = qd_hash_internal_insert(h, key, &exists, handle);
if (!item)
return QD_ERROR_ALLOC;
if (exists)
return QD_ERROR_ALREADY_EXISTS;
item->v.val = val;
return QD_ERROR_NONE;
}
qd_error_t qd_hash_insert_const(qd_hash_t *h, qd_field_iterator_t *key, const void *val, qd_hash_handle_t **handle)
{
assert(h->is_const);
int error = 0;
qd_hash_item_t *item = qd_hash_internal_insert(h, key, &error, handle);
if (item)
item->v.val_const = val;
return error;
}
static qd_hash_item_t *qd_hash_internal_retrieve_with_hash(qd_hash_t *h, uint32_t hash, qd_field_iterator_t *key)
{
uint32_t idx = hash & h->bucket_mask;
qd_hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
if (qd_field_iterator_equal(key, item->key))
break;
item = item->next;
}
return item;
}
static qd_hash_item_t *qd_hash_internal_retrieve(qd_hash_t *h, qd_field_iterator_t *key)
{
uint32_t hash = qd_iterator_hash_function(key);
return qd_hash_internal_retrieve_with_hash(h, hash, key);
}
void qd_hash_retrieve_prefix(qd_hash_t *h, qd_field_iterator_t *iter, void **val)
{
//Hash individual segments by iterating thru the octets in the iterator.
qd_iterator_hash_segments(iter);
uint32_t hash = 0;
qd_hash_item_t *item;
while (qd_iterator_hash_and_reset(iter, &hash)) {
item = qd_hash_internal_retrieve_with_hash(h, hash, iter);
if (item)
break;
}
if (item)
*val = item->v.val;
else
*val = 0;
}
void qd_hash_retrieve_prefix_const(qd_hash_t *h, qd_field_iterator_t *iter, const void **val)
{
assert(h->is_const);
uint32_t hash = 0;
qd_hash_item_t *item;
while (qd_iterator_hash_and_reset(iter, &hash)) {
item = qd_hash_internal_retrieve_with_hash(h, hash, iter);
if (item)
break;
}
if (item)
*val = item->v.val_const;
else
*val = 0;
}
qd_error_t qd_hash_retrieve(qd_hash_t *h, qd_field_iterator_t *key, void **val)
{
qd_hash_item_t *item = qd_hash_internal_retrieve(h, key);
if (item)
*val = item->v.val;
else
*val = 0;
return QD_ERROR_NONE;
}
qd_error_t qd_hash_retrieve_const(qd_hash_t *h, qd_field_iterator_t *key, const void **val)
{
assert(h->is_const);
qd_hash_item_t *item = qd_hash_internal_retrieve(h, key);
if (item)
*val = item->v.val_const;
else
*val = 0;
return QD_ERROR_NONE;
}
qd_error_t qd_hash_remove(qd_hash_t *h, qd_field_iterator_t *key)
{
uint32_t idx = qd_iterator_hash_function(key) & h->bucket_mask;
qd_hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
if (qd_field_iterator_equal(key, item->key))
break;
item = item->next;
}
if (item) {
free(item->key);
DEQ_REMOVE(h->buckets[idx].items, item);
free_qd_hash_item_t(item);
h->size--;
return QD_ERROR_NONE;
}
return QD_ERROR_NOT_FOUND;
}
void qd_hash_handle_free(qd_hash_handle_t *handle)
{
if (handle)
free_qd_hash_handle_t(handle);
}
const unsigned char *qd_hash_key_by_handle(const qd_hash_handle_t *handle)
{
if (handle)
return handle->item->key;
return 0;
}
qd_error_t qd_hash_remove_by_handle(qd_hash_t *h, qd_hash_handle_t *handle)
{
unsigned char *key = 0;
qd_error_t error = qd_hash_remove_by_handle2(h, handle, &key);
if (key)
free(key);
return error;
}
qd_error_t qd_hash_remove_by_handle2(qd_hash_t *h, qd_hash_handle_t *handle, unsigned char **key)
{
if (!handle)
return QD_ERROR_NOT_FOUND;
*key = handle->item->key;
DEQ_REMOVE(handle->bucket->items, handle->item);
free_qd_hash_item_t(handle->item);
h->size--;
return QD_ERROR_NONE;
}