/* * Generic hash table handler... * * Copyright 2000 by Gray Watson. * * This file is part of the table package. * * Permission to use, copy, modify, and distribute this software for * any purpose and without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies, and that the name of Gray Watson not be used in advertising * or publicity pertaining to distribution of the document or software * without specific, written prior permission. * * Gray Watson makes no representations about the suitability of the * software described herein for any purpose. It is provided "as is" * without express or implied warranty. * * The author may be reached via http://256.com/gray/ * * $Id: table.c,v 1.19 2000/03/09 03:30:41 gray Exp $ */ /* * Handles basic hash-table manipulations. This is an implementation * of open hashing with an array of buckets holding linked lists of * elements. Each element has a key and a data. The user indexes on * the key to find the data. See the typedefs in table_loc.h for more * information. */ #include #include #include #include #ifdef unix #include #else #include #include #define NO_MMAP #define open _open #endif #ifndef NO_MMAP #include #include #ifndef MAP_FAILED #define MAP_FAILED (caddr_t)0L #endif #endif #define TABLE_MAIN #include "table.h" #include "table_loc.h" #ifdef DMALLOC #include "dmalloc.h" #endif static char *rcs_id = "$Id: table.c,v 1.19 2000/03/09 03:30:41 gray Exp $"; /* * Version id for the library. You also need to add an entry to the * NEWS and ChangeLog files. */ static char *version_id = "$TableVersion: 4.3.0 March 8, 2000 $"; /****************************** local functions ******************************/ /* * static table_entry_t *first_entry * * DESCRIPTION: * * Return the first entry in the table. It will set the linear * structure counter to the position of the first entry. * * RETURNS: * * Success: A pointer to the first entry in the table. * * Failure: NULL if there is no first entry. * * ARGUMENTS: * * table_p -> Table whose next entry we are finding. * * linear_p <-> Pointer to a linear structure which we will advance * and then find the corresponding entry. */ static table_entry_t *first_entry(const table_t *table_p, table_linear_t *linear_p) { table_entry_t *entry_p; unsigned int bucket_c = 0; /* look for the first non-empty bucket */ for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) { entry_p = table_p->ta_buckets[bucket_c]; if (entry_p != NULL) { if (linear_p != NULL) { linear_p->tl_bucket_c = bucket_c; linear_p->tl_entry_c = 0; } return TABLE_POINTER(table_p, table_entry_t *, entry_p); } } return NULL; } /* * static table_entry_t *next_entry * * DESCRIPTION: * * Return the next entry in the table which is past the position in * our linear pointer. It will advance the linear structure counters. * * RETURNS: * * Success: A pointer to the next entry in the table. * * Failure: NULL. * * ARGUMENTS: * * table_p -> Table whose next entry we are finding. * * linear_p <-> Pointer to a linear structure which we will advance * and then find the corresponding entry. * * error_p <- Pointer to an integer which when the routine returns * will contain a table error code. */ static table_entry_t *next_entry(const table_t *table_p, table_linear_t *linear_p, int *error_p) { table_entry_t *entry_p; int entry_c; /* can't next if we haven't first-ed */ if (linear_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_LINEAR); return NULL; } if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { /* * NOTE: this might happen if we delete an item which shortens the * table bucket numbers. */ SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); return NULL; } linear_p->tl_entry_c++; /* find the entry which is the nth in the list */ entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; /* NOTE: we swap the order here to be more efficient */ for (entry_c = linear_p->tl_entry_c; entry_c > 0; entry_c--) { /* did we reach the end of the list? */ if (entry_p == NULL) { break; } entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; } /* did we find an entry in the current bucket? */ if (entry_p != NULL) { SET_POINTER(error_p, TABLE_ERROR_NONE); return TABLE_POINTER(table_p, table_entry_t *, entry_p); } /* find the first entry in the next non-empty bucket */ linear_p->tl_entry_c = 0; for (linear_p->tl_bucket_c++; linear_p->tl_bucket_c < table_p->ta_bucket_n; linear_p->tl_bucket_c++) { entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; if (entry_p != NULL) { SET_POINTER(error_p, TABLE_ERROR_NONE); return TABLE_POINTER(table_p, table_entry_t *, entry_p); } } SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); return NULL; } /* * static table_entry_t *this_entry * * DESCRIPTION: * * Return the entry pointer in the table which is currently being * indicated by our linear pointer. * * RETURNS: * * Success: A pointer to the next entry in the table. * * Failure: NULL. * * ARGUMENTS: * * table_p -> Table whose next entry we are finding. * * linear_p -> Pointer to a linear structure which we will find the * corresponding entry. * * error_p <- Pointer to an integer which when the routine returns * will contain a table error code. */ static table_entry_t *this_entry(const table_t *table_p, const table_linear_t *linear_p, int *error_p) { table_entry_t *entry_p; int entry_c; /* can't next if we haven't first-ed */ if (linear_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_LINEAR); return NULL; } if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { /* * NOTE: this might happen if we delete an item which shortens the * table bucket numbers. */ SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); return NULL; } /* find the entry which is the nth in the list */ entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; /* NOTE: we swap the order here to be more efficient */ for (entry_c = linear_p->tl_entry_c; entry_c > 0; entry_c--) { /* did we reach the end of the list? */ if (entry_p == NULL) { break; } entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; } /* did we find an entry in the current bucket? */ if (entry_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); return NULL; } else { SET_POINTER(error_p, TABLE_ERROR_NONE); return TABLE_POINTER(table_p, table_entry_t *, entry_p); } } /* * static unsigned int hash * * DESCRIPTION: * * Hash a variable-length key into a 32-bit value. Every bit of the * key affects every bit of the return value. Every 1-bit and 2-bit * delta achieves avalanche. About (6 * len + 35) instructions. The * best hash table sizes are powers of 2. There is no need to use mod * (sooo slow!). If you need less than 32 bits, use a bitmask. For * example, if you need only 10 bits, do h = (h & hashmask(10)); In * which case, the hash table should have hashsize(10) elements. * * By Bob Jenkins, 1996. bob_jenkins@compuserve.com. You may use * this code any way you wish, private, educational, or commercial. * It's free. See * http://ourworld.compuserve.com/homepages/bob_jenkins/evahash.htm * Use for hash table lookup, or anything where one collision in 2^^32 * is acceptable. Do NOT use for cryptographic purposes. * * RETURNS: * * Returns a 32-bit hash value. * * ARGUMENTS: * * key - Key (the unaligned variable-length array of bytes) that we * are hashing. * * length - Length of the key in bytes. * * init_val - Initialization value of the hash if you need to hash a * number of strings together. For instance, if you are hashing N * strings (unsigned char **)keys, do it like this: * * for (i=0, h=0; i= 12; len -= 12) { a += (key_p[0] + ((unsigned int)key_p[1] << 8) + ((unsigned int)key_p[2] << 16) + ((unsigned int)key_p[3] << 24)); b += (key_p[4] + ((unsigned int)key_p[5] << 8) + ((unsigned int)key_p[6] << 16) + ((unsigned int)key_p[7] << 24)); c += (key_p[8] + ((unsigned int)key_p[9] << 8) + ((unsigned int)key_p[10] << 16) + ((unsigned int)key_p[11] << 24)); HASH_MIX(a,b,c); key_p += 12; } c += length; /* all the case statements fall through to the next */ switch(len) { case 11: c += ((unsigned int)key_p[10] << 24); case 10: c += ((unsigned int)key_p[9] << 16); case 9: c += ((unsigned int)key_p[8] << 8); /* the first byte of c is reserved for the length */ case 8: b += ((unsigned int)key_p[7] << 24); case 7: b += ((unsigned int)key_p[6] << 16); case 6: b += ((unsigned int)key_p[5] << 8); case 5: b += key_p[4]; case 4: a += ((unsigned int)key_p[3] << 24); case 3: a += ((unsigned int)key_p[2] << 16); case 2: a += ((unsigned int)key_p[1] << 8); case 1: a += key_p[0]; /* case 0: nothing left to add */ } HASH_MIX(a, b, c); return c; } /* * static int entry_size * * DESCRIPTION: * * Calculates the appropriate size of an entry to include the key and * data sizes as well as any associated alignment to the data. * * RETURNS: * * The associated size of the entry. * * ARGUMENTS: * * table_p - Table associated with the entries whose size we are * determining. * * key_size - Size of the entry key. * * data - Size of the entry data. */ static int entry_size(const table_t *table_p, const unsigned int key_size, const unsigned int data_size) { int size, left; /* initial size -- key is already aligned if right after struct */ size = sizeof(struct table_shell_st) + key_size; /* if there is no alignment then it is easy */ if (table_p->ta_data_align == 0) { return size + data_size; } /* add in our alignement */ left = size & (table_p->ta_data_align - 1); if (left > 0) { size += table_p->ta_data_align - left; } /* we add the data size here after the alignment */ size += data_size; return size; } /* * static unsigned char *entry_data_buf * * DESCRIPTION: * * Companion to the ENTRY_DATA_BUF macro but this handles any * associated alignment to the data in the entry. * * NOTE: we assume here that the data-alignment is > 0. * * RETURNS: * * Pointer to the data segment of the entry. * * ARGUMENTS: * * table_p - Table associated with the entry. * * entry_p - Entry whose data pointer we are determining. */ static unsigned char *entry_data_buf(const table_t *table_p, const table_entry_t *entry_p) { const unsigned char *buf_p; unsigned int size, pad; buf_p = entry_p->te_key_buf + entry_p->te_key_size; /* we need the size of the space before the data */ size = sizeof(struct table_shell_st) + entry_p->te_key_size; /* add in our alignment */ pad = size & (table_p->ta_data_align - 1); if (pad > 0) { pad = table_p->ta_data_align - pad; } return (unsigned char *)buf_p + pad; } /******************************* sort routines *******************************/ /* * static int local_compare * * DESCRIPTION: * * Compare two entries by calling user's compare program or by using * memcmp. * * RETURNS: * * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. * * ARGUMENTS: * * p1 - First entry pointer to compare. * * p2 - Second entry pointer to compare. * * compare - User comparison function. Ignored. * * table_p - Associated table being ordered. Ignored. * * err_bp - Pointer to an integer which will be set with 1 if an error * has occurred. It cannot be NULL. */ static int local_compare(const void *p1, const void *p2, table_compare_t compare, const table_t *table_p, int *err_bp) { const table_entry_t * const *ent1_p = p1, * const *ent2_p = p2; int cmp; unsigned int size; /* compare as many bytes as we can */ size = (*ent1_p)->te_key_size; if ((*ent2_p)->te_key_size < size) { size = (*ent2_p)->te_key_size; } cmp = memcmp(ENTRY_KEY_BUF(*ent1_p), ENTRY_KEY_BUF(*ent2_p), size); /* if common-size equal, then if next more bytes, it is larger */ if (cmp == 0) { cmp = (*ent1_p)->te_key_size - (*ent2_p)->te_key_size; } *err_bp = 0; return cmp; } /* * static int local_compare_pos * * DESCRIPTION: * * Compare two entries by calling user's compare program or by using * memcmp. * * RETURNS: * * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. * * ARGUMENTS: * * p1 - First entry pointer to compare. * * p2 - Second entry pointer to compare. * * compare - User comparison function. Ignored. * * table_p - Associated table being ordered. * * err_bp - Pointer to an integer which will be set with 1 if an error * has occurred. It cannot be NULL. */ static int local_compare_pos(const void *p1, const void *p2, table_compare_t compare, const table_t *table_p, int *err_bp) { const table_linear_t *lin1_p = p1, *lin2_p = p2; const table_entry_t *ent1_p, *ent2_p; int cmp, ret; unsigned int size; /* get entry pointers */ ent1_p = this_entry(table_p, lin1_p, &ret); ent2_p = this_entry(table_p, lin2_p, &ret); if (ent1_p == NULL || ent2_p == NULL) { *err_bp = 1; return 0; } /* compare as many bytes as we can */ size = ent1_p->te_key_size; if (ent2_p->te_key_size < size) { size = ent2_p->te_key_size; } cmp = memcmp(ENTRY_KEY_BUF(ent1_p), ENTRY_KEY_BUF(ent2_p), size); /* if common-size equal, then if next more bytes, it is larger */ if (cmp == 0) { cmp = ent1_p->te_key_size - ent2_p->te_key_size; } *err_bp = 0; return cmp; } /* * static int external_compare * * DESCRIPTION: * * Compare two entries by calling user's compare program or by using * memcmp. * * RETURNS: * * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. * * ARGUMENTS: * * p1 - First entry pointer to compare. * * p2 - Second entry pointer to compare. * * user_compare - User comparison function. * * table_p - Associated table being ordered. * * err_bp - Pointer to an integer which will be set with 1 if an error * has occurred. It cannot be NULL. */ static int external_compare(const void *p1, const void *p2, table_compare_t user_compare, const table_t *table_p, int *err_bp) { const table_entry_t * const *ent1_p = p1, * const *ent2_p = p2; /* since we know we are not aligned we can use the EXTRY_DATA_BUF macro */ *err_bp = 0; return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size, ENTRY_DATA_BUF(table_p, *ent1_p), (*ent1_p)->te_data_size, ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size, ENTRY_DATA_BUF(table_p, *ent2_p), (*ent2_p)->te_data_size); } /* * static int external_compare_pos * * DESCRIPTION: * * Compare two entries by calling user's compare program or by using * memcmp. * * RETURNS: * * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. * * ARGUMENTS: * * p1 - First entry pointer to compare. * * p2 - Second entry pointer to compare. * * user_compare - User comparison function. * * table_p - Associated table being ordered. * * err_bp - Pointer to an integer which will be set with 1 if an error * has occurred. It cannot be NULL. */ static int external_compare_pos(const void *p1, const void *p2, table_compare_t user_compare, const table_t *table_p, int *err_bp) { const table_linear_t *lin1_p = p1, *lin2_p = p2; const table_entry_t *ent1_p, *ent2_p; int ret; /* get entry pointers */ ent1_p = this_entry(table_p, lin1_p, &ret); ent2_p = this_entry(table_p, lin2_p, &ret); if (ent1_p == NULL || ent2_p == NULL) { *err_bp = 1; return 0; } /* since we know we are not aligned we can use the EXTRY_DATA_BUF macro */ *err_bp = 0; return user_compare(ENTRY_KEY_BUF(ent1_p), (ent1_p)->te_key_size, ENTRY_DATA_BUF(table_p, ent1_p), ent1_p->te_data_size, ENTRY_KEY_BUF(ent2_p), ent2_p->te_key_size, ENTRY_DATA_BUF(table_p, ent2_p), ent2_p->te_data_size); } /* * static int external_compare_align * * DESCRIPTION: * * Compare two entries by calling user's compare program or by using * memcmp. Alignment information is necessary. * * RETURNS: * * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. * * ARGUMENTS: * * p1 - First entry pointer to compare. * * p2 - Second entry pointer to compare. * * user_compare - User comparison function. * * table_p - Associated table being ordered. * * err_bp - Pointer to an integer which will be set with 1 if an error * has occurred. It cannot be NULL. */ static int external_compare_align(const void *p1, const void *p2, table_compare_t user_compare, const table_t *table_p, int *err_bp) { const table_entry_t * const *ent1_p = p1, * const *ent2_p = p2; /* since we are aligned we have to use the entry_data_buf function */ *err_bp = 0; return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size, entry_data_buf(table_p, *ent1_p), (*ent1_p)->te_data_size, ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size, entry_data_buf(table_p, *ent2_p), (*ent2_p)->te_data_size); } /* * static int external_compare_align_pos * * DESCRIPTION: * * Compare two entries by calling user's compare program or by using * memcmp. Alignment information is necessary. * * RETURNS: * * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. * * ARGUMENTS: * * p1 - First entry pointer to compare. * * p2 - Second entry pointer to compare. * * user_compare - User comparison function. * * table_p - Associated table being ordered. * * err_bp - Pointer to an integer which will be set with 1 if an error * has occurred. It cannot be NULL. */ static int external_compare_align_pos(const void *p1, const void *p2, table_compare_t user_compare, const table_t *table_p, int *err_bp) { const table_linear_t *lin1_p = p1, *lin2_p = p2; const table_entry_t *ent1_p, *ent2_p; int ret; /* get entry pointers */ ent1_p = this_entry(table_p, lin1_p, &ret); ent2_p = this_entry(table_p, lin2_p, &ret); if (ent1_p == NULL || ent2_p == NULL) { *err_bp = 1; return 0; } /* since we are aligned we have to use the entry_data_buf function */ *err_bp = 0; return user_compare(ENTRY_KEY_BUF(ent1_p), ent1_p->te_key_size, entry_data_buf(table_p, ent1_p), ent1_p->te_data_size, ENTRY_KEY_BUF(ent2_p), ent2_p->te_key_size, entry_data_buf(table_p, ent2_p), ent2_p->te_data_size); } /* * static void swap_bytes * * DESCRIPTION: * * Swap the values between two items of a specified size. * * RETURNS: * * None. * * ARGUMENTS: * * item1_p -> Pointer to the first item. * * item2_p -> Pointer to the first item. * * ele_size -> Size of the two items. */ static void swap_bytes(unsigned char *item1_p, unsigned char *item2_p, int ele_size) { unsigned char char_temp; for (; ele_size > 0; ele_size--) { char_temp = *item1_p; *item1_p = *item2_p; *item2_p = char_temp; item1_p++; item2_p++; } } /* * static void insert_sort * * DESCRIPTION: * * Do an insertion sort which is faster for small numbers of items and * better if the items are already sorted. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * first_p <-> Start of the list that we are splitting. * * last_p <-> Last entry in the list that we are splitting. * * holder_p <-> Location of hold area we can store an entry. * * ele_size -> Size of the each element in the list. * * compare -> Our comparison function. * * user_compare -> User comparison function. Could be NULL if we are * just using a local comparison function. * * table_p -> Associated table being sorted. */ static int insert_sort(unsigned char *first_p, unsigned char *last_p, unsigned char *holder_p, const unsigned int ele_size, compare_t compare, table_compare_t user_compare, table_t *table_p) { unsigned char *inner_p, *outer_p; int ret, err_b; for (outer_p = first_p + ele_size; outer_p <= last_p; ) { /* look for the place to insert the entry */ for (inner_p = outer_p - ele_size; inner_p >= first_p; inner_p -= ele_size) { ret = compare(outer_p, inner_p, user_compare, table_p, &err_b); if (err_b) { return TABLE_ERROR_COMPARE; } if (ret >= 0) { break; } } inner_p += ele_size; /* do we need to insert the entry in? */ if (outer_p != inner_p) { /* * Now we shift the entry down into its place in the already * sorted list. */ memcpy(holder_p, outer_p, ele_size); memmove(inner_p + ele_size, inner_p, outer_p - inner_p); memcpy(inner_p, holder_p, ele_size); } outer_p += ele_size; } return TABLE_ERROR_NONE; } /* * static int split * * DESCRIPTION: * * This sorts an array of longs via the quick sort algorithm (it's * pretty quick) * * RETURNS: * * None. * * ARGUMENTS: * * first_p -> Start of the list that we are splitting. * * last_p -> Last entry in the list that we are splitting. * * ele_size -> Size of the each element in the list. * * compare -> Our comparison function. * * user_compare -> User comparison function. Could be NULL if we are * just using a local comparison function. * * table_p -> Associated table being sorted. */ static int split(unsigned char *first_p, unsigned char *last_p, const unsigned int ele_size, compare_t compare, table_compare_t user_compare, table_t *table_p) { unsigned char *left_p, *right_p, *pivot_p, *left_last_p, *right_first_p; unsigned char *firsts[MAX_QSORT_SPLITS], *lasts[MAX_QSORT_SPLITS], *pivot; unsigned int width, split_c = 0; int size1, size2, min_qsort_size; int ret, err_b; /* * Allocate some space for our pivot value. We also use this as * holder space for our insert sort. */ pivot = alloca(ele_size); if (pivot == NULL) { /* what else can we do? */ abort(); } min_qsort_size = MAX_QSORT_MANY * ele_size; while (1) { /* find the left, right, and mid point */ left_p = first_p; right_p = last_p; /* is there a faster way to find this? */ width = (last_p - first_p) / ele_size; pivot_p = first_p + ele_size * (width >> 1); /* * Find which of the left, middle, and right elements is the * median (Knuth vol3 p123). */ ret = compare(first_p, pivot_p, user_compare, table_p, &err_b); if (err_b) { return TABLE_ERROR_COMPARE; } if (ret > 0) { swap_bytes(first_p, pivot_p, ele_size); } ret = compare(pivot_p, last_p, user_compare, table_p, &err_b); if (err_b) { return TABLE_ERROR_COMPARE; } if (ret > 0) { swap_bytes(pivot_p, last_p, ele_size); ret = compare(first_p, pivot_p, user_compare, table_p, &err_b); if (err_b) { return TABLE_ERROR_COMPARE; } if (ret > 0) { swap_bytes(first_p, pivot_p, ele_size); } } /* * save our pivot so we don't have to worry about hitting and * swapping it elsewhere while we iterate across the list below. */ memcpy(pivot, pivot_p, ele_size); do { /* shift the left side up until we reach the pivot value */ while (1) { ret = compare(left_p, pivot, user_compare, table_p, &err_b); if (err_b) { return TABLE_ERROR_COMPARE; } if (ret >= 0) { break; } left_p += ele_size; } /* shift the right side down until we reach the pivot value */ while (1) { ret = compare(pivot, right_p, user_compare, table_p, &err_b); if (err_b) { return TABLE_ERROR_COMPARE; } if (ret >= 0) { break; } right_p -= ele_size; } /* if we met in the middle then we are done */ if (left_p == right_p) { left_p += ele_size; right_p -= ele_size; break; } else if (left_p < right_p) { /* * swap the left and right since they both were on the wrong * size of the pivot and continue */ swap_bytes(left_p, right_p, ele_size); left_p += ele_size; right_p -= ele_size; } } while (left_p <= right_p); /* Rename variables to make more sense. This will get optimized out. */ right_first_p = left_p; left_last_p = right_p; /* determine the size of the left and right hand parts */ size1 = left_last_p - first_p; size2 = last_p - right_first_p; /* is the 1st half small enough to just insert-sort? */ if (size1 < min_qsort_size) { /* use the pivot as our temporary space */ ret = insert_sort(first_p, left_last_p, pivot, ele_size, compare, user_compare, table_p); if (ret != TABLE_ERROR_NONE) { return ret; } /* is the 2nd part small as well? */ if (size2 < min_qsort_size) { /* use the pivot as our temporary space */ ret = insert_sort(right_first_p, last_p, pivot, ele_size, compare, user_compare, table_p); if (ret != TABLE_ERROR_NONE) { return ret; } /* pop a partition off our stack */ if (split_c == 0) { /* we are done */ return TABLE_ERROR_NONE; } split_c--; first_p = firsts[split_c]; last_p = lasts[split_c]; } else { /* we can just handle the right side immediately */ first_p = right_first_p; /* last_p = last_p */ } } else if (size2 < min_qsort_size) { /* use the pivot as our temporary space */ ret = insert_sort(right_first_p, last_p, pivot, ele_size, compare, user_compare, table_p); if (ret != TABLE_ERROR_NONE) { return ret; } /* we can just handle the left side immediately */ /* first_p = first_p */ last_p = left_last_p; } else { /* * neither partition is small, we'll have to push the larger one * of them on the stack */ if (split_c >= MAX_QSORT_SPLITS) { /* sanity check here -- we should never get here */ abort(); } if (size1 > size2) { /* push the left partition on the stack */ firsts[split_c] = first_p; lasts[split_c] = left_last_p; split_c++; /* continue handling the right side */ first_p = right_first_p; /* last_p = last_p */ } else { /* push the right partition on the stack */ firsts[split_c] = right_first_p; lasts[split_c] = last_p; split_c++; /* continue handling the left side */ /* first_p = first_p */ last_p = left_last_p; } } } return TABLE_ERROR_NONE; } /*************************** exported routines *******************************/ /* * table_t *table_alloc * * DESCRIPTION: * * Allocate a new table structure. * * RETURNS: * * A pointer to the new table structure which must be passed to * table_free to be deallocated. On error a NULL is returned. * * ARGUMENTS: * * bucket_n - Number of buckets for the hash table. Our current hash * value works best with base two numbers. Set to 0 to take the * library default of 1024. * * error_p - Pointer to an integer which, if not NULL, will contain a * table error code. */ table_t *table_alloc(const unsigned int bucket_n, int *error_p) { table_t *table_p = NULL; unsigned int buck_n; /* allocate a table structure */ table_p = malloc(sizeof(table_t)); if (table_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); return NULL; } if (bucket_n > 0) { buck_n = bucket_n; } else { buck_n = DEFAULT_SIZE; } /* allocate the buckets which are NULLed */ table_p->ta_buckets = (table_entry_t **)calloc(buck_n, sizeof(table_entry_t *)); if (table_p->ta_buckets == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); free(table_p); return NULL; } /* initialize structure */ table_p->ta_magic = TABLE_MAGIC; table_p->ta_flags = 0; table_p->ta_bucket_n = buck_n; table_p->ta_entry_n = 0; table_p->ta_data_align = 0; table_p->ta_linear.tl_magic = 0; table_p->ta_linear.tl_bucket_c = 0; table_p->ta_linear.tl_entry_c = 0; table_p->ta_mmap = NULL; table_p->ta_file_size = 0; table_p->ta_mem_pool = NULL; table_p->ta_alloc_func = NULL; table_p->ta_resize_func = NULL; table_p->ta_free_func = NULL; SET_POINTER(error_p, TABLE_ERROR_NONE); return table_p; } /* * table_t *table_alloc_in_pool * * DESCRIPTION: * * Allocate a new table structure in a memory pool or using * alternative allocation and free functions. * * RETURNS: * * A pointer to the new table structure which must be passed to * table_free to be deallocated. On error a NULL is returned. * * ARGUMENTS: * * bucket_n - Number of buckets for the hash table. Our current hash * value works best with base two numbers. Set to 0 to take the * library default of 1024. * * mem_pool <-> Memory pool to associate with the table. Can be NULL. * * alloc_func -> Allocate function we are overriding malloc() with. * * resize_func -> Resize function we are overriding the standard * memory resize/realloc with. This can be NULL in which cause the * library will allocate, copy, and free itself. * * free_func -> Free function we are overriding free() with. * * error_p - Pointer to an integer which, if not NULL, will contain a * table error code. */ table_t *table_alloc_in_pool(const unsigned int bucket_n, void *mem_pool, table_mem_alloc_t alloc_func, table_mem_resize_t resize_func, table_mem_free_t free_func, int *error_p) { table_t *table_p = NULL; unsigned int buck_n, size; /* make sure we have real functions, mem_pool and resize_func can be NULL */ if (alloc_func == NULL || free_func == NULL) { SET_POINTER(error_p, TABLE_ERROR_ARG_NULL); return NULL; } /* allocate a table structure */ table_p = alloc_func(mem_pool, sizeof(table_t)); if (table_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); return NULL; } if (bucket_n > 0) { buck_n = bucket_n; } else { buck_n = DEFAULT_SIZE; } /* allocate the buckets which are NULLed */ size = buck_n * sizeof(table_entry_t *); table_p->ta_buckets = (table_entry_t **)alloc_func(mem_pool, size); if (table_p->ta_buckets == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); (void)free_func(mem_pool, table_p, sizeof(table_t)); return NULL; } /* * We zero it ourselves to save the necessity of having a * table_mem_calloc_t memory override function. */ memset(table_p->ta_buckets, 0, size); /* initialize structure */ table_p->ta_magic = TABLE_MAGIC; table_p->ta_flags = 0; table_p->ta_bucket_n = buck_n; table_p->ta_entry_n = 0; table_p->ta_data_align = 0; table_p->ta_linear.tl_magic = 0; table_p->ta_linear.tl_bucket_c = 0; table_p->ta_linear.tl_entry_c = 0; table_p->ta_mmap = NULL; table_p->ta_file_size = 0; table_p->ta_mem_pool = mem_pool; table_p->ta_alloc_func = alloc_func; table_p->ta_resize_func = resize_func; table_p->ta_free_func = free_func; SET_POINTER(error_p, TABLE_ERROR_NONE); return table_p; } /* * int table_attr * * DESCRIPTION: * * Set the attributes for the table. The available attributes are * specified at the top of table.h. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Pointer to a table structure which we will be altering. * * attr - Attribute(s) that we will be applying to the table. */ int table_attr(table_t *table_p, const int attr) { if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } table_p->ta_flags = attr; return TABLE_ERROR_NONE; } /* * int table_set_data_alignment * * DESCRIPTION: * * Set the alignment for the data in the table. This is used when you * want to store binary data types and refer to them directly out of * the table storage. For instance if you are storing integers as * data in the table and want to be able to retrieve the location of * the interger and then increment it as (*loc_p)++. Otherwise you * would have to memcpy it out to an integer, increment it, and memcpy * it back. If you are storing character data, no alignment is * necessary. * * For most data elements, sizeof(long) is recommended unless you use * smaller data types exclusively. * * WARNING: If necessary, you must set the data alignment before any * data gets put into the table. Otherwise a TABLE_ERROR_NOT_EMPTY * error will be returned. * * NOTE: there is no way to set the key data alignment although it * should automatically be long aligned. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Pointer to a table structure which we will be altering. * * alignment - Alignment requested for the data. Must be a power of * 2. Set to 0 for none. */ int table_set_data_alignment(table_t *table_p, const int alignment) { int val; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (table_p->ta_entry_n > 0) { return TABLE_ERROR_NOT_EMPTY; } /* defaults */ if (alignment < 2) { table_p->ta_data_align = 0; } else { /* verify we have a base 2 number */ for (val = 2; val < MAX_ALIGNMENT; val *= 2) { if (val == alignment) { break; } } if (val >= MAX_ALIGNMENT) { return TABLE_ERROR_ALIGNMENT; } table_p->ta_data_align = alignment; } return TABLE_ERROR_NONE; } /* * int table_clear * * DESCRIPTION: * * Clear out and free all elements in a table structure. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer that we will be clearing. */ int table_clear(table_t *table_p) { int final = TABLE_ERROR_NONE; table_entry_t *entry_p, *next_p; table_entry_t **bucket_p, **bounds_p; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } #ifndef NO_MMAP /* no mmap support so immediate error */ if (table_p->ta_mmap != NULL) { return TABLE_ERROR_MMAP_OP; } #endif /* free the table allocation and table structure */ bounds_p = table_p->ta_buckets + table_p->ta_bucket_n; for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) { for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) { /* record the next pointer before we free */ next_p = entry_p->te_next_p; if (table_p->ta_free_func == NULL) { free(entry_p); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size))) { final = TABLE_ERROR_FREE; } } /* clear the bucket entry after we free its entries */ *bucket_p = NULL; } /* reset table state info */ table_p->ta_entry_n = 0; table_p->ta_linear.tl_magic = 0; table_p->ta_linear.tl_bucket_c = 0; table_p->ta_linear.tl_entry_c = 0; return final; } /* * int table_free * * DESCRIPTION: * * Deallocates a table structure. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer that we will be freeing. */ int table_free(table_t *table_p) { int ret; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } #ifndef NO_MMAP /* no mmap support so immediate error */ if (table_p->ta_mmap != NULL) { return TABLE_ERROR_MMAP_OP; } #endif ret = table_clear(table_p); if (table_p->ta_buckets != NULL) { if (table_p->ta_free_func == NULL) { free(table_p->ta_buckets); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, table_p->ta_buckets, table_p->ta_bucket_n * sizeof(table_entry_t *))) { return TABLE_ERROR_FREE; } } table_p->ta_magic = 0; if (table_p->ta_free_func == NULL) { free(table_p); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, table_p, sizeof(table_t))) { if (ret == TABLE_ERROR_NONE) { ret = TABLE_ERROR_FREE; } } return ret; } /* * int table_insert_kd * * DESCRIPTION: * * Like table_insert except it passes back a pointer to the key and * the data buffers after they have been inserted into the table * structure. * * This routine adds a key/data pair both of which are made up of a * buffer of bytes and an associated size. Both the key and the data * will be copied into buffers allocated inside the table. If the key * exists already, the associated data will be replaced if the * overwrite flag is set, otherwise an error is returned. * * NOTE: be very careful changing the values since the table library * provides the pointers to its memory. The key can _never_ be * changed otherwise you will not find it again. The data can be * changed but its length can never be altered unless you delete and * re-insert it into the table. * * WARNING: The pointers to the key and data are not in any specific * alignment. Accessing the key and/or data as an short, integer, or * long pointer directly can cause problems. * * WARNING: Replacing a data cell (not inserting) will cause the table * linked list to be temporarily invalid. Care must be taken with * multiple threaded programs which are relying on the first/next * linked list to be always valid. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer into which we will be inserting a * new key/data pair. * * key_buf - Buffer of bytes of the key that we are inserting. If you * are storing an (int) as the key (for example) then key_buf should * be a (int *). * * key_size - Size of the key_buf buffer. If set to < 0 then the * library will do a strlen of key_buf and add 1 for the '\0'. If you * are storing an (int) as the key (for example) then key_size should * be sizeof(int). * * data_buf - Buffer of bytes of the data that we are inserting. If * it is NULL then the library will allocate space for the data in the * table without copying in any information. If data_buf is NULL and * data_size is 0 then the library will associate a NULL data pointer * with the key. If you are storing a (long) as the data (for * example) then data_buf should be a (long *). * * data_size - Size of the data_buf buffer. If set to < 0 then the * library will do a strlen of data_buf and add 1 for the '\0'. If * you are storing an (long) as the key (for example) then key_size * should be sizeof(long). * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the key storage that was allocated in the table. If you are * storing an (int) as the key (for example) then key_buf_p should be * (int **) i.e. the address of a (int *). * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that was allocated in the table. If you are * storing an (long) as the data (for example) then data_buf_p should * be (long **) i.e. the address of a (long *). * * overwrite - Flag which, if set to 1, will allow the overwriting of * the data in the table with the new data if the key already exists * in the table. */ int table_insert_kd(table_t *table_p, const void *key_buf, const int key_size, const void *data_buf, const int data_size, void **key_buf_p, void **data_buf_p, const char overwrite_b) { int bucket; unsigned int ksize, dsize, new_size, old_size, copy_size; table_entry_t *entry_p, *last_p, *new_entry_p; void *key_copy_p, *data_copy_p; /* check the arguments */ if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (key_buf == NULL) { return TABLE_ERROR_ARG_NULL; } /* data_buf can be null but size must be >= 0, if it isn't null size != 0 */ if ((data_buf == NULL && data_size < 0) || (data_buf != NULL && data_size == 0)) { return TABLE_ERROR_SIZE; } #ifndef NO_MMAP /* no mmap support so immediate error */ if (table_p->ta_mmap != NULL) { return TABLE_ERROR_MMAP_OP; } #endif /* determine sizes of key and data */ if (key_size < 0) { ksize = strlen((char *)key_buf) + sizeof(char); } else { ksize = key_size; } if (data_size < 0) { dsize = strlen((char *)data_buf) + sizeof(char); } else { dsize = data_size; } /* get the bucket number via a hash function */ bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; /* look for the entry in this bucket, only check keys of the same size */ last_p = NULL; for (entry_p = table_p->ta_buckets[bucket]; entry_p != NULL; last_p = entry_p, entry_p = entry_p->te_next_p) { if (entry_p->te_key_size == ksize && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) { break; } } /* did we find it? then we are in replace mode. */ if (entry_p != NULL) { /* can we not overwrite existing data? */ if (! overwrite_b) { SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } return TABLE_ERROR_OVERWRITE; } /* re-alloc entry's data if the new size != the old */ if (dsize != entry_p->te_data_size) { /* * First we delete it from the list to keep the list whole. * This properly preserves the linked list in case we have a * thread marching through the linked list while we are * inserting. Maybe this is an unnecessary protection but it * should not harm that much. */ if (last_p == NULL) { table_p->ta_buckets[bucket] = entry_p->te_next_p; } else { last_p->te_next_p = entry_p->te_next_p; } /* * Realloc the structure which may change its pointer. NOTE: * this may change any previous data_key_p and data_copy_p * pointers. */ new_size = entry_size(table_p, entry_p->te_key_size, dsize); if (table_p->ta_resize_func == NULL) { /* if the alloc function has not been overriden do realloc */ if (table_p->ta_alloc_func == NULL) { entry_p = (table_entry_t *)realloc(entry_p, new_size); if (entry_p == NULL) { return TABLE_ERROR_ALLOC; } } else { old_size = new_size - dsize + entry_p->te_data_size; /* * if the user did override alloc but not resize, assume * that the user's allocation functions can't grok realloc * and do it ourselves the hard way. */ new_entry_p = (table_entry_t *)table_p->ta_alloc_func(table_p->ta_mem_pool, new_size); if (new_entry_p == NULL) { return TABLE_ERROR_ALLOC; } if (new_size > old_size) { copy_size = old_size; } else { copy_size = new_size; } memcpy(new_entry_p, entry_p, copy_size); if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, old_size)) { return TABLE_ERROR_FREE; } entry_p = new_entry_p; } } else { old_size = new_size - dsize + entry_p->te_data_size; entry_p = (table_entry_t *) table_p->ta_resize_func(table_p->ta_mem_pool, entry_p, old_size, new_size); if (entry_p == NULL) { return TABLE_ERROR_ALLOC; } } /* add it back to the front of the list */ entry_p->te_data_size = dsize; entry_p->te_next_p = table_p->ta_buckets[bucket]; table_p->ta_buckets[bucket] = entry_p; } /* copy or replace data in storage */ if (dsize > 0) { if (table_p->ta_data_align == 0) { data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); } else { data_copy_p = entry_data_buf(table_p, entry_p); } if (data_buf != NULL) { memcpy(data_copy_p, data_buf, dsize); } } else { data_copy_p = NULL; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(data_buf_p, data_copy_p); /* returning from the section where we were overwriting table data */ return TABLE_ERROR_NONE; } /* * It is a new entry. */ /* allocate a new entry */ new_size = entry_size(table_p, ksize, dsize); if (table_p->ta_alloc_func == NULL) { entry_p = (table_entry_t *)malloc(new_size); } else { entry_p = (table_entry_t *)table_p->ta_alloc_func(table_p->ta_mem_pool, new_size); } if (entry_p == NULL) { return TABLE_ERROR_ALLOC; } /* copy key into storage */ entry_p->te_key_size = ksize; key_copy_p = ENTRY_KEY_BUF(entry_p); memcpy(key_copy_p, key_buf, ksize); /* copy data in */ entry_p->te_data_size = dsize; if (dsize > 0) { if (table_p->ta_data_align == 0) { data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); } else { data_copy_p = entry_data_buf(table_p, entry_p); } if (data_buf != NULL) { memcpy(data_copy_p, data_buf, dsize); } } else { data_copy_p = NULL; } SET_POINTER(key_buf_p, key_copy_p); SET_POINTER(data_buf_p, data_copy_p); /* insert into list, no need to append */ entry_p->te_next_p = table_p->ta_buckets[bucket]; table_p->ta_buckets[bucket] = entry_p; table_p->ta_entry_n++; /* do we need auto-adjust? */ if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) && SHOULD_TABLE_GROW(table_p)) { return table_adjust(table_p, table_p->ta_entry_n); } return TABLE_ERROR_NONE; } /* * int table_insert * * DESCRIPTION: * * Exactly the same as table_insert_kd except it does not pass back a * pointer to the key after they have been inserted into the table * structure. This is still here for backwards compatibility. * * See table_insert_kd for more information. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer into which we will be inserting a * new key/data pair. * * key_buf - Buffer of bytes of the key that we are inserting. If you * are storing an (int) as the key (for example) then key_buf should * be a (int *). * * key_size - Size of the key_buf buffer. If set to < 0 then the * library will do a strlen of key_buf and add 1 for the '\0'. If you * are storing an (int) as the key (for example) then key_size should * be sizeof(int). * * data_buf - Buffer of bytes of the data that we are inserting. If * it is NULL then the library will allocate space for the data in the * table without copying in any information. If data_buf is NULL and * data_size is 0 then the library will associate a NULL data pointer * with the key. If you are storing a (long) as the data (for * example) then data_buf should be a (long *). * * data_size - Size of the data_buf buffer. If set to < 0 then the * library will do a strlen of data_buf and add 1 for the '\0'. If * you are storing an (long) as the key (for example) then key_size * should be sizeof(long). * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that was allocated in the table. If you are * storing an (long) as the data (for example) then data_buf_p should * be (long **) i.e. the address of a (long *). * * overwrite - Flag which, if set to 1, will allow the overwriting of * the data in the table with the new data if the key already exists * in the table. */ int table_insert(table_t *table_p, const void *key_buf, const int key_size, const void *data_buf, const int data_size, void **data_buf_p, const char overwrite_b) { return table_insert_kd(table_p, key_buf, key_size, data_buf, data_size, NULL, data_buf_p, overwrite_b); } /* * int table_retrieve * * DESCRIPTION: * * This routine looks up a key made up of a buffer of bytes and an * associated size in the table. If found then it returns the * associated data information. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer into which we will be searching * for the key. * * key_buf - Buffer of bytes of the key that we are searching for. If * you are looking for an (int) as the key (for example) then key_buf * should be a (int *). * * key_size - Size of the key_buf buffer. If set to < 0 then the * library will do a strlen of key_buf and add 1 for the '\0'. If you * are looking for an (int) as the key (for example) then key_size * should be sizeof(int). * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that was allocated in the table and that is * associated with the key. If a (long) was stored as the data (for * example) then data_buf_p should be (long **) i.e. the address of a * (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data stored in the table that is associated with * the key. */ int table_retrieve(table_t *table_p, const void *key_buf, const int key_size, void **data_buf_p, int *data_size_p) { int bucket; unsigned int ksize; table_entry_t *entry_p, **buckets; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (key_buf == NULL) { return TABLE_ERROR_ARG_NULL; } /* find key size */ if (key_size < 0) { ksize = strlen((char *)key_buf) + sizeof(char); } else { ksize = key_size; } /* get the bucket number via a has function */ bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; /* look for the entry in this bucket, only check keys of the same size */ buckets = table_p->ta_buckets; for (entry_p = buckets[bucket]; entry_p != NULL; entry_p = entry_p->te_next_p) { entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p); if (entry_p->te_key_size == ksize && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) { break; } } /* not found? */ if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * int table_delete * * DESCRIPTION: * * This routine looks up a key made up of a buffer of bytes and an * associated size in the table. If found then it will be removed * from the table. The associated data can be passed back to the user * if requested. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * NOTE: this could be an allocation error if the library is to return * the data to the user. * * ARGUMENTS: * * table_p - Table structure pointer from which we will be deleteing * the key. * * key_buf - Buffer of bytes of the key that we are searching for to * delete. If you are deleting an (int) key (for example) then * key_buf should be a (int *). * * key_size - Size of the key_buf buffer. If set to < 0 then the * library will do a strlen of key_buf and add 1 for the '\0'. If you * are deleting an (int) key (for example) then key_size should be * sizeof(int). * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that was allocated in the table and that was * associated with the key. If a (long) was stored as the data (for * example) then data_buf_p should be (long **) i.e. the address of a * (long *). If a pointer is passed in, the caller is responsible for * freeing it after use. If data_buf_p is NULL then the library will * free up the data allocation itself. * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that was stored in the table and that was * associated with the key. */ int table_delete(table_t *table_p, const void *key_buf, const int key_size, void **data_buf_p, int *data_size_p) { int bucket; unsigned int ksize; unsigned char *data_copy_p; table_entry_t *entry_p, *last_p; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (key_buf == NULL) { return TABLE_ERROR_ARG_NULL; } #ifndef NO_MMAP /* no mmap support so immediate error */ if (table_p->ta_mmap != NULL) { return TABLE_ERROR_MMAP_OP; } #endif /* get the key size */ if (key_size < 0) { ksize = strlen((char *)key_buf) + sizeof(char); } else { ksize = key_size; } /* find our bucket */ bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; /* look for the entry in this bucket, only check keys of the same size */ for (last_p = NULL, entry_p = table_p->ta_buckets[bucket]; entry_p != NULL; last_p = entry_p, entry_p = entry_p->te_next_p) { if (entry_p->te_key_size == ksize && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) { break; } } /* did we find it? */ if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } /* * NOTE: we may want to adjust the linear counters here if the entry * we are deleting is the one we are pointing on or is ahead of the * one in the bucket list */ /* remove entry from the linked list */ if (last_p == NULL) { table_p->ta_buckets[bucket] = entry_p->te_next_p; } else { last_p->te_next_p = entry_p->te_next_p; } /* free entry */ if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { /* * if we were storing it compacted, we now need to malloc some * space if the user wants the value after the delete. */ if (table_p->ta_alloc_func == NULL) { *data_buf_p = malloc(entry_p->te_data_size); } else { *data_buf_p = table_p->ta_alloc_func(table_p->ta_mem_pool, entry_p->te_data_size); } if (*data_buf_p == NULL) { return TABLE_ERROR_ALLOC; } if (table_p->ta_data_align == 0) { data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); } else { data_copy_p = entry_data_buf(table_p, entry_p); } memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size); } } SET_POINTER(data_size_p, entry_p->te_data_size); if (table_p->ta_free_func == NULL) { free(entry_p); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size))) { return TABLE_ERROR_FREE; } table_p->ta_entry_n--; /* do we need auto-adjust down? */ if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN) && SHOULD_TABLE_SHRINK(table_p)) { return table_adjust(table_p, table_p->ta_entry_n); } return TABLE_ERROR_NONE; } /* * int table_delete_first * * DESCRIPTION: * * This is like the table_delete routines except it deletes the first * key/data pair in the table instead of an entry corresponding to a * particular key. The associated key and data information can be * passed back to the user if requested. This routines is handy to * clear out a table. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * NOTE: this could be an allocation error if the library is to return * the data to the user. * * ARGUMENTS: * * table_p - Table structure pointer from which we will be deleteing * the first key. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the first key that was allocated in the table. * If an (int) was stored as the first key (for example) then * key_buf_p should be (int **) i.e. the address of a (int *). If a * pointer is passed in, the caller is responsible for freeing it * after use. If key_buf_p is NULL then the library will free up the * key allocation itself. * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that was stored in the table and that was * associated with the key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that was allocated in the table and that was * associated with the key. If a (long) was stored as the data (for * example) then data_buf_p should be (long **) i.e. the address of a * (long *). If a pointer is passed in, the caller is responsible for * freeing it after use. If data_buf_p is NULL then the library will * free up the data allocation itself. * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that was stored in the table and that was * associated with the key. */ int table_delete_first(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { unsigned char *data_copy_p; table_entry_t *entry_p; table_linear_t linear; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } #ifndef NO_MMAP /* no mmap support so immediate error */ if (table_p->ta_mmap != NULL) { return TABLE_ERROR_MMAP_OP; } #endif /* take the first entry */ entry_p = first_entry(table_p, &linear); if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } /* * NOTE: we may want to adjust the linear counters here if the entry * we are deleting is the one we are pointing on or is ahead of the * one in the bucket list */ /* remove entry from the linked list */ table_p->ta_buckets[linear.tl_bucket_c] = entry_p->te_next_p; /* free entry */ if (key_buf_p != NULL) { if (entry_p->te_key_size == 0) { *key_buf_p = NULL; } else { /* * if we were storing it compacted, we now need to malloc some * space if the user wants the value after the delete. */ if (table_p->ta_alloc_func == NULL) { *key_buf_p = malloc(entry_p->te_key_size); } else { *key_buf_p = table_p->ta_alloc_func(table_p->ta_mem_pool, entry_p->te_key_size); } if (*key_buf_p == NULL) { return TABLE_ERROR_ALLOC; } memcpy(*key_buf_p, ENTRY_KEY_BUF(entry_p), entry_p->te_key_size); } } SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { /* * if we were storing it compacted, we now need to malloc some * space if the user wants the value after the delete. */ if (table_p->ta_alloc_func == NULL) { *data_buf_p = malloc(entry_p->te_data_size); } else { *data_buf_p = table_p->ta_alloc_func(table_p->ta_mem_pool, entry_p->te_data_size); } if (*data_buf_p == NULL) { return TABLE_ERROR_ALLOC; } if (table_p->ta_data_align == 0) { data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); } else { data_copy_p = entry_data_buf(table_p, entry_p); } memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size); } } SET_POINTER(data_size_p, entry_p->te_data_size); if (table_p->ta_free_func == NULL) { free(entry_p); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size))) { return TABLE_ERROR_FREE; } table_p->ta_entry_n--; /* do we need auto-adjust down? */ if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN) && SHOULD_TABLE_SHRINK(table_p)) { return table_adjust(table_p, table_p->ta_entry_n); } return TABLE_ERROR_NONE; } /* * int table_info * * DESCRIPTION: * * Get some information about a table_p structure. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting * information. * * num_buckets_p - Pointer to an integer which, if not NULL, will * contain the number of buckets in the table. * * num_entries_p - Pointer to an integer which, if not NULL, will * contain the number of entries stored in the table. */ int table_info(table_t *table_p, int *num_buckets_p, int *num_entries_p) { if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } SET_POINTER(num_buckets_p, table_p->ta_bucket_n); SET_POINTER(num_entries_p, table_p->ta_entry_n); return TABLE_ERROR_NONE; } /* * int table_adjust * * DESCRIPTION: * * Set the number of buckets in a table to a certain value. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer of which we are adjusting. * * bucket_n - Number buckets to adjust the table to. Set to 0 to * adjust the table to its number of entries. */ int table_adjust(table_t *table_p, const int bucket_n) { table_entry_t *entry_p, *next_p; table_entry_t **buckets, **bucket_p, **bounds_p; int bucket; unsigned int buck_n, bucket_size; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } #ifndef NO_MMAP /* no mmap support so immediate error */ if (table_p->ta_mmap != NULL) { return TABLE_ERROR_MMAP_OP; } #endif /* * NOTE: we walk through the entries and rehash them. If we stored * the hash value as a full int in the table-entry, all we would * have to do is remod it. */ /* normalize to the number of entries */ if (bucket_n == 0) { buck_n = table_p->ta_entry_n; } else { buck_n = bucket_n; } /* we must have at least 1 bucket */ if (buck_n == 0) { buck_n = 1; } (void)printf("growing table to %d\n", buck_n); /* make sure we have something to do */ if (buck_n == table_p->ta_bucket_n) { return TABLE_ERROR_NONE; } /* allocate a new bucket list */ bucket_size = buck_n * sizeof(table_entry_t *); if (table_p->ta_alloc_func == NULL) { buckets = (table_entry_t **)malloc(bucket_size); } else { buckets = (table_entry_t **)table_p->ta_alloc_func(table_p->ta_mem_pool, bucket_size); } if (buckets == NULL) { return TABLE_ERROR_ALLOC; } /* * We zero it ourselves to save the necessity of having a * table_mem_calloc_t memory override function. */ memset(buckets, 0, bucket_size); /* * run through each of the items in the current table and rehash * them into the newest bucket sizes */ bounds_p = table_p->ta_buckets + table_p->ta_bucket_n; for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) { for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) { /* hash the old data into the new table size */ bucket = hash(ENTRY_KEY_BUF(entry_p), entry_p->te_key_size, 0) % buck_n; /* record the next one now since we overwrite next below */ next_p = entry_p->te_next_p; /* insert into new list, no need to append */ entry_p->te_next_p = buckets[bucket]; buckets[bucket] = entry_p; /* * NOTE: we may want to adjust the bucket_c linear entry here to * keep it current */ } /* remove the old table pointers as we go by */ *bucket_p = NULL; } /* replace the table buckets with the new ones */ if (table_p->ta_free_func == NULL) { free(table_p->ta_buckets); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, table_p->ta_buckets, table_p->ta_bucket_n * sizeof(table_entry_t *))) { return TABLE_ERROR_FREE; } table_p->ta_buckets = buckets; table_p->ta_bucket_n = buck_n; return TABLE_ERROR_NONE; } /* * int table_type_size * * DESCRIPTION: * * Return the size of the internal table type. * * RETURNS: * * The size of the table_t type. * * ARGUMENTS: * * None. */ int table_type_size(void) { return sizeof(table_t); } /************************* linear access routines ****************************/ /* * int table_first * * DESCRIPTION: * * Find first element in a table and pass back information about the * key/data pair. If any of the key/data pointers are NULL then they * are ignored. * * NOTE: This function is not reentrant. More than one thread cannot * be doing a first and next on the same table at the same time. Use * the table_first_r version below for this. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * first element. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the first key that is allocated in the table. If * an (int) is stored as the first key (for example) then key_buf_p * should be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table and that is * associated with the first key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that is allocated in the table and that is * associated with the first key. If a (long) is stored as the data * (for example) then data_buf_p should be (long **) i.e. the address * of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table and that is * associated with the first key. */ int table_first(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } /* initialize our linear magic number */ table_p->ta_linear.tl_magic = LINEAR_MAGIC; entry_p = first_entry(table_p, &table_p->ta_linear); if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * int table_next * * DESCRIPTION: * * Find the next element in a table and pass back information about * the key/data pair. If any of the key/data pointers are NULL then * they are ignored. * * NOTE: This function is not reentrant. More than one thread cannot * be doing a first and next on the same table at the same time. Use * the table_next_r version below for this. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * next element. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the next key that is allocated in the table. If * an (int) is stored as the next key (for example) then key_buf_p * should be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table and that is * associated with the next key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that is allocated in the table and that is * associated with the next key. If a (long) is stored as the data * (for example) then data_buf_p should be (long **) i.e. the address * of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table and that is * associated with the next key. */ int table_next(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p; int error; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (table_p->ta_linear.tl_magic != LINEAR_MAGIC) { return TABLE_ERROR_LINEAR; } /* move to the next entry */ entry_p = next_entry(table_p, &table_p->ta_linear, &error); if (entry_p == NULL) { return error; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * int table_this * * DESCRIPTION: * * Find the current element in a table and pass back information about * the key/data pair. If any of the key/data pointers are NULL then * they are ignored. * * NOTE: This function is not reentrant. Use the table_current_r * version below. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * current element. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the current key that is allocated in the table. * If an (int) is stored as the current key (for example) then * key_buf_p should be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table and that is * associated with the current key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that is allocated in the table and that is * associated with the current key. If a (long) is stored as the data * (for example) then data_buf_p should be (long **) i.e. the address * of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table and that is * associated with the current key. */ int table_this(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p = NULL; int entry_c; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (table_p->ta_linear.tl_magic != LINEAR_MAGIC) { return TABLE_ERROR_LINEAR; } /* if we removed an item that shorted the bucket list, we may get this */ if (table_p->ta_linear.tl_bucket_c >= table_p->ta_bucket_n) { /* * NOTE: this might happen if we delete an item which shortens the * table bucket numbers. */ return TABLE_ERROR_NOT_FOUND; } /* find the entry which is the nth in the list */ entry_p = table_p->ta_buckets[table_p->ta_linear.tl_bucket_c]; /* NOTE: we swap the order here to be more efficient */ for (entry_c = table_p->ta_linear.tl_entry_c; entry_c > 0; entry_c--) { /* did we reach the end of the list? */ if (entry_p == NULL) { break; } entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; } /* is this a NOT_FOUND or a LINEAR error */ if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * int table_first_r * * DESCRIPTION: * * Reetrant version of the table_first routine above. Find first * element in a table and pass back information about the key/data * pair. If any of the key/data pointers are NULL then they are * ignored. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * first element. * * linear_p - Pointer to a table linear structure which is initialized * here. The same pointer should then be passed to table_next_r * below. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the first key that is allocated in the table. If * an (int) is stored as the first key (for example) then key_buf_p * should be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table and that is * associated with the first key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that is allocated in the table and that is * associated with the first key. If a (long) is stored as the data * (for example) then data_buf_p should be (long **) i.e. the address * of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table and that is * associated with the first key. */ int table_first_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (linear_p == NULL) { return TABLE_ERROR_ARG_NULL; } /* initialize our linear magic number */ linear_p->tl_magic = LINEAR_MAGIC; entry_p = first_entry(table_p, linear_p); if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * int table_next_r * * DESCRIPTION: * * Reetrant version of the table_next routine above. Find next * element in a table and pass back information about the key/data * pair. If any of the key/data pointers are NULL then they are * ignored. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * next element. * * linear_p - Pointer to a table linear structure which is incremented * here. The same pointer must have been passed to table_first_r * first so that it can be initialized. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the next key that is allocated in the table. If * an (int) is stored as the next key (for example) then key_buf_p * should be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL will be set * to the size of the key that is stored in the table and that is * associated with the next key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that is allocated in the table and that is * associated with the next key. If a (long) is stored as the data * (for example) then data_buf_p should be (long **) i.e. the address * of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table and that is * associated with the next key. */ int table_next_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p; int error; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (linear_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (linear_p->tl_magic != LINEAR_MAGIC) { return TABLE_ERROR_LINEAR; } /* move to the next entry */ entry_p = next_entry(table_p, linear_p, &error); if (entry_p == NULL) { return error; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * int table_this_r * * DESCRIPTION: * * Reetrant version of the table_this routine above. Find current * element in a table and pass back information about the key/data * pair. If any of the key/data pointers are NULL then they are * ignored. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * current element. * * linear_p - Pointer to a table linear structure which is accessed * here. The same pointer must have been passed to table_first_r * first so that it can be initialized. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of the current key that is allocated in the table. * If an (int) is stored as the current key (for example) then * key_buf_p should be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table and that is * associated with the current key. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage that is allocated in the table and that is * associated with the current key. If a (long) is stored as the data * (for example) then data_buf_p should be (long **) i.e. the address * of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table and that is * associated with the current key. */ int table_this_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p; int entry_c; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (linear_p->tl_magic != LINEAR_MAGIC) { return TABLE_ERROR_LINEAR; } /* if we removed an item that shorted the bucket list, we may get this */ if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { /* * NOTE: this might happen if we delete an item which shortens the * table bucket numbers. */ return TABLE_ERROR_NOT_FOUND; } /* find the entry which is the nth in the list */ for (entry_c = linear_p->tl_entry_c, entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; entry_p != NULL && entry_c > 0; entry_c--, entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p) { } if (entry_p == NULL) { return TABLE_ERROR_NOT_FOUND; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /******************************* mmap routines *******************************/ /* * table_t *table_mmap * * DESCRIPTION: * * Mmap a table from a file that had been written to disk earlier via * table_write. * * RETURNS: * * A pointer to the new table structure which must be passed to * table_munmap to be deallocated. On error a NULL is returned. * * ARGUMENTS: * * path - Table file to mmap in. * * error_p - Pointer to an integer which, if not NULL, will contain a * table error code. */ table_t *table_mmap(const char *path, int *error_p) { #ifdef NO_MMAP /* no mmap support so immediate error */ SET_POINTER(error_p, TABLE_ERROR_MMAP_NONE); return NULL; #else table_t *table_p; struct stat sbuf; int fd, state; table_p = (table_t *)malloc(sizeof(table_t)); if (table_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); return NULL; } /* open the mmap file */ fd = open(path, O_RDONLY, 0); if (fd < 0) { free(table_p); SET_POINTER(error_p, TABLE_ERROR_OPEN); return NULL; } /* get the file size */ if (fstat(fd, &sbuf) != 0) { free(table_p); SET_POINTER(error_p, TABLE_ERROR_OPEN); return NULL; } /* mmap the space and close the file */ #ifdef __alpha state = (MAP_SHARED | MAP_FILE | MAP_VARIABLE); #else state = MAP_SHARED; #endif table_p->ta_mmap = (table_t *)mmap((caddr_t)0, sbuf.st_size, PROT_READ, state, fd, 0); (void)close(fd); if (table_p->ta_mmap == (table_t *)MAP_FAILED) { SET_POINTER(error_p, TABLE_ERROR_MMAP); return NULL; } /* is the mmap file contain bad info or maybe another system type? */ if (table_p->ta_mmap->ta_magic != TABLE_MAGIC) { SET_POINTER(error_p, TABLE_ERROR_PNT); return NULL; } /* sanity check on the file size */ if (table_p->ta_mmap->ta_file_size != sbuf.st_size) { SET_POINTER(error_p, TABLE_ERROR_SIZE); return NULL; } /* copy the fields out of the mmap file into our memory version */ table_p->ta_magic = TABLE_MAGIC; table_p->ta_flags = table_p->ta_mmap->ta_flags; table_p->ta_bucket_n = table_p->ta_mmap->ta_bucket_n; table_p->ta_entry_n = table_p->ta_mmap->ta_entry_n; table_p->ta_data_align = table_p->ta_mmap->ta_data_align; table_p->ta_buckets = TABLE_POINTER(table_p, table_entry_t **, table_p->ta_mmap->ta_buckets); table_p->ta_linear.tl_magic = 0; table_p->ta_linear.tl_bucket_c = 0; table_p->ta_linear.tl_entry_c = 0; /* mmap is already set */ table_p->ta_file_size = table_p->ta_mmap->ta_file_size; SET_POINTER(error_p, TABLE_ERROR_NONE); return table_p; #endif } /* * int table_munmap * * DESCRIPTION: * * Unmmap a table that was previously mmapped using table_mmap. * * RETURNS: * * Returns table error codes. * * ARGUMENTS: * * table_p - Mmaped table pointer to unmap. */ int table_munmap(table_t *table_p) { #ifdef NO_MMAP /* no mmap support so immediate error */ return TABLE_ERROR_MMAP_NONE; #else if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (table_p->ta_mmap == NULL) { return TABLE_ERROR_PNT; } (void)munmap((caddr_t)table_p->ta_mmap, table_p->ta_file_size); table_p->ta_magic = 0; free(table_p); return TABLE_ERROR_NONE; #endif } /******************************* file routines *******************************/ /* * int table_read * * DESCRIPTION: * * Read in a table from a file that had been written to disk earlier * via table_write. * * RETURNS: * * Success - Pointer to the new table structure which must be passed * to table_free to be deallocated. * * Failure - NULL * * ARGUMENTS: * * path - Table file to read in. * * error_p - Pointer to an integer which, if not NULL, will contain a * table error code. */ table_t *table_read(const char *path, int *error_p) { unsigned int size; int fd, ent_size; FILE *infile; table_entry_t entry, **bucket_p, *entry_p = NULL, *last_p; unsigned long pos; table_t *table_p; /* open the file */ fd = open(path, O_RDONLY, 0); if (fd < 0) { SET_POINTER(error_p, TABLE_ERROR_OPEN); return NULL; } /* allocate a table structure */ table_p = malloc(sizeof(table_t)); if (table_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); return NULL; } /* now open the fd to get buffered i/o */ infile = fdopen(fd, "r"); if (infile == NULL) { SET_POINTER(error_p, TABLE_ERROR_OPEN); return NULL; } /* read the main table struct */ if (fread(table_p, sizeof(table_t), 1, infile) != 1) { SET_POINTER(error_p, TABLE_ERROR_READ); free(table_p); return NULL; } table_p->ta_file_size = 0; /* is the mmap file contain bad info or maybe another system type? */ if (table_p->ta_magic != TABLE_MAGIC) { SET_POINTER(error_p, TABLE_ERROR_PNT); return NULL; } /* allocate the buckets */ table_p->ta_buckets = (table_entry_t **)calloc(table_p->ta_bucket_n, sizeof(table_entry_t *)); if (table_p->ta_buckets == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); free(table_p); return NULL; } if (fread(table_p->ta_buckets, sizeof(table_entry_t *), table_p->ta_bucket_n, infile) != (size_t)table_p->ta_bucket_n) { SET_POINTER(error_p, TABLE_ERROR_READ); free(table_p->ta_buckets); free(table_p); return NULL; } /* read in the entries */ for (bucket_p = table_p->ta_buckets; bucket_p < table_p->ta_buckets + table_p->ta_bucket_n; bucket_p++) { /* skip null buckets */ if (*bucket_p == NULL) { continue; } /* run through the entry list */ last_p = NULL; for (pos = *(unsigned long *)bucket_p;; pos = (unsigned long)entry_p->te_next_p) { /* read in the entry */ if (fseek(infile, pos, SEEK_SET) != 0) { SET_POINTER(error_p, TABLE_ERROR_SEEK); free(table_p->ta_buckets); free(table_p); if (entry_p != NULL) { free(entry_p); } /* the other table elements will not be freed */ return NULL; } if (fread(&entry, sizeof(struct table_shell_st), 1, infile) != 1) { SET_POINTER(error_p, TABLE_ERROR_READ); free(table_p->ta_buckets); free(table_p); if (entry_p != NULL) { free(entry_p); } /* the other table elements will not be freed */ return NULL; } /* make a new entry */ ent_size = entry_size(table_p, entry.te_key_size, entry.te_data_size); entry_p = (table_entry_t *)malloc(ent_size); if (entry_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); free(table_p->ta_buckets); free(table_p); /* the other table elements will not be freed */ return NULL; } entry_p->te_key_size = entry.te_key_size; entry_p->te_data_size = entry.te_data_size; entry_p->te_next_p = entry.te_next_p; if (last_p == NULL) { *bucket_p = entry_p; } else { last_p->te_next_p = entry_p; } /* determine how much more we have to read */ size = ent_size - sizeof(struct table_shell_st); if (fread(ENTRY_KEY_BUF(entry_p), sizeof(char), size, infile) != size) { SET_POINTER(error_p, TABLE_ERROR_READ); free(table_p->ta_buckets); free(table_p); free(entry_p); /* the other table elements will not be freed */ return NULL; } /* we are done if the next pointer is null */ if (entry_p->te_next_p == (unsigned long)0) { break; } last_p = entry_p; } } (void)fclose(infile); SET_POINTER(error_p, TABLE_ERROR_NONE); return table_p; } /* * int table_write * * DESCRIPTION: * * Write a table from memory to file. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Pointer to the table that we are writing to the file. * * path - Table file to write out to. * * mode - Mode of the file. This argument is passed on to open when * the file is created. */ int table_write(const table_t *table_p, const char *path, const int mode) { int fd, rem, ent_size; unsigned int bucket_c, bucket_size; unsigned long size; table_entry_t *entry_p, **buckets, **bucket_p, *next_p; table_t main_tab; FILE *outfile; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } fd = open(path, O_WRONLY | O_CREAT, mode); if (fd < 0) { return TABLE_ERROR_OPEN; } outfile = fdopen(fd, "w"); if (outfile == NULL) { return TABLE_ERROR_OPEN; } /* allocate a block of sizes for each bucket */ bucket_size = sizeof(table_entry_t *) * table_p->ta_bucket_n; if (table_p->ta_alloc_func == NULL) { buckets = (table_entry_t **)malloc(bucket_size); } else { buckets = (table_entry_t **)table_p->ta_alloc_func(table_p->ta_mem_pool, bucket_size); } if (buckets == NULL) { return TABLE_ERROR_ALLOC; } /* make a copy of the main struct */ main_tab = *table_p; /* start counting the bytes */ size = 0; size += sizeof(table_t); /* buckets go right after main struct */ main_tab.ta_buckets = (table_entry_t **)size; size += sizeof(table_entry_t *) * table_p->ta_bucket_n; /* run through and count the buckets */ for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) { bucket_p = table_p->ta_buckets + bucket_c; if (*bucket_p == NULL) { buckets[bucket_c] = NULL; continue; } buckets[bucket_c] = (table_entry_t *)size; for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) { size += entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size); /* * We now have to round the file to the nearest long so the * mmaping of the longs in the entry structs will work. */ rem = size & (sizeof(long) - 1); if (rem > 0) { size += sizeof(long) - rem; } } } /* add a \0 at the end to fill the last section */ size++; /* set the main fields */ main_tab.ta_linear.tl_magic = 0; main_tab.ta_linear.tl_bucket_c = 0; main_tab.ta_linear.tl_entry_c = 0; main_tab.ta_mmap = NULL; main_tab.ta_file_size = size; /* * Now we can start the writing because we got the bucket offsets. */ /* write the main table struct */ size = 0; if (fwrite(&main_tab, sizeof(table_t), 1, outfile) != 1) { if (table_p->ta_free_func == NULL) { free(buckets); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size); } return TABLE_ERROR_WRITE; } size += sizeof(table_t); if (fwrite(buckets, sizeof(table_entry_t *), table_p->ta_bucket_n, outfile) != (size_t)table_p->ta_bucket_n) { if (table_p->ta_free_func == NULL) { free(buckets); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size); } return TABLE_ERROR_WRITE; } size += sizeof(table_entry_t *) * table_p->ta_bucket_n; /* write out the entries */ for (bucket_p = table_p->ta_buckets; bucket_p < table_p->ta_buckets + table_p->ta_bucket_n; bucket_p++) { for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) { ent_size = entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size); size += ent_size; /* round to nearest long here so we can write copy */ rem = size & (sizeof(long) - 1); if (rem > 0) { size += sizeof(long) - rem; } next_p = entry_p->te_next_p; if (next_p != NULL) { entry_p->te_next_p = (table_entry_t *)size; } /* now write to disk */ if (fwrite(entry_p, ent_size, 1, outfile) != 1) { if (table_p->ta_free_func == NULL) { free(buckets); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size); } return TABLE_ERROR_WRITE; } /* restore the next pointer */ if (next_p != NULL) { entry_p->te_next_p = next_p; } /* now write the padding information */ if (rem > 0) { rem = sizeof(long) - rem; /* * NOTE: this won't leave fseek'd space at the end but we * don't care there because there is no accessed memory * afterwards. We write 1 \0 at the end to make sure. */ if (fseek(outfile, rem, SEEK_CUR) != 0) { if (table_p->ta_free_func == NULL) { free(buckets); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size); } return TABLE_ERROR_SEEK; } } } } /* * Write a \0 at the end of the file to make sure that the last * fseek filled with nulls. */ (void)fputc('\0', outfile); (void)fclose(outfile); if (table_p->ta_free_func == NULL) { free(buckets); } else if (! table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size)) { return TABLE_ERROR_FREE; } return TABLE_ERROR_NONE; } /******************************** table order ********************************/ /* * table_entry_t *table_order * * DESCRIPTION: * * Order a table by building an array of table entry pointers and then * sorting this array using the qsort function. To retrieve the * sorted entries, you can then use the table_entry routine to access * each entry in order. * * NOTE: This routine is thread safe and makes use of an internal * status qsort function. * * RETURNS: * * Success - An allocated list of table-linear structures which must * be freed by table_order_free later. * * Failure - NULL * * ARGUMENTS: * * table_p - Pointer to the table that we are ordering. * * compare - Comparison function defined by the user. Its definition * is at the top of the table.h file. If this is NULL then it will * order the table my memcmp-ing the keys. * * num_entries_p - Pointer to an integer which, if not NULL, will * contain the number of entries in the returned entry pointer array. * * error_p - Pointer to an integer which, if not NULL, will contain a * table error code. */ table_entry_t **table_order(table_t *table_p, table_compare_t compare, int *num_entries_p, int *error_p) { table_entry_t *entry_p, **entries, **entries_p; table_linear_t linear; compare_t comp_func; unsigned int entries_size; int ret; if (table_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ARG_NULL); return NULL; } if (table_p->ta_magic != TABLE_MAGIC) { SET_POINTER(error_p, TABLE_ERROR_PNT); return NULL; } /* there must be at least 1 element in the table for this to work */ if (table_p->ta_entry_n == 0) { SET_POINTER(error_p, TABLE_ERROR_EMPTY); return NULL; } entries_size = table_p->ta_entry_n * sizeof(table_entry_t *); if (table_p->ta_alloc_func == NULL) { entries = (table_entry_t **)malloc(entries_size); } else { entries = (table_entry_t **)table_p->ta_alloc_func(table_p->ta_mem_pool, entries_size); } if (entries == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); return NULL; } /* get a pointer to all entries */ entry_p = first_entry(table_p, &linear); if (entry_p == NULL) { if (table_p->ta_free_func == NULL) { free(entries); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, entries, entries_size); } SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); return NULL; } /* add all of the entries to the array */ for (entries_p = entries; entry_p != NULL; entry_p = next_entry(table_p, &linear, &ret)) { *entries_p++ = entry_p; } if (ret != TABLE_ERROR_NOT_FOUND) { if (table_p->ta_free_func == NULL) { free(entries); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, entries, entries_size); } SET_POINTER(error_p, ret); return NULL; } if (compare == NULL) { /* this is regardless of the alignment */ comp_func = local_compare; } else if (table_p->ta_data_align == 0) { comp_func = external_compare; } else { comp_func = external_compare_align; } /* now qsort the entire entries array from first to last element */ ret = split((unsigned char *)entries, (unsigned char *)(entries + table_p->ta_entry_n - 1), sizeof(table_entry_t *), comp_func, compare, table_p); if (ret != TABLE_ERROR_NONE) { if (table_p->ta_free_func == NULL) { free(entries); } else { (void)table_p->ta_free_func(table_p->ta_mem_pool, entries, entries_size); } SET_POINTER(error_p, ret); return NULL; } SET_POINTER(num_entries_p, table_p->ta_entry_n); SET_POINTER(error_p, TABLE_ERROR_NONE); return entries; } /* * int table_order_free * * DESCRIPTION: * * Free the pointer returned by the table_order or table_order_pos * routines. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Pointer to the table. * * table_entries - Allocated list of entry pointers returned by * table_order. * * entry_n - Number of entries in the array as passed back by * table_order or table_order_pos in num_entries_p. */ int table_order_free(table_t *table_p, table_entry_t **table_entries, const int entry_n) { int ret, final = TABLE_ERROR_NONE; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (table_p->ta_free_func == NULL) { free(table_entries); } else { ret = table_p->ta_free_func(table_p->ta_mem_pool, table_entries, sizeof(table_entry_t *) * entry_n); if (ret != 1) { final = TABLE_ERROR_FREE; } } return final; } /* * int table_entry * * DESCRIPTION: * * Get information about an element. The element is one from the * array returned by the table_order function. If any of the key/data * pointers are NULL then they are ignored. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * element. * * entry_p - Pointer to a table entry from the array returned by the * table_order function. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of this entry that is allocated in the table. If an * (int) is stored as this entry (for example) then key_buf_p should * be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage of this entry that is allocated in the table. * If a (long) is stored as this entry data (for example) then * data_buf_p should be (long **) i.e. the address of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table. */ int table_entry(table_t *table_p, table_entry_t *entry_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (entry_p == NULL) { return TABLE_ERROR_ARG_NULL; } SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); SET_POINTER(key_size_p, entry_p->te_key_size); if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } SET_POINTER(data_size_p, entry_p->te_data_size); return TABLE_ERROR_NONE; } /* * table_linear_t *table_order_pos * * DESCRIPTION: * * Order a table by building an array of table linear structures and * then sorting this array using the qsort function. To retrieve the * sorted entries, you can then use the table_entry_pos routine to * access each entry in order. * * NOTE: This routine is thread safe and makes use of an internal * status qsort function. * * RETURNS: * * Success - An allocated list of table-linear structures which must * be freed by table_order_pos_free later. * * Failure - NULL * * ARGUMENTS: * * table_p - Pointer to the table that we are ordering. * * compare - Comparison function defined by the user. Its definition * is at the top of the table.h file. If this is NULL then it will * order the table my memcmp-ing the keys. * * num_entries_p - Pointer to an integer which, if not NULL, will * contain the number of entries in the returned entry pointer array. * * error_p - Pointer to an integer which, if not NULL, will contain a * table error code. */ table_linear_t *table_order_pos(table_t *table_p, table_compare_t compare, int *num_entries_p, int *error_p) { table_entry_t *entry_p; table_linear_t linear, *linears, *linears_p; compare_t comp_func; int ret; if (table_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_ARG_NULL); return NULL; } if (table_p->ta_magic != TABLE_MAGIC) { SET_POINTER(error_p, TABLE_ERROR_PNT); return NULL; } /* there must be at least 1 element in the table for this to work */ if (table_p->ta_entry_n == 0) { SET_POINTER(error_p, TABLE_ERROR_EMPTY); return NULL; } if (table_p->ta_alloc_func == NULL) { linears = (table_linear_t *)malloc(table_p->ta_entry_n * sizeof(table_linear_t)); } else { linears = (table_linear_t *)table_p->ta_alloc_func(table_p->ta_mem_pool, table_p->ta_entry_n * sizeof(table_linear_t)); } if (linears == NULL) { SET_POINTER(error_p, TABLE_ERROR_ALLOC); return NULL; } /* get a pointer to all entries */ entry_p = first_entry(table_p, &linear); if (entry_p == NULL) { SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); return NULL; } /* add all of the entries to the array */ for (linears_p = linears; entry_p != NULL; entry_p = next_entry(table_p, &linear, &ret)) { *linears_p++ = linear; } if (ret != TABLE_ERROR_NOT_FOUND) { SET_POINTER(error_p, ret); return NULL; } if (compare == NULL) { /* this is regardless of the alignment */ comp_func = local_compare_pos; } else if (table_p->ta_data_align == 0) { comp_func = external_compare_pos; } else { comp_func = external_compare_align_pos; } /* now qsort the entire entries array from first to last element */ split((unsigned char *)linears, (unsigned char *)(linears + table_p->ta_entry_n - 1), sizeof(table_linear_t), comp_func, compare, table_p); if (num_entries_p != NULL) { *num_entries_p = table_p->ta_entry_n; } SET_POINTER(error_p, TABLE_ERROR_NONE); return linears; } /* * int table_order_pos_free * * DESCRIPTION: * * Free the pointer returned by the table_order or table_order_pos * routines. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Pointer to the table. * * table_entries - Allocated list of entry pointers returned by * table_order_pos. * * entry_n - Number of entries in the array as passed back by * table_order or table_order_pos in num_entries_p. */ int table_order_pos_free(table_t *table_p, table_linear_t *table_entries, const int entry_n) { int ret, final = TABLE_ERROR_NONE; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (table_p->ta_free_func == NULL) { free(table_entries); } else { ret = table_p->ta_free_func(table_p->ta_mem_pool, table_entries, sizeof(table_linear_t) * entry_n); if (ret != 1) { final = TABLE_ERROR_FREE; } } return final; } /* * int table_entry_pos * * DESCRIPTION: * * Get information about an element. The element is one from the * array returned by the table_order function. If any of the key/data * pointers are NULL then they are ignored. * * RETURNS: * * Success - TABLE_ERROR_NONE * * Failure - Table error code. * * ARGUMENTS: * * table_p - Table structure pointer from which we are getting the * element. * * linear_p - Pointer to a table linear structure from the array * returned by the table_order function. * * key_buf_p - Pointer which, if not NULL, will be set to the address * of the storage of this entry that is allocated in the table. If an * (int) is stored as this entry (for example) then key_buf_p should * be (int **) i.e. the address of a (int *). * * key_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the key that is stored in the table. * * data_buf_p - Pointer which, if not NULL, will be set to the address * of the data storage of this entry that is allocated in the table. * If a (long) is stored as this entry data (for example) then * data_buf_p should be (long **) i.e. the address of a (long *). * * data_size_p - Pointer to an integer which, if not NULL, will be set * to the size of the data that is stored in the table. */ int table_entry_pos(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p) { table_entry_t *entry_p; int ret; if (table_p == NULL) { return TABLE_ERROR_ARG_NULL; } if (table_p->ta_magic != TABLE_MAGIC) { return TABLE_ERROR_PNT; } if (linear_p == NULL) { return TABLE_ERROR_ARG_NULL; } /* find the associated entry */ entry_p = this_entry(table_p, linear_p, &ret); if (entry_p == NULL) { return ret; } if (key_buf_p != NULL) { *key_buf_p = ENTRY_KEY_BUF(entry_p); } if (key_size_p != NULL) { *key_size_p = entry_p->te_key_size; } if (data_buf_p != NULL) { if (entry_p->te_data_size == 0) { *data_buf_p = NULL; } else { if (table_p->ta_data_align == 0) { *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); } else { *data_buf_p = entry_data_buf(table_p, entry_p); } } } if (data_size_p != NULL) { *data_size_p = entry_p->te_data_size; } return TABLE_ERROR_NONE; } /* * const char *table_strerror * * DESCRIPTION: * * Return the corresponding string for the error number. * * RETURNS: * * Success - String equivalient of the error. * * Failure - String "invalid error code" * * ARGUMENTS: * * error - Error number that we are converting. */ const char *table_strerror(const int error) { error_str_t *err_p; for (err_p = errors; err_p->es_error != 0; err_p++) { if (err_p->es_error == error) { return err_p->es_string; } } return INVALID_ERROR; }