/****************************************************************************** * * Copyright (C) 2002 Jason Evans . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice(s), this list of conditions and the following disclaimer * unmodified other than the allowable addition of one or more * copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ #include #include #include #define LIBTOP_DBG #ifndef LIBTOP_DBG /* Disable assertions. */ #ifndef NDEBUG #define NDEBUG #endif #endif #include #include "qr.h" #include "ql.h" #include "ch.h" #include "dch.h" #ifdef LIBTOP_DBG #define LIBTOP_DCH_MAGIC 0x4327589e #endif static boolean_t dch_p_grow(libtop_dch_t *a_dch); static boolean_t dch_p_shrink(libtop_dch_t *a_dch); static void dch_p_insert(libtop_ch_t *a_ch, libtop_chi_t * a_chi); libtop_dch_t * dch_new(libtop_dch_t *a_dch, unsigned a_base_table, unsigned a_base_grow, unsigned a_base_shrink, libtop_ch_hash_t *a_hash, libtop_ch_key_comp_t *a_key_comp) { libtop_dch_t *retval; assert(a_base_table > 0); assert(a_base_grow > 0); assert(a_base_grow > a_base_shrink); assert(a_hash != NULL); assert(a_key_comp != NULL); if (a_dch != NULL) { retval = a_dch; memset(retval, 0, sizeof(libtop_dch_t)); retval->is_malloced = FALSE; } else { retval = (libtop_dch_t *)malloc(sizeof(libtop_dch_t)); memset(retval, 0, sizeof(libtop_dch_t)); retval->is_malloced = TRUE; } retval->base_table = a_base_table; retval->base_grow = a_base_grow; retval->base_shrink = a_base_shrink; retval->grow_factor = 1; retval->hash = a_hash; retval->key_comp = a_key_comp; retval->ch = (libtop_ch_t *)malloc(LIBTOP_CH_TABLE2SIZEOF(retval->base_table)); if (retval->ch == NULL) { if (retval->is_malloced) { free(retval); } retval = NULL; goto RETURN; } ch_new(retval->ch, retval->base_table, retval->hash, retval->key_comp); #ifdef LIBTOP_DBG retval->magic = LIBTOP_DCH_MAGIC; #endif RETURN: return retval; } void dch_delete(libtop_dch_t *a_dch) { assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); #ifdef LIBTOP_DCH_VERBOSE fprintf(stderr, "%s(%p): num_collisions: %llu, num_inserts: %llu," " num_removes: %llu, num_searches: %llu, num_grows: %llu," " num_shrinks: %llu\n", __FUNCTION__, a_dch, a_dch->ch->num_collisions, a_dch->ch->num_inserts, a_dch->ch->num_removes, a_dch->ch->num_searches, a_dch->num_grows, a_dch->num_shrinks); #endif ch_delete(a_dch->ch); if (a_dch->is_malloced) { free(a_dch); } #ifdef LIBTOP_DBG else { memset(a_dch, 0x5a, sizeof(libtop_dch_t)); } #endif } unsigned dch_count(libtop_dch_t *a_dch) { assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); return ch_count(a_dch->ch); } boolean_t dch_insert(libtop_dch_t *a_dch, const void *a_key, const void *a_data, libtop_chi_t *a_chi) { boolean_t retval; assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); if (dch_p_grow(a_dch)) { retval = TRUE; goto RETURN; } ch_insert(a_dch->ch, a_key, a_data, a_chi); retval = FALSE; RETURN: return retval; } boolean_t dch_remove(libtop_dch_t *a_dch, const void *a_search_key, void **r_key, void **r_data, libtop_chi_t **r_chi) { boolean_t retval; assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); if (dch_p_shrink(a_dch) || ch_remove(a_dch->ch, a_search_key, r_key, r_data, r_chi)) { retval = TRUE; goto RETURN; } retval = FALSE; RETURN: return retval; } void dch_clear(libtop_dch_t *a_dch) { assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); ch_clear(a_dch->ch); } boolean_t dch_search(libtop_dch_t *a_dch, const void *a_key, void **r_data) { assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); return ch_search(a_dch->ch, a_key, r_data); } boolean_t dch_get_iterate(libtop_dch_t *a_dch, void **r_key, void **r_data) { assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); return ch_get_iterate(a_dch->ch, r_key, r_data); } boolean_t dch_remove_iterate(libtop_dch_t *a_dch, void **r_key, void **r_data, libtop_chi_t **r_chi) { boolean_t retval; assert(a_dch != NULL); assert(a_dch->magic == LIBTOP_DCH_MAGIC); dch_p_shrink(a_dch); if (ch_remove_iterate(a_dch->ch, r_key, r_data, r_chi)) { retval = TRUE; goto RETURN; } retval = FALSE; RETURN: return retval; } /* * Given the ch API, there is no way to both safely and efficiently transfer the * contents of one ch to another. Therefore, this function mucks with ch * internals. */ static boolean_t dch_p_grow(libtop_dch_t *a_dch) { boolean_t retval; libtop_ch_t *t_ch; libtop_chi_t *chi; unsigned count, i; count = ch_count(a_dch->ch); if ((count + 1) > (a_dch->grow_factor * a_dch->base_grow)) { /* Too big. Create a new ch twice as large and populate it. */ t_ch = (libtop_ch_t *)malloc( LIBTOP_CH_TABLE2SIZEOF(a_dch->base_table * a_dch->grow_factor * 2)); if (t_ch == NULL) { retval = NULL; goto RETURN; } ch_new(t_ch, a_dch->base_table * a_dch->grow_factor * 2, a_dch->hash, a_dch->key_comp); for (i = 0; i < count; i++) { chi = ql_first(&a_dch->ch->chi_ql); ql_remove(&a_dch->ch->chi_ql, chi, ch_link); ql_elm_new(chi, slot_link); dch_p_insert(t_ch, chi); } a_dch->grow_factor *= 2; #ifdef LIBTOP_DCH_COUNT a_dch->num_grows++; t_ch->num_collisions += a_dch->ch->num_collisions; t_ch->num_inserts += a_dch->ch->num_inserts; t_ch->num_removes += a_dch->ch->num_removes; t_ch->num_searches += a_dch->ch->num_searches; #endif /* * Set to NULL to keep ch_delete() from deleting all the items. */ ql_first(&a_dch->ch->chi_ql) = NULL; ch_delete(a_dch->ch); a_dch->ch = t_ch; } retval = FALSE; RETURN: return retval; } /* * Given the ch API, there is no way to both safely and efficiently transfer the * contents of one ch to another. Therefore, this function mucks with ch * internals. */ static boolean_t dch_p_shrink(libtop_dch_t *a_dch) { boolean_t retval; libtop_ch_t *t_ch; libtop_chi_t *chi; unsigned count, i; count = ch_count(a_dch->ch); if ((count - 1 < a_dch->base_shrink * a_dch->grow_factor) && (a_dch->grow_factor > 1)) { unsigned new_factor; /* * Too big. Create a new ch with the smallest grow factor that * does not cause the ch to be overflowed. */ for (new_factor = 1; new_factor * a_dch->base_grow <= count - 1; new_factor *= 2) { assert(new_factor < a_dch->grow_factor); } assert(new_factor > 0); assert(new_factor < a_dch->grow_factor); t_ch = (libtop_ch_t *)malloc( LIBTOP_CH_TABLE2SIZEOF(a_dch->base_table * new_factor)); if (t_ch == NULL) { retval = NULL; goto RETURN; } ch_new(t_ch, a_dch->base_table * new_factor, a_dch->hash, a_dch->key_comp); for (i = 0; i < count; i++) { chi = ql_first(&a_dch->ch->chi_ql); ql_remove(&a_dch->ch->chi_ql, chi, ch_link); ql_elm_new(chi, slot_link); dch_p_insert(t_ch, chi); } a_dch->grow_factor = new_factor; #ifdef LIBTOP_DCH_COUNT a_dch->num_shrinks++; t_ch->num_collisions += a_dch->ch->num_collisions; t_ch->num_inserts += a_dch->ch->num_inserts; t_ch->num_removes += a_dch->ch->num_removes; t_ch->num_searches += a_dch->ch->num_searches; #endif /* * Set to NULL to keep ch_delete() from deleting all the items. */ ql_first(&a_dch->ch->chi_ql) = NULL; ch_delete(a_dch->ch); a_dch->ch = t_ch; } retval = FALSE; RETURN: return retval; } /* * Given the ch API, there is no way to both safely and efficiently transfer the * contents of one ch to another. Therefore, this function mucks with ch * internals. */ static void dch_p_insert(libtop_ch_t *a_ch, libtop_chi_t *a_chi) { unsigned slot; /* Initialize a_chi. */ slot = a_ch->hash(a_chi->key) % a_ch->table_size; a_chi->slot = slot; /* Hook into ch-wide list. */ ql_tail_insert(&a_ch->chi_ql, a_chi, ch_link); /* Hook into the slot list. */ #ifdef LIBTOP_DCH_COUNT if (ql_first(&a_ch->table[slot]) != NULL) { a_ch->num_collisions++; } #endif ql_head_insert(&a_ch->table[slot], a_chi, slot_link); a_ch->count++; #ifdef LIBTOP_DCH_COUNT a_ch->num_inserts++; #endif }