/* Objective Modula-2 Compiler (objm2c) * * @file objm2_key_value_storage.c * * Low level key/value table storage implementation * * Author: Benjamin Kowarsch * * Copyright (C) 2009 The Objective Modula-2 Project. All rights reserved. * * License: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met * * 1) This file, or any part thereof, may NOT be hosted on websites which * contain advertising, unless specific prior written permission has been * obtained. The licensor will grant such permission upon request at its * sole discretion to websites the licensor does NOT consider abusive in * their use of advertising. Small notices in the footer of a website * naming corporate rights holders, infrastructure providers or sponsors * are not considered advertising in the context of this license. * * 2) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 3) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and other materials provided with the distribution. * * 4) Neither the author's name nor the names of any contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * 5) Where this list of conditions or the following disclaimer, in part or * as a whole is overruled or nullified by applicable law, no permission * is granted to use the software. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Version history: * * 2.00 2009-01-31 BK new file from various spin-offs of v.1.x * 2009-01-22 BK added status parameter to all functions * 2009-04-23 BK retrieval both by copy and by reference */ #include "common_types.h" #include "common_macros.h" #include "objm2_alloc.h" #include "objm2_key_value_storage.h" // --------------------------------------------------------------------------- // KVS table entry pointer type // --------------------------------------------------------------------------- struct _objm2_kvs_entry_s; /* FORWARD */ typedef struct _objm2_kvs_entry_s *objm2_kvs_entry; // --------------------------------------------------------------------------- // KVS table entry type // --------------------------------------------------------------------------- struct _objm2_kvs_entry_s { uint32_t key; // unique 32 bit key cardinal size; // allocation size in bytes opaque_t value; // pointer to arbitrary data objm2_kvs_entry next; // next entry in same bucket word_t ref_count; // entry's reference count octet_t marked_for_removal; // must be true or false octet_t null_terminated_data; // must be true or false }; typedef struct _objm2_kvs_entry_s objm2_kvs_entry_s; // --------------------------------------------------------------------------- // KVS table type // --------------------------------------------------------------------------- typedef struct /* objm2_kvs_table_s */ { objm2_kvs_entry last_retrieved_entry; cardinal entry_count; cardinal bucket_count; objm2_kvs_entry bucket[0]; } objm2_kvs_table_s; // =========================================================================== // P R I V A T E F U N C T I O N P R O T O T Y P E S A N D M A C R O S // =========================================================================== #define _kvs_set_status(_status_p,_code) \ { if (_status_p != NULL) *_status_p = _code; } static objm2_kvs_entry _kvs_find_entry(objm2_kvs_table_t table, uint32_t key, objm2_kvs_status_t *status); static fmacro objm2_kvs_entry _kvs_new_entry(uint32_t key, void *value, cardinal len, objm2_kvs_status_t *status); static fmacro void *_kvs_copy_value(objm2_kvs_entry entry, objm2_kvs_status_t *status); static fmacro void _kvs_dispose_entry(objm2_kvs_entry entry, objm2_kvs_status_t *status); // =========================================================================== // P U B L I C F U N C T I O N I M P L E M E N T A T I O N S // =========================================================================== // --------------------------------------------------------------------------- // function: objm2_kvs_new_table(size, status) // --------------------------------------------------------------------------- // // Creates and returns a new KVS table object with number of buckets. // If zero is passed in , then the new table will be created with the // default table size as defined by OBJM2_KVS_DEFAULT_TABLE_SIZE. Returns // NULL if the KVS table object could not be created. // The status of the operation is passed back in , unless NULL was // passed in for . objm2_kvs_table_t objm2_kvs_new_table(cardinal size, objm2_kvs_status_t *status) { cardinal index, bucket_count; objm2_kvs_table_s *new_table; // determine table size if (size == 0) bucket_count = OBJM2_KVS_DEFAULT_TABLE_SIZE; else bucket_count = size; // allocate table base new_table = OBJM2_ALLOCATE(sizeof(objm2_kvs_table_s) + sizeof(objm2_kvs_entry) * (bucket_count - 1)); // exit if allocation failed if (new_table == NULL) { _kvs_set_status(status, OBJM2_KVS_STATUS_UNABLE_TO_ALLOCATE); return NULL; } // end if // initialise table meta data new_table->last_retrieved_entry = NULL; new_table->entry_count = 0; new_table->bucket_count = bucket_count; // initialise buckets with NULL pointers for (index = 0; index < bucket_count; index++) { new_table->bucket[index] = NULL; } // end for // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); // return a reference to the new table return (objm2_kvs_table_t)new_table; } // end objm2_kvs_new_table // --------------------------------------------------------------------------- // function: objm2_kvs_add_entry(table, key, value, len, status) // --------------------------------------------------------------------------- // // Adds a value for key to KVS table . The value to be stored is // passed as a pointer in . Values are copied into a newly allocated // container within the KVS table. The number of bytes to copy is passed in // . If a zero-value is passed in , then the pointer passed in // is treated as a pointer to a C string and data will be copied up to // and including the first zero-value byte encountered. The initial reference // count of the new entry is set to one. // // Keys must be unique. Existing entries are not replaced. Duplicate entries // are not added. The status of the operation is passed back in , // unless NULL was passed in for . void objm2_kvs_add_entry(objm2_kvs_table_t table, uint32_t key, objm2_kvs_data_t value, cardinal len, objm2_kvs_status_t *status) { cardinal index; objm2_kvs_status_t _status; objm2_kvs_entry new_entry, this_entry; objm2_kvs_table_s *this_table = (objm2_kvs_table_s *)table; // key must not be zero if (key == 0) { _kvs_set_status(status, OBJM2_KVS_STATUS_INVALID_KEY); return; } // end if // value must not be NULL if (value == NULL) { _kvs_set_status(status, OBJM2_KVS_STATUS_INVALID_DATA); return; } // end if // calculate the bucket index for key index = key % this_table->bucket_count; if /* bucket is empty */ (this_table->bucket[index] == NULL) { // create a new entry new_entry = _kvs_new_entry(key, value, len, &_status); // exit if allocation failed if (new_entry == NULL) { _kvs_set_status(status, _status); return; } // end if // link the empty bucket to the new entry this_table->bucket[index] = new_entry; // update the entry counter this_table->entry_count++; // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); } else /* bucket is not empty */ { // first entry in this bucket is starting point this_entry = (objm2_kvs_entry)this_table->bucket[index]; // check every entry in this bucket for a key match while ((this_entry->key != key) && (this_entry->next != NULL)) this_entry = this_entry->next; // the passed in key is unique if there was no key match if /* new key is unique */ (this_entry->key != key) { // create a new entry new_entry = _kvs_new_entry(key, value, len, &_status); // exit if allocation failed if (new_entry == NULL) { _kvs_set_status(status, _status); return; } // end if // link the final entry in the chain to the new entry this_entry->next = new_entry; // update the entry counter this_table->entry_count++; // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); } else /* key is not unique */ { // do not add a new entry // set status _kvs_set_status(status, OBJM2_KVS_STATUS_KEY_NOT_UNIQUE); } // end if } // end if return; } // end objm2_kvs_add_entry // --------------------------------------------------------------------------- // function: objm2_kvs_entry_exists(table, key, status) // --------------------------------------------------------------------------- // // Returns true if a valid entry for exists in KVS table
, // returns false otherwise. If an entry is found, valid or invalid, then it // will be cached internally and a subsequent search request for the same key // will check the cached entry first, which is slighly faster than a lookup of // a non-cached entry. The reference count of the entry is not modified. // The status of the operation is passed back in , unless NULL was // passed in for . bool objm2_kvs_entry_exists(objm2_kvs_table_t table, uint32_t key, objm2_kvs_status_t *status) { objm2_kvs_entry this_entry; // try to find entry for key this_entry = _kvs_find_entry(table, key, status); if /* entry not found */ (this_entry == NULL) { return false; } else if (this_entry->marked_for_removal) { _kvs_set_status(status, OBJM2_KVS_STATUS_ENTRY_PENDING_REMOVAL); return false; } else /* entry is valid */ { return true; } // end if } // end objm2_kvs_entry_exists // --------------------------------------------------------------------------- // function: objm2_kvs_value_for_key(table, key, copy, len, status) // --------------------------------------------------------------------------- // // Retrieves the table entry stored in
for either by copy or by // reference. If true is passed in for , then the function operates // in by-copy mode, otherwise it operates in by-reference mode. // // In by-copy mode, if the entry exists, a newly allocated copy of its value // is created, and a pointer to it is returned as function result. The size // of the entry's value (in bytes) is passed back in , unless the entry // was stored as a C string, in which case zero is passed back in . In // by-copy mode, the reference count for the entry is not incremented. // // In by-reference mode, if the entry exists, a pointer to the entry's value // is returned as function result. The size of the entry's value (in bytes) // is passed back in , unless the entry was stored as a C string, in // which case zero is passed back in . In by-reference mode the reference // count of the entry is incremented. // // If the entry has been successfully retrieved, then it is cached within the // table, regardless of whether it was returned by copy or by reference. // // If the entry does not exist, or, if it has been marked for removal, then // NULL is returned, no data is copied, no table meta data is modified, // no entry meta data is modified, and remains unchanged. // // The status of the operation is passed back in , unless NULL was // passed in for . /*const*/ objm2_kvs_data_t objm2_kvs_value_for_key(objm2_kvs_table_t table, uint32_t key, bool copy, cardinal *len, objm2_kvs_status_t *status) { octet_t *retrieved_value = NULL; objm2_kvs_entry this_entry; // try to find entry for key this_entry = _kvs_find_entry(table, key, status); if /* entry found */ (this_entry != NULL) { // check if entry is pending removal if (this_entry->marked_for_removal) { // set status _kvs_set_status(status, OBJM2_KVS_STATUS_ENTRY_PENDING_REMOVAL); // remember to return null, indicating entry not found retrieved_value = NULL; } // if not pending removal, prepare to return entry ... else if /* by copy */ (copy) { // copy the value of entry found and remember pointer to the copy retrieved_value = _kvs_copy_value(this_entry, status); } else /* by reference */ { // remember pointer to the entry's value itself retrieved_value = this_entry->value; // increment the reference count for entry this_entry->ref_count++; // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); } // end if // pass size (or zero) in len, but only if an actual value is returned if (retrieved_value != NULL) { // and don't try to write to a null pointer, just in case if (len != NULL) { // if value represents a C string then len must be zero if (this_entry->null_terminated_data) *len = 0; // otherwise len must contain the actual size else *len = this_entry->size; } // end if } // end if } // end if // return the remembered pointer return retrieved_value; } // end objm2_kvs_value_for_key // --------------------------------------------------------------------------- // function: objm2_kvs_reference_count_for_key(table, key, status) // --------------------------------------------------------------------------- // // Returns the reference count of the entry stored in
for . If // no entry exists for , then zero is returned. Valid entries always // have a reference count greater than zero. The status of the operation is // passed back in , unless NULL was passed in for . cardinal objm2_kvs_reference_count_for_key(objm2_kvs_table_t table, uint32_t key, objm2_kvs_status_t *status) { objm2_kvs_entry this_entry; this_entry = _kvs_find_entry(table, key, status); if (this_entry != NULL) return this_entry->ref_count; else return 0; // indicating nonexistent entry } // end objm2_kvs_reference_count_for_key // --------------------------------------------------------------------------- // function: objm2_kvs_release_entry(table, key, status) // --------------------------------------------------------------------------- // // Decrements the reference count of entry stored in
for by one. // If the entry has previously been marked for removal and its reference count // reaches one as a result of this release, then the entry will be removed. // The status of the operation is passed back in , unless NULL was // passed in for . void objm2_kvs_release_entry(objm2_kvs_table_t table, uint32_t key, objm2_kvs_status_t *status) { objm2_kvs_entry this_entry; this_entry = _kvs_find_entry(table, key, status); if (this_entry != NULL) { if (this_entry->ref_count > 1) { this_entry->ref_count--; if ((this_entry->ref_count == 1) && (this_entry->marked_for_removal)) { objm2_kvs_remove_entry(table, key, status); } // end if } // end if } // end if return; } // end objm2_kvs_release_entry // --------------------------------------------------------------------------- // function: objm2_kvs_remove_entry(table, key, status) // --------------------------------------------------------------------------- // // Marks the entry stored in
for as removed. An entry which has // been marked as removed can no longer be retrieved and will be removed when // its reference count reaches zero. The status of the operation is passed // back in , unless NULL was passed in for . void objm2_kvs_remove_entry(objm2_kvs_table_t table, uint32_t key, objm2_kvs_status_t *status) { cardinal index; objm2_kvs_entry prev_entry, this_entry; objm2_kvs_table_s *this_table = (objm2_kvs_table_s *)table; // table must not be NULL if (table == NULL) { _kvs_set_status(status, OBJM2_KVS_STATUS_UNDEFINED); return; } // end if // key must not be zero if (key == 0) { _kvs_set_status(status, OBJM2_KVS_STATUS_INVALID_KEY); return; } // end if // calculate the bucket index for key index = key % this_table->bucket_count; if /* bucket is empty */ (this_table->bucket[index] == NULL) { // entry not found // set status _kvs_set_status(status, OBJM2_KVS_STATUS_ENTRY_NOT_FOUND); } else /* bucket not empty */ { // starting point this_entry = this_table->bucket[index]; prev_entry = this_entry; // move to next entry until key matches or last entry is reached while ((this_entry->key != key) && (this_entry->next != NULL)) { prev_entry = this_entry; this_entry = this_entry->next; } // end while if /* key matched */ (this_entry->key == key) { // only remove if reference count is 1 (or less, just in case) if (this_entry->ref_count <= 1) { // if chached, remove the entry from the cache if (this_table->last_retrieved_entry == this_entry) this_table->last_retrieved_entry = NULL; // remove the entry from the bucket if /* first entry */ (this_table->bucket[index] == this_entry) { // link bucket root to successor this_table->bucket[index] = this_entry->next; } else /* not first entry */ { // link predecessor to successor prev_entry->next = this_entry->next; } // end if // deallocate the entry _kvs_dispose_entry(this_entry, status); // update the entry counter this_table->entry_count--; } else /* reference count > 1 */ { // don't remove the entry yet, mark it for removal this_entry->marked_for_removal = true; // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); } // end if } else /* key did not match any key in the bucket */ { // entry not found // set status _kvs_set_status(status, OBJM2_KVS_STATUS_ENTRY_NOT_FOUND); } // end if } // end if return; } // end objm2_kvs_remove_entry // --------------------------------------------------------------------------- // function: objm2_kvs_number_of_buckets(table) // --------------------------------------------------------------------------- // // Returns the number of buckets of KVS table
, returns zero if NULL // is passed in for
. cardinal objm2_kvs_number_of_buckets(objm2_kvs_table_t table) { objm2_kvs_table_s *this_table = (objm2_kvs_table_s *)table; if (table != NULL) return this_table->bucket_count; else return 0; } // end objm2_kvs_number_of_buckets // --------------------------------------------------------------------------- // function: objm2_kvs_number_of_entries(table) // --------------------------------------------------------------------------- // // Returns the number of entries stored in KVS table
, returns zero // if NULL is passed in for
. cardinal objm2_kvs_number_of_entries(objm2_kvs_table_t table) { objm2_kvs_table_s *this_table = (objm2_kvs_table_s *)table; if (table != NULL) return this_table->entry_count; else return 0; } // end objm2_kvs_number_of_entries // --------------------------------------------------------------------------- // function: objm2_kvs_dispose_table(table, status) // --------------------------------------------------------------------------- // // Disposes of KVS table object
, deallocating all its entries. The // table and its entries are disposed of regardless of any references held to // any values stored in the table. The status of the operation is passed back // in , unless NULL was passed in for . void objm2_kvs_dispose_table(objm2_kvs_table_t table, objm2_kvs_status_t *status) { cardinal index; objm2_kvs_entry prev_entry, this_entry; objm2_kvs_table_s *this_table = (objm2_kvs_table_s *)table; if (table == NULL) { _kvs_set_status(status, OBJM2_KVS_STATUS_UNDEFINED); return; } // end if if (this_table->entry_count != 0) { for (index = 0; index < this_table->entry_count; index++) { this_entry = this_table->bucket[index]; while (this_entry != NULL) { prev_entry = this_entry; this_entry = this_entry->next; _kvs_dispose_entry(prev_entry, NULL); } // end while this_table->bucket[index] = NULL; } // end for } // end if // dispose table base OBJM2_DEALLOCATE(this_table); // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); return; } // end objm2_kvs_dispose_table // =========================================================================== // P R I V A T E F U N C T I O N I M P L E M E N T A T I O N S // =========================================================================== // --------------------------------------------------------------------------- // private function: _kvs_find_entry(table, key, status) // --------------------------------------------------------------------------- // // If an entry for exists in
then the function returns a pointer // to the entry, otherwise it returns NULL. If the entry is found, then it // will be cached within the table, and a subsequent request to find the same // same entry will then return the cached entry pointer, which is slightly // faster than a lookup of a non-cached entry. The reference count of the // entry is not incremented by this function. The status of the operation // is passed back in , unless NULL was passed in for . static objm2_kvs_entry _kvs_find_entry(objm2_kvs_table_t table, uint32_t key, objm2_kvs_status_t *status) { cardinal index; objm2_kvs_entry this_entry; objm2_kvs_table_s *this_table = (objm2_kvs_table_s *)table; // table must not be NULL if (table == NULL) { _kvs_set_status(status, OBJM2_KVS_STATUS_UNDEFINED); return NULL; } // end if // check if the entry has been cached if ((this_table->last_retrieved_entry != NULL) && (key == this_table->last_retrieved_entry->key)) { // entry has been cached by previous lookup // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); // return pointer to the cached entry return this_table->last_retrieved_entry; } // end if // calculate the bucket index for key index = key % this_table->bucket_count; if /* bucket is empty */ (this_table->bucket[index] == NULL) { // set status _kvs_set_status(status, OBJM2_KVS_STATUS_ENTRY_NOT_FOUND); // return null, indicating entry not found return NULL; } else /* bucket not empty */ { // first entry in this bucket is starting point this_entry = this_table->bucket[index]; // check every entry in this bucket for a key match while ((this_entry->key != key) && (this_entry->next != NULL)) this_entry = this_entry->next; // the entry is found if there was a key match if /* key matched */ (this_entry->key == key) { // cache the entry for faster subsequent lookup this_table->last_retrieved_entry = this_entry; // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); // return pointer to entry found return this_entry; } else /* key did not match */ { // entry not found // set status _kvs_set_status(status, OBJM2_KVS_STATUS_ENTRY_NOT_FOUND); // return null, indicating entry not found return NULL; } // end if } // end if } // end _kvs_find_entry // -------------------------------------------------------------------------- // private function: _kvs_new_entry(key, value, len, status) // -------------------------------------------------------------------------- // // Returns a newly allocated table entry with , and . The // operation will fail if zero is passed in for , or if NULL is passed // in for . If the operation could not be completed, then NULL is // returned. The status of the operation is passed back in , // unless NULL wass passed in for . static fmacro objm2_kvs_entry _kvs_new_entry(uint32_t key, void *value, cardinal len, objm2_kvs_status_t *status) { octet_t *source, *target; objm2_kvs_entry_s *new_entry; cardinal index, num_of_bytes; // key must not be zero if (key == 0) { _kvs_set_status(status, OBJM2_KVS_STATUS_INVALID_KEY); return NULL; } // end if // value must not be NULL if (value == NULL) { _kvs_set_status(status, OBJM2_KVS_STATUS_INVALID_DATA); return NULL; } // end if // if len is zero, count the number of bytes source = value; num_of_bytes = len; if (len == 0) { while (*source != 0) { num_of_bytes++; source++; } // end while } // end if // allocate storage for new entry new_entry = OBJM2_ALLOCATE(sizeof(objm2_kvs_entry_s)); // exit if allocation failed if (new_entry == NULL) { // set status _kvs_set_status(status, OBJM2_KVS_STATUS_UNABLE_TO_ALLOCATE); // return null, indicating failure return NULL; } // end if // allocate storage for the entry's value new_entry->value = OBJM2_ALLOCATE(num_of_bytes); // exit if allocation failed if (value == NULL) { // undo allocation for entry OBJM2_DEALLOCATE(new_entry); // set status _kvs_set_status(status, OBJM2_KVS_STATUS_UNABLE_TO_ALLOCATE); // return null, indicating failure return NULL; } // end if // initialise new_entry->next = NULL; new_entry->ref_count = 1; new_entry->marked_for_removal = false; // copy key new_entry->key = key; // set type information if (len == 0) new_entry->null_terminated_data = true; // C string else new_entry->null_terminated_data = false; // arbitrary data // copy value source = value; target = new_entry->value; for (index = 0; index <= num_of_bytes; index++) { *target = *source; target++; source++; } // end for // remember allocation size new_entry->size = num_of_bytes; // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); // return pointer to the new entry return new_entry; } // end _kvs_new_entry // --------------------------------------------------------------------------- // private function: _kvs_copy_value(entry, status) // --------------------------------------------------------------------------- // // Returns a pointer to a newly allocated copy of the value of entry. If the // value was stored as a C string, the copy will include all chars up to and // including the first ASCII NUL character, otherwise the copy will include // the number of bytes specified for the value when it was stored. If storage // could not be allocated for the copy, then NULL is returned. The status of // the operation is passed back in , unless NULL was passed. static fmacro void *_kvs_copy_value(objm2_kvs_entry entry, objm2_kvs_status_t *status) { cardinal index = 0; octet_t *source, *target; source = (octet_t *)entry->value; target = (octet_t *)OBJM2_ALLOCATE(entry->size); // exit if storage allocation for copy failed if (target == NULL) { // set status _kvs_set_status(status, OBJM2_KVS_STATUS_UNABLE_TO_ALLOCATE); // return null, indicating failure return NULL; } // otherwise set status to success else { _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); } // end if // if null-terminated, copy up to and including first null char if (entry->null_terminated_data) { while (source[index] != 0) { target[index] = source[index]; index++; } // end while target[index] = 0; } // otherwise copy size number of bytes else /* not null-terminated */ { for (index = 0; index <= entry->size; index++) target[index] = source[index]; } // end if // return pointer to copy return (void *)target; } // end _kvs_copy_value // --------------------------------------------------------------------------- // private function: _kvs_dispose_entry(entry, status) // --------------------------------------------------------------------------- // // Deallocates . Status passed back in unless NULL passed in. static fmacro void _kvs_dispose_entry(objm2_kvs_entry entry, objm2_kvs_status_t *status) { // don't try to free any null pointers for entry if (entry == NULL) { // set status _kvs_set_status(status, OBJM2_KVS_STATUS_INVALID_ENTRY); // exit return; } // end if // don't try to free any null pointers for value either, just in case if (entry->value != NULL) { // deallocate entry's value OBJM2_DEALLOCATE(entry->value); // set status _kvs_set_status(status, OBJM2_KVS_STATUS_SUCCESS); // don't exit here, the entry itself still needs to be deallocated } // end if // deallocate the entry itself OBJM2_DEALLOCATE(entry); return; } // end _kvs_dispose_entry // END OF FILE