diff --git a/Source/NSHashTable.m b/Source/NSHashTable.m new file mode 100644 index 000000000..52cda3b68 --- /dev/null +++ b/Source/NSHashTable.m @@ -0,0 +1,503 @@ +/* NSHashTable implementation for GNUStep. + * Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 12 23:54:09 EST 1994 + * Updated: Sat Feb 10 15:59:11 EST 1996 + * Serial: 96.02.10.01 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* These are to increase readabilty locally. */ +typedef unsigned int (*NSHT_hash_func_t)(NSHashTable *, const void *); +typedef BOOL (*NSHT_isEqual_func_t)(NSHashTable *, + const void *, + const void *); +typedef void (*NSHT_retain_func_t)(NSHashTable *, const void *); +typedef void (*NSHT_release_func_t)(NSHashTable *, void *); +typedef NSString *(*NSHT_describe_func_t)(NSHashTable *, const void *); + +/** Standard NSHashTable callbacks **/ + +const NSHashTableCallBacks NSIntHashCallBacks = +{ + (NSHT_hash_func_t) _NSLF_int_hash, + (NSHT_isEqual_func_t) _NSLF_int_is_equal, + (NSHT_retain_func_t) fn_null_function, + (NSHT_release_func_t) fn_null_function, + (NSHT_describe_Func_t) _NSLF_int_describe +}; + +const NSHashTableCallBacks NSNonOwnedPointerHashCallBacks = +{ + (NSHT_hash_func_t) _NSLF_void_p_hash, + (NSHT_isEqual_func_t) _NSLF_void_p_is_equal, + (NSHT_retain_func_t) fn_null_function, + (NSHT_release_func_t) fn_null_function, + (NSHT_describe_func_t) _NSLF_void_p_describe +}; + +const NSHashTableCallBacks NSNonRetainedObjectsHashCallBacks = +{ + (NSHT_hash_func_t) _NSLF_id_hash, + (NSHT_isEqual_func_t) _NSLF_id_is_equal, + (NSHT_retain_func_t) fn_null_function, + (NSHT_release_func_t) fn_null_function, + (NSHT_describe_func_t) _NSLF_id_describe +}; + +const NSHashTableCallBacks NSObjectsHashCallBacks = +{ + (NSHT_hash_func_t) _NSLF_id_hash, + (NSHT_isEqual_func_t) _NSLF_id_is_equal, + (NSHT_retain_func_t) _NSLF_id_retain, + (NSHT_release_func_t) _NSLF_id_object, + (NSHT_describe_func_t) _NSLF_id_describe +}; + +const NSHashTableCallBacks NSOwnedPointerHashCallBacks = +{ + (NSHT_hash_func_t) _NSLF_void_p_hash, + (NSHT_isEqual_func_t) _NSLF_void_p_is_equal, + (NSHT_retain_func_t) fn_null_function, + (NSHT_release_func_t) _NSLF_void_p_release, + (NSHT_describe_func_t) _NSLF_void_p_describe +}; + +const NSHashTableCallBacks NSPointerToStructHashCallBacks = +{ + (NSHT_hash_func_t) _NSLF_int_p_hash, + (NSHT_isEqual_func_t) _NSLF_int_p_is_equal, + (NSHT_retain_func_t) fn_null_function, + (NSHT_release_func_t) fn_null_function, + (NSHT_describe_func_t) _NSLF_int_p_describe +}; + +/** Macros **/ + +#define NSHT_ZONE(T) \ + ((NSZone *)((fn_hash_allocs((fn_hash_t *)(T))).user_data)) + +#define NSHT_CALLBACKS(T) \ + (*((NSHashTableCallBacks *)(__void_p__(fn_hash_extra((fn_hash_t *)(T)))))) + +#define NSHT_DESCRIBE(T, P) \ + NSHT_CALLBACKS((T)).describe((T), (P)) + +/** Dummy callbacks **/ + +size_t +_NSHT_hash(fn_generic_t element, void *table) +{ + return NSHT_CALLBACKS(table).hash((NSHashTable *)table, + __void_p__(element)); +} + +int +_NSHT_compare(fn_generic_t element1, + fn_generic_t element2, + void *table) +{ + return !(NSHT_CALLBACKS(table).isEqual((NSHashTable *)table, + __void_p__(element1), + __void_p__(element2))); +} + +int +_NSHT_is_equal(fn_generic_t element1, + fn_generic_t element2, + void *table) +{ + return NSHT_CALLBACKS(table).isEqual((NSHashTable *) table, + __void_p__(element1), + __void_p__(element2)); +} + +fn_generic_t +_NSHT_retain(fn_generic_t element, void *table) +{ + NSHT_CALLBACKS(table).retain((NSHashTable *)table, + __void_p__(element)); + return element; +} + +void +_NSHT_release(fn_generic_t element, void *table) +{ + NSHT_CALLBACKS(table).release(table, __void_p__(element)); + return; +} + +/* These are wrappers for getting at the real callbacks. */ +const fn_callbacks_t _NSHT_callbacks = +{ + _NSHT_hash, + _NSHT_compare, + _NSHT_is_equal, + _NSHT_retain, + _NSHT_release, + (fn_describe_func_t)fn_null_function, + 0 +}; + +/** Extra, extra **/ + +/* Make a copy of a hash table's callbacks. */ +fn_generic_t +_NSHT_extra_retain(fn_generic_t extra, void *table) +{ + /* Pick out the callbacks in EXTRA. */ + NSHashTableCallBacks *callBacks = (NSHashTableCallBacks *)__void_p__(extra); + + /* Find our zone. */ + NSZone *zone = NSHT_ZONE(table); + + /* A pointer to some new callbacks. */ + NSHashTableCallbacks *newCallBacks; + + /* Set aside space for our new callbacks in the right zone. */ + newCallBacks = (NSHashTableCallBacks *)NSZoneMalloc(zone, + sizeof(NSHashTableCallBacks)); + + /* Copy CALLBACKS into NEWCALLBACKS. */ + *newCallBacks = *callBacks; + + /* Stuff NEWCALLBACKS into EXTRA. */ + __void_p__(extra) = newCallBacks; + + /* Return our new EXTRA. */ + return extra; +} + +void +_NSHT_extra_release(fn_generic_t extra, void *table) +{ + void *ptr = __void_p__(extra); + NSZone *zone = NSHT_ZONE(table); + + if (ptr != NULL) + NSZoneFree(zone, ptr); + + return; +} + +/* The idea here is that these callbacks ensure that the + * NSHashTableCallbacks which are associated with a given NSHashTable + * remain so throughout the life of the table and its copies. */ +fn_callbacks_t _NSHT_extra_callbacks = +{ + (fn_hash_func_t) fn_generic_hash, + (fn_is_equal_func_t) fn_generic_is_equal, + (fn_compare_func_t) fn_generic_compare, + _NSHT_extra_retain, + _NSHT_extra_release, + (fn_describe_func_t) fn_null_function, + 0 +}; + +/**** Function Implementations ***********************************************/ + +/** Creating NSHashTables **/ + +NSHashTable * +NSCreateHashTableWithZone(NSHashTableCallBacks callBacks, + unsigned int capacity, + NSZone *zone) +{ + NSHashTable *table; + fn_callbacks_t callbacks; + fn_allocs_t allocs; + + /* These callbacks just look in the TABLE's extra and uses the + * callbacks there. See above for precise definitions. */ + callbacks = _NSHT_callbacks; + allocs = fn_allocs_for_zone(zone); + + /* Then we build the table. */ + table = fn_hash_with_allocs_with_callbacks(allocs, callbacks); + + if (table != NULL) + { + fn_generic_t extra; + + /* Resize TABLE to CAPACITY. */ + fn_hash_resize(table, capacity); + + /* Set aside space for the NSHashTableExtra. */ + __void_p__(extra) = &callBacks; + + /* Add EXTRA to TABLE. This takes care of everything for us. */ + fn_hash_set_extra_callbacks(table, _NSHT_extra_callbacks); + fn_hash_set_extra(table, extra); + } + + /* Wah-hoo! */ + return table; +} + +NSHashTable * +NSCreateHashTable(NSHashTableCallBacks callBacks, + unsigned int capacity) +{ + return NSCreateHashTableWithZone(callBacks, capacity, NULL); +} + +/** Copying **/ + +NSHashTable * +NSCopyHashTableWithZone(NSHashTable *table, NSZone *zone) +{ + fn_allocs_t allocs; + NSHashTable *new_table; + + /* Due to the wonders of modern Libfn technology, everything we care + * about is automagically transferred. */ + allocs = fn_allocs_for_zone(zone); + new_table = fn_hash_copy_with_allocs(table, allocs); + + return new_table; +} + +/** Destroying **/ + +void +NSFreeHashTable(NSHashTable *table) +{ + /* Due to the wonders of modern Libfn technology, everything we care + * about is automagically and safely destroyed. */ + fn_hash_dealloc(table); + return; +} + +/** Resetting **/ + +void +NSResetHashTable(NSHashTable *table) +{ + fn_hash_empty(table); + return; +} + +/** Comparing **/ + +BOOL +NSCompareHashTables(NSHashTable *table1, NSHashTable *table2) +{ + return (fn_hash_is_equal_to_hash(table1, table2) ? YES : NO); +} + +/** Counting **/ + +unsigned int +NSCountHashTable(NSHashTable *table) +{ + return (unsigned int) fn_hash_count(table); +} + +/** Retrieving **/ + +void * +NSHashGet(NSHashTable *table, const void *pointer) +{ + fn_generic_t element; + fn_generic_t member; + + /* Stuff POINTER into (the `void *' facet of) ELEMENT. */ + __void_p__(element) = pointer; + + /* Look up ELEMENT in TABLE. */ + member = fn_hash_element(table, element); + + /* Return the `void *' facet of MEMBER. */ + return __void_p__(member); +} + +NSArray * +NSAllHashTableObjects(NSHashTable *table) +{ + NSArray *array; + fn_generic_t *elements; + id *objects; + unsigned int count; + + /* FIXME: We should really be locking TABLE somehow, to insure + * the thread-safeness of this method. */ + + /* Get an array of the (generically-typed) elements of TABLE. */ + elements = fn_hash_all_elements(table); + + /* How many thing are in TABLE? */ + count = NSCountHashTable(table); + + /* Make enough room for our array of `id's together with a + * terminating `nil' that we add below. */ + objects = fn_calloc(fn_hash_allocs(table), count + 1, sizeof(id)); + + /* Step through the generic array and copy the `id' facet of each + * into the corresponding member of the objects array. Remember + * that this function is only suppossed to be called when TABLE + * contains objects. Otherwise, everything goes to hell remarkably + * quickly. */ + for (i = 0; i < count; ++i) + objects[i] = __id__(elements[i]); + + /* `nil' terminate OBJECTS. */ + objects[i] = nil; + + /* Build the NSArray to return. */ + array = [[NSArray alloc] initWithObjects:objects count:count]; + + /* Free up all the space we allocated here. */ + fn_free(fn_hash_allocs(table), elements); + fn_free(fn_hash_allocs(table), objects); + + /* FIXME: Should ARRAY returned be `autorelease'd? */ + return [array autorelease]; +} + +/** Enumerating **/ + +NSHashEnumerator +NSEnumerateHashTable(NSHashTable *table) +{ + return fn_hash_enumerator(table); +} + +void * +NSNextHashEnumeratorItem(NSHashEnumerator *enumerator) +{ + fn_generic_t element; + + /* Grab the next element. */ + fn_hash_enumerator_next_element(enumerator, &element); + + /* Return ELEMENT's `void *' facet. */ + return __void_p__(element); +} + +/** Adding **/ + +void +NSHashInsert(NSHashTable *table, const void *pointer) +{ + fn_generic_t element; + + /* Stuff POINTER into ELEMENT. */ + __void_p__(element) = (void *)pointer; + + /* Place ELEMENT in TABLE. */ + fn_hash_add_element(table, element); + + return; +} + +void +NSHashInsertKnownAbsent(NSHashTable *table, const void *pointer) +{ + fn_generic_t element; + + __void_p__(element) = pointer; + + if (fn_hash_contains_element(table, element)) + { + /* FIXME: I should make this give the user/programmer more + * information. Not difficult to do, just something for a later + * date. */ + [NSException raise:NSInvalidArgumentException + format:@"Attempted reinsertion of \"%@\" into a hash table.", + NSHT_DESCRIBE(table, pointer)]; + } + else + { + fn_hash_add_element_known_absent(table, element); + } + + return; +} + +void * +NSHashInsertIfAbsent(NSHashTable *table, const void *pointer) +{ + fn_generic_t element; + + /* Stuff POINTER into ELEMENT. */ + __void_p__(element) = (void *)pointer; + + /* Place ELEMENT in TABLE. */ + element = fn_hash_add_element_if_absent(table, element); + + /* Return the `void *' facet of ELEMENT. */ + return __void_p__(element); +} + +/** Removing **/ + +void +NSHashRemove(NSHashTable *table, const void *pointer) +{ + fn_generic_t element; + + /* Stuff POINTER into ELEMENT. */ + __void_p__(element) = pointer; + + /* Remove ELEMENT from TABLE. */ + fn_hash_remove_element(table, element); + + return; +} + +/** Describing **/ + +/* FIXME: Make this nicer. I don't know what is desired here, though. + * If somebody has a clear idea of what this string should look like, + * please tell me, and I'll make it happen. */ +NSString * +NSStringFromHashTable(NSHashTable *table) +{ + NSString *string; + NSHashEnumerator enumerator; + void *pointer; + + /* This will be our string. */ + string = [NSMutableString string]; + + /* Get an enumerator for TABLE. */ + enumerator = NSEnumerateHashTable(table); + + /* Iterate over the elements of TABLE, appending the description of + * each to the mutable string STRING. */ + while ((pointer = NSNextHashEnumeratorItem(&enumerator)) != NULL) + [string appendFormat:@"%@;", NSHT_DESCRIBE(table, pointer)]; + + /* Note that this string'll need to be `retain'ed. */ + /* FIXME: Should I `autorelease' STRING? I think so. */ + return [string autorelease]; +} + diff --git a/Source/NSMapTable.m b/Source/NSMapTable.m new file mode 100644 index 000000000..5190f89a8 --- /dev/null +++ b/Source/NSMapTable.m @@ -0,0 +1,612 @@ +/* NSMapTable implementation for GNUStep. + * Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 12 23:59:57 EST 1994 + * Updated: Sat Feb 10 16:00:25 EST 1996 + * Serial: 96.02.10.01 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* This is for keeping track of information... */ +typedef struct _NSMT_extra NSMT_extra_t; + +struct _NSMT_extra +{ + NSMapTableKeyCallBacks keyCallBacks; + NSMapTableValueCallBacks valueCallBacks; +}; + +/* These are to increase readabilty locally. */ +typedef unsigned int (*NSMT_hash_func_t)(NSMapTable *, const void *); +typedef BOOL (*NSMT_is_equal_func_t)(NSMapTable *, const void *, + const void *); +typedef void (*NSMT_retain_func_t)(NSMapTable *, const void *); +typedef void (*NSMT_release_func_t)(NSMapTable *, void *); +typedef NSString *(*NSMT_describe_func_t)(NSMapTable *, const void *); + +const NSMapTableKeyCallBacks NSIntMapKeyCallBacks = +{ + (NSMT_hash_func_t) _NS_int_hash, + (NSMT_is_equal_func_t) _NS_int_is_equal, + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) fn_null_function, + (NSMT_describe_func_t) _NS_int_describe, + (const void *) 0 +}; + +const NSMapTableKeyCallBacks NSNonOwnedPointerMapKeyCallBacks = +{ + (NSMT_hash_func_t) _NS_void_p_hash, + (NSMT_is_equal_func_t) _NS_void_p_is_equal, + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) fn_null_function, + (NSMT_describe_func_t) _NS_void_p_describe, + (const void *) NULL +}; + +const NSMapTableKeyCallBacks NSNonOwnedPointerOrNullMapKeyCallBacks = +{ + (NSMT_hash_func_t) _NS_void_p_hash, + (NSMT_is_equal_func_t) _NS_void_p_is_equal, + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) fn_null_function, + (NSMT_describe_func_t) _NS_void_p_describe, + /* FIXME: Oh my. Is this really ok? I did it in a moment of + * weakness. A fit of madness, I say! And if this is wrong, what + * *should* it be?!? */ + (const void *) -1 +}; + +const NSMapTableKeyCallBacks NSNonRetainedObjectMapKeyCallBacks = +{ + (NSMT_hash_func_t) _NS_id_hash, + (NSMT_is_equal_func_t) _NS_id_is_equal, + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) fn_null_function, + (NSMT_describe_func_t) _NS_id_describe, + (const void *) NULL +}; + +const NSMapTableKeyCallBacks NSObjectMapKeyCallBacks = +{ + (NSMT_hash_func_t) _NS_id_hash, + (NSMT_is_equal_func_t) _NS_id_is_equal, + (NSMT_retain_func_t) _NS_id_retain, + (NSMT_release_func_t) _NS_id_release, + (NSMT_describe_func_t) _NS_id_describe, + (const void *) NULL +}; + +const NSMapTableKeyCallBacks NSOwnedPointerMapKeyCallBacks = +{ + (NSMT_hash_func_t) _NS_void_p_hash, + (NSMT_is_equal_func_t) _NS_void_p_is_equal, + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) _NS_void_p_release, + (NSMT_describe_func_t) _NS_void_p_describe, + (const void *) NULL +}; + +const NSMapTableValueCallBacks NSIntMapValueCallBacks = +{ + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) fn_null_function, + (NSMT_describe_func_t) _NS_int_describe +}; + +const NSMapTableValueCallBacks NSNonOwnedPointerMapValueCallBacks = +{ + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) fn_null_function, + (NSMT_describe_func_t) _NS_void_p_describe +}; + +const NSMapTableValueCallBacks NSObjectMapValueCallBacks = +{ + (NSMT_retain_func_t) _NS_id_retain, + (NSMT_release_func_t) _NS_id_release, + (NSMT_describe_func_t) _NS_id_describe +}; + +const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks = +{ + (NSMT_retain_func_t) fn_null_function, + (NSMT_release_func_t) _NS_void_p_release, + (NSMT_describe_func_t) _NS_void_p_describe +}; + +/** Macros **/ + +#define NSMT_ZONE(T) \ + ((NSZone *)((fn_map_allocs((fn_map_t *)(T))).user_data)) + +#define NSMT_EXTRA(T) \ + ((NSMT_extra_t *)(__void_p__(fn_map_extra((fn_map_t *)(T))))) + +#define NSMT_KEY_CALLBACKS(T) \ + ((NSMT_EXTRA((T)))->keyCallBacks) + +#define NSMT_VALUE_CALLBACKS(T) \ + ((NSMT_EXTRA((T)))->valueCallBacks) + +#define NSMT_DESCRIBE_KEY(T, P) \ + NSMT_KEY_CALLBACKS((T)).describe((T), (P)) + +#define NSMT_DESCRIBE_VALUE(T, P) \ + NSMT_VALUE_CALLBACKS((T)).describe((T), (P)) + +/** Dummy callbacks **/ + +size_t +_NSMT_key_hash(fn_generic_t element, void *table) +{ + return NSMT_KEY_CALLBACKS(table).hash((NSMapTable *)table, + __void_p__(element)); +} + +int +_NSMT_key_compare(fn_generic_t element1, + fn_generic_t element2, + void *table) +{ + return !(NSMT_KEY_CALLBACKS(table).isEqual((NSMapTable *)table, + __void_p__(element1), + __void_p__(element2))); +} + +int +_NSMT_key_is_equal(fn_generic_t element1, + fn_generic_t element2, + void *table) +{ + return NSMT_KEY_CALLBACKS(table).isEqual((NSMapTable *) table, + __void_p__(element1), + __void_p__(element2)); +} + +fn_generic_t +_NSMT_key_retain(fn_generic_t element, void *table) +{ + NSMT_KEY_CALLBACKS(table).retain((NSMapTable *)table, + __void_p__(element)); + return element; +} + +void +_NSMT_key_release(fn_generic_t element, void *table) +{ + NSMT_KEY_CALLBACKS(table).release(table, __void_p__(element)); + return; +} + +fn_generic_t +_NSMT_value_retain(fn_generic_t element, void *table) +{ + NSMT_VALUE_CALLBACKS(table).retain((NSMapTable *)table, + __void_p__(element)); + return element; +} + +void +_NSMT_value_release(fn_generic_t element, void *table) +{ + NSMT_VALUE_CALLBACKS(table).release(table, __void_p__(element)); + return; +} + +/* These are wrappers for getting at the real callbacks. */ +fn_callbacks_t _NSMT_key_callbacks = +{ + _NSMT_key_hash, + _NSMT_key_compare, + _NSMT_key_is_equal, + _NSMT_key_retain, + _NSMT_key_release, + (fn_describe_func_t)fn_null_function, + 0 +}; + +fn_callbacks_t +_NSMT_callbacks_for_key_callbacks(NSMapTableKeyCallBacks keyCallBacks) +{ + fn_callbacks_t cb = _NSMT_key_callbacks; + + __void_p__(cb.not_an_item_marker) = (void *)(keyCallBacks.notAKeyMarker); + + return callbacks; +} + +fn_callbacks_t _NSMT_value_callbacks = +{ + (fn_hash_func_t) fn_generic_hash, + (fn_compare_func_t) fn_generic_compare, + (fn_is_equal_func_t) fn_generic_is_equal, + _NSMT_value_retain, + _NSMT_value_release, + (fn_describe_func_t)fn_null_function, + 0 +}; + +/** Extra, extra **/ + +/* Make a copy of a hash table's callbacks. */ +fn_generic_t +_NSMT_extra_retain(fn_generic_t g, void *table) +{ + /* A pointer to some space for new callbacks. */ + NSMT_extra_t *new_extra; + + /* Set aside space for our new callbacks in the right zone. */ + new_extra = (NSMT_extra_t *)NSZoneMalloc(NSMT_ZONE(table), + sizeof(NSMT_extra_t)); + + /* Copy the old callbacks into NEW_EXTRA. */ + *new_extra = *((NSMT_extra_t *)(__void_p__(g))) + + /* Stuff NEW_EXTRA into G. */ + __void_p__(g) = new_extra; + + /* Return our new EXTRA. */ + return g; +} + +void +_NSMT_extra_release(fn_generic_t extra, void *table) +{ + void *ptr = __void_p__(extra); + NSZone *zone = NSMT_ZONE(table); + + if (ptr != NULL) + NSZoneFree(zone, ptr); + + return; +} + +/* The idea here is that these callbacks ensure that the + * NSMapTableCallbacks which are associated with a given NSMapTable + * remain so throughout the life of the table and its copies. */ +fn_callbacks_t _NSMT_extra_callbacks = +{ + (fn_hash_func_t) fn_generic_hash, + (fn_is_equal_func_t) fn_generic_is_equal, + (fn_compare_func_t) fn_generic_compare, + _NSMT_extra_retain, + _NSMT_extra_release, + (fn_describe_func_t) fn_null_function, + 0 +}; + +/**** Function Implementations ****/ + +/** Creating an NSMapTable **/ + +NSMapTable * +NSCreateMapTableWithZone(NSMapTableKeyCallBacks keyCallBacks, + NSMapTableValueCallBacks valueCallBacks, + unsigned capacity, + NSZone *zone) +{ + NSMapTable *table; + fn_callbacks_t key_callbacks, value_callbacks; + fn_allocs_t alloc; + + /* Transform the callbacks we were given. */ + key_callbacks = _NSMT_callbacks_for_key_callbacks(keyCallBacks); + value_callbacks = _NSMT_value_callbacks; + + /* Get some useful allocs. */ + alloc = fn_allocs_for_zone(zone); + + /* Create a map table. */ + table = fn_map_with_allocs_with_callbacks(allocs, key_callbacks, + value_callbacks); + + /* Adjust the capacity of TABLE. */ + fn_map_resize(table, capacity); + + if (table != NULL) + { + NSMapTableExtras *extras; + + /* Set aside space for the NSMapTableExtras. */ + extras = _NSNewMapTableExtrasWithZone(zone); + extras->keyCallBacks = keyCallBacks; + extras->valueCallBacks = valueCallBacks; + + table->extras = extras; + } + + return table; +} + +NSMapTable * +NSCreateMapTable(NSMapTableKeyCallBacks keyCallBacks, + NSMapTableValueCallBacks valueCallBacks, + unsigned int capacity) +{ + return NSCreateMapTableWithZone(keyCallBacks, + valueCallBacks, + capacity, + NULL); +} + +/* FIXME: CODE THIS! */ +NSMapTable * +NSCopyMapTableWithZone(NSMapTable *table, NSZone *zone) +{ + fn_allocs_t allocs; + NSMapTable *new_table; + + allocs = fn_allocs_for_zone(zone); + new_table = fn_map_copy_with_allocs(table, alloc); + + return new_table; +} + +/** Freeing an NSMapTable **/ + +void +NSFreeMapTable(NSMapTable *table) +{ + fn_map_dealloc(table); + return; +} + +void +NSResetMapTable(NSMapTable *table) +{ + fn_map_empty(table); + return; +} + +/** Comparing two NSMapTables **/ + +BOOL +NSCompareMapTables(NSMapTable *table1, NSMapTable *table2) +{ + return fn_map_is_equal_map(table1, table2) ? YES : NO; +} + +/** Getting the number of items in an NSMapTable **/ + +unsigned int +NSCountMapTable(NSMapTable *table) +{ + return (unsigned int) fn_map_count(table); +} + +/** Retrieving items from an NSMapTable **/ + +BOOL +NSMapMember(NSMapTable *table, const void *key, + void **originalKey, void **value) +{ + fn_generic_t k, ok, v; + int i; + + /* Stuff KEY into K. */ + __void_p__(k) = key; + + /* Check for K in TABLE. */ + i = fn_map_key_and_value(table, k, &ok, &v); + + /* Put the `void *' facet of OK and V into ORIGINALKEY and VALUE. */ + if (originalKey != NULL) + *originalKey = __void_p__(ok); + if (value != NULL) + *value = __void_p__(v); + + /* Indicate our state of success. */ + return i ? YES : NO; +} + +void * +NSMapGet(NSMapTable *table, const void *key) +{ + return fn_map_value(table, key); +} + +NSMapEnumerator +NSEnumerateMapTable(NSMapTable *table) +{ + return fn_map_enumerator(table); +} + +BOOL +NSNextMapEnumeratorPair(NSMapEnumerator *enumerator, + void **key, void **value) +{ + fn_generic_t k, v; + int i; + + /* Get the next pair. */ + i = fn_map_enumerator_next_key_and_value(enumerator, &k, &v); + + /* Put the `void *' facet of K and V into KEY and VALUE. */ + *key = __void_p__(k); + *value = __void_p__(v); + + /* Indicate our success or failure. */ + return i ? YES : NO; +} + +NSArray * +NSAllMapTableKeys(NSMapTable *table) +{ + NSArray *array; + fn_generic_t *keys; + id *objects; + unsigned int count; + + count = fn_map_count(table); + keys = fn_map_all_keys(table); + objects = fn_calloc(fn_set_allocs(table), count + 1, sizeof(id)); + + for (i = 0; i < count; ++i) + objects[i] = __id__(keys[i]); + + objects[i] = nil; + + array = [[NSArray alloc] initWithObjects:objects count:count]; + + fn_free(fn_map_allocs(table), keys); + fn_free(fn_map_allocs(table), objects); + + /* FIXME: Should ARRAY returned be `autorelease'd? */ + return [array autorelease]; +} + +NSArray * +NSAllMapTableValues(NSMapTable *table) +{ + NSArray *array; + fn_generic_t *values; + id *objects; + unsigned int count; + + count = fn_map_count(table); + values = fn_map_all_values(table); + objects = fn_calloc(fn_set_allocs(table), count + 1, sizeof(id)); + + for (i = 0; i < count; ++i) + objects[i] = __id__(values[i]); + + objects[i] = nil; + + array = [[NSArray alloc] initWithObjects:objects count:count]; + + fn_free(fn_map_allocs(table), keys); + fn_free(fn_map_allocs(table), objects); + + /* FIXME: Should ARRAY returned be `autorelease'd? */ + return [array autorelease]; +} + +/** Adding items to an NSMapTable **/ + +void +NSMapInsert(NSMapTable *table, const void *key, const void *value) +{ + fn_generic_t k, v; + + /* Stuff KEY and VALUE into K and V. */ + __void_p__(k) = key; + __void_p__(v) = value; + + /* Put K -> V into TABLE. */ + fn_map_at_key_put_value(table, k, v); + + return; +} + +void * +NSMapInsertIfAbsent(NSMapTable *table, const void *key, const void *value) +{ + fn_generic_t k, v, m; + + /* Stuff KEY and VALUE into K and V. */ + __void_p__(k) = key; + __void_p__(v) = value; + + /* Put K -> V into TABLE. */ + m = fn_map_at_key_put_value_if_absent(table, k, v); + + /* Return the `void *' facet of M. */ + return __void_p__(m); +} + +void +NSMapInsertKnownAbsent(NSMapTable *table, const void *key, const void *value) +{ + fn_generic_t k, v; + + /* Stuff KEY and VALUE into K and V. */ + __void_p__(k) = key; + __void_p__(v) = value; + + /* Is the key already in the table? */ + if (fn_map_contains_key(table, k)) + { + /* Ooh. Bad. The docs say to raise an exception! */ + /* FIXME: I should make this much more informative. */ + [NSException raise:NSInvalidArgumentException + format:@"That key's already in the table."]; + } + else + { + /* Well, we know it's not there, so... */ + fn_map_at_key_put_value_known_absent(table, k, v); + } + + /* Yah-hoo! */ + return; +} + +/** Removing items from an NSMapTable **/ + +void +NSMapRemove(NSMapTable *table, const void *key) +{ + fn_map_remove_key(table, key); + return; +} + +/** Getting an NSString representation of an NSMapTable **/ + +NSString * +NSStringFromMapTable(NSMapTable *table) +{ + NSString *string; + NSMapEnumerator enumerator; + NSMapTableKeyCallBacks keyCallBacks; + NSMapTableValueCallBacks valueCallBacks; + void *key, *value; + + /* Get an empty mutable string. */ + string = [NSMutableString string]; + + /* Pull the NSMapTable...CallBacks out of the mess. */ + keyCallBacks = NSMT_KEY_CALLBACKS(table); + valueCallBacks = NSMT_VALUE_CALLBACKS(table); + + /* Get an enumerator for our table. */ + enumerator = NSEnumerateMapTable(table); + + /* Now, just step through the elements of the table, and add their + * descriptions to the string. */ + while (NSNextMapEnumeratorPair(&enumerator, &key, &value)) + [string appendFormat:@"%@ = %@;", (keyCallBacks.describe)(table, key), + (valueCallBacks.describe)(table, value)]; + + /* Note that this string'll need to be `retain'ed. */ + /* FIXME: Should I be `autorelease'ing it? */ + return [string autorelease]; +} diff --git a/Source/abort.c b/Source/abort.c new file mode 100644 index 000000000..dcab8e09a --- /dev/null +++ b/Source/abort.c @@ -0,0 +1,46 @@ +/* A hookable abort function. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 12:31:57 EST 1996 + * Updated: Sat Feb 10 12:31:57 EST 1996 + * Serial: 96.02.10.01 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +static void (*__objects_abort) (void) = NULL; + +/**** Function Implementations ***********************************************/ + +void +objects_abort (void) +{ + if (__objects_abort != NULL) + (__objects_abort) (); + else + abort (); + return; +} diff --git a/Source/allocs.c b/Source/allocs.c new file mode 100644 index 000000000..7552ccece --- /dev/null +++ b/Source/allocs.c @@ -0,0 +1,81 @@ +/* Modular memory management through structures. + * Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Thu Oct 13 23:45:49 EDT 1994 + * Updated: Sat Feb 10 15:19:32 EST 1996 + * Serial: 96.02.10.03 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +static objects_allocs_t __objects_allocs_standard = +{ + (objects_malloc_func_t) malloc, + (objects_calloc_func_t) calloc, + (objects_realloc_func_t) realloc, + (objects_free_func_t) free, + (void *) 0 +}; + +/**** Function Implementations ***********************************************/ + +objects_allocs_t +objects_allocs_standard (void) +{ + return __objects_allocs_standard; +} + +void * +objects_malloc (objects_allocs_t allocs, size_t s) +{ + return (*(allocs.malloc)) (s, allocs.user_data); +} + +void * +objects_calloc (objects_allocs_t allocs, size_t n, size_t s) +{ + return (*(allocs.calloc)) (n, s, allocs.user_data); +} + +void * +objects_realloc (objects_allocs_t allocs, void *p, size_t s) +{ + return (*(allocs.realloc)) (p, s, allocs.user_data); +} + +void +objects_free (objects_allocs_t allocs, void *p) +{ + (*(allocs.free)) (p, allocs.user_data); + return; +} + +size_t +objects_next_power_of_two (size_t beat) +{ + size_t start = 1; + while ((start <= beat) && (start <<= 1)); + return start; +} diff --git a/Source/array.c b/Source/array.c new file mode 100644 index 000000000..f13908b8d --- /dev/null +++ b/Source/array.c @@ -0,0 +1,904 @@ +/* A (pretty good) implementation of a sparse array. + * Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Thu Mar 2 02:28:50 EST 1994 + * Updated: Sat Feb 10 16:16:12 EST 1996 + * Serial: 96.02.10.02 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Background functions **/ + +size_t +_objects_array_fold_index (size_t index, size_t slot_count) +{ + return (slot_count ? (index % slot_count) : 0); +} + +size_t +_objects_array_internal_index (objects_array_t * array, size_t index) +{ + return _objects_array_fold_index (index, array->slot_count); +} + +objects_array_slot_t * +_objects_array_slot_for_index (objects_array_t * array, size_t index) +{ + return (array->slots + _objects_array_internal_index (array, index)); +} + +objects_array_bucket_t * +_objects_array_bucket_for_index (objects_array_t * array, size_t index) +{ + objects_array_slot_t *slot; + objects_array_bucket_t *bucket; + + /* First, we translate the index into a bucket index to find our + * candidate for the bucket. */ + slot = _objects_array_slot_for_index (array, index); + bucket = *slot; + + /* But we need to check to see whether this is really the bucket we + * wanted. */ + if (bucket != NULL && bucket->index == index) + /* Bucket `index' exists, and we've got it, so... */ + return bucket; + else + /* Either no bucket or some other bucket is where bucket `index' + * would be, if it existed. So... */ + return NULL; +} + +objects_array_bucket_t * +_objects_array_new_bucket (objects_array_t * array, size_t index, void *element) +{ + objects_array_bucket_t *bucket; + + bucket = (objects_array_bucket_t *) objects_malloc (objects_array_allocs (array), + sizeof (objects_array_bucket_t)); + if (bucket != NULL) + { + objects_retain (objects_array_element_callbacks (array), element, array); + bucket->index = index; + bucket->element = element; + } + return bucket; +} + +void +_objects_array_free_bucket (objects_array_t * array, objects_array_bucket_t * bucket) +{ + if (bucket != NULL) + { + objects_release (objects_array_element_callbacks (array), bucket->element, array); + objects_free (objects_array_allocs (array), bucket); + } + return; +} + +objects_array_slot_t * +_objects_array_new_slots (objects_array_t * array, size_t slot_count) +{ + return (objects_array_slot_t *) objects_calloc (objects_array_allocs (array), + slot_count, + sizeof (objects_array_slot_t)); +} + +void +_objects_array_free_slots (objects_array_t * array, objects_array_slot_t * slots) +{ + if (slots != NULL) + objects_free (objects_array_allocs (array), slots); + return; +} + +void +_objects_array_empty_slot (objects_array_t * array, objects_array_slot_t * slot) +{ + if (*slot != NULL) + { + /* Get rid of the bucket. */ + _objects_array_free_bucket (array, *slot); + + /* Mark the slot as empty. */ + *slot = NULL; + + /* Keep the element count accurate */ + --(array->element_count); + } + + /* And return. */ + return; +} + +void +_objects_array_insert_bucket (objects_array_t * array, objects_array_bucket_t * bucket) +{ + objects_array_slot_t *slot; + + slot = _objects_array_slot_for_index (array, bucket->index); + + /* We're adding a bucket, so the current set of sorted slots is now + * invalidated. */ + if (array->sorted_slots != NULL) + { + _objects_array_free_slots (array, array->sorted_slots); + array->sorted_slots = NULL; + } + + if ((*slot) == NULL) + { + /* There's nothing there, so we can put `bucket' there. */ + *slot = bucket; + + /* Increment the array's bucket counter. */ + ++(array->element_count); + return; + } + if ((*slot)->index == bucket->index) + { + /* There's a bucket there, and it has the same index as `bucket'. + * So we get rid of the old one, and put the new one in its + * place. */ + _objects_array_free_bucket (array, *slot); + *slot = bucket; + return; + } + else + { + /* Now we get to fiddle around with things to make the world a + * better place... */ + + size_t new_slot_count; + objects_array_slot_t *new_slots; /* This guy holds the buckets while we + * muck about with them. */ + size_t d; /* Just a counter */ + + /* FIXME: I *really* wish I had a way of generating + * statistically better initial values for this variable. So + * I'll run a few tests and see... And is there a better + * algorithm, e.g., a better collection of sizes in the sense + * that the likelyhood of fitting everything in earlier is + * high? Well, enough mumbling. */ + /* At any rate, we're guaranteed to need at least this many. */ + new_slot_count = array->element_count + 1; + + do + { + /* First we make a new pile of slots for the buckets. */ + new_slots = _objects_array_new_slots (array, new_slot_count); + + if (new_slots == NULL) + objects_abort (); + + /* Then we put the new bucket in the pile. */ + new_slots[_objects_array_fold_index (bucket->index, + new_slot_count)] = bucket; + + /* Now loop and try to place the others. Upon collision + * with a previously inserted bucket, try again with more + * `new_slots'. */ + for (d = 0; d < array->slot_count; ++d) + { + if (array->slots[d] != NULL) + { + size_t i; + + i = _objects_array_fold_index (array->slots[d]->index, + new_slot_count); + + if (new_slots[i] == NULL) + { + new_slots[i] = array->slots[d]; + } + else + { + /* A collision. Clean up and try again. */ + + /* Free the current set of new buckets. */ + _objects_array_free_slots (array, new_slots); + + /* Bump up the number of new buckets. */ + ++new_slot_count; + + /* Break out of the `for' loop. */ + break; + } + } + } + } + while (d < array->slot_count); + + if (array->slots != NULL) + _objects_array_free_slots (array, array->slots); + + array->slots = new_slots; + array->slot_count = new_slot_count; + ++(array->element_count); + + return; + } +} + +int +_objects_array_compare_slots (const objects_array_slot_t * slot1, + const objects_array_slot_t * slot2) +{ + if (slot1 == slot2) + return 0; + if (*slot1 == NULL) + return 1; + if (*slot2 == NULL) + return -1; + + if ((*slot1)->index < (*slot2)->index) + return -1; + else if ((*slot1)->index > (*slot2)->index) + return 1; + else + return 0; +} + +typedef int (*qsort_compare_func_t) (const void *, const void *); + +void +_objects_array_make_sorted_slots (objects_array_t * array) +{ + objects_array_slot_t *new_slots; + + /* If there're already some sorted slots, then they're valid, and + * we're done. */ + if (array->sorted_slots != NULL) + return; + + /* Make some new slots. */ + new_slots = _objects_array_new_slots (array, array->slot_count); + + /* Copy the pointers to buckets into the new slots. */ + memcpy (new_slots, array->slots, (array->slot_count + * sizeof (objects_array_slot_t))); + + /* Sort the new slots. */ + qsort (new_slots, array->slot_count, sizeof (objects_array_slot_t), + (qsort_compare_func_t) _objects_array_compare_slots); + + /* Put the newly sorted slots in the `sorted_slots' element of the + * array structure. */ + array->sorted_slots = new_slots; + + return; +} + +objects_array_bucket_t * +_objects_array_enumerator_next_bucket (objects_array_enumerator_t * enumerator) +{ + if (enumerator->is_sorted) + { + if (enumerator->is_ascending) + { + if (enumerator->array->sorted_slots == NULL) + return NULL; + + if (enumerator->index < enumerator->array->element_count) + { + objects_array_bucket_t *bucket; + + bucket = enumerator->array->sorted_slots[enumerator->index]; + ++(enumerator->index); + return bucket; + } + else + return NULL; + } + else + { + if (enumerator->array->sorted_slots == NULL) + return NULL; + + if (enumerator->index > 0) + { + objects_array_bucket_t *bucket; + + --(enumerator->index); + bucket = enumerator->array->sorted_slots[enumerator->index]; + return bucket; + } + else + return NULL; + } + } + else + { + objects_array_bucket_t *bucket; + + if (enumerator->array->slots == NULL) + return NULL; + + for (bucket = NULL; + (enumerator->index < enumerator->array->slot_count + && bucket == NULL); + ++(enumerator->index)) + { + bucket = enumerator->array->slots[enumerator->index]; + } + + return bucket; + } +} + +/** Statistics **/ + +size_t +objects_array_count (objects_array_t * array) +{ + return array->element_count; +} + +size_t +objects_array_capacity (objects_array_t * array) +{ + return array->slot_count; +} + +int +objects_array_check (objects_array_t * array) +{ + return 0; +} + +int +objects_array_is_empty (objects_array_t * array) +{ + return objects_array_count (array) != 0; +} + +/** Emptying **/ + +void +objects_array_empty (objects_array_t * array) +{ + size_t c; + + /* Just empty each slot out, one by one. */ + for (c = 0; c < array->slot_count; ++c) + _objects_array_empty_slot (array, array->slots + c); + + return; +} + +/** Creating **/ + +objects_array_t * +objects_array_alloc_with_allocs (objects_allocs_t allocs) +{ + objects_array_t *array; + + /* Get a new array. */ + array = _objects_array_alloc_with_allocs (allocs); + + return array; +} + +objects_array_t * +objects_array_alloc (void) +{ + return objects_array_alloc_with_allocs (objects_allocs_standard ()); +} + +objects_array_t * +objects_array_with_allocs (objects_allocs_t allocs) +{ + return objects_array_init (objects_array_alloc_with_allocs (allocs)); +} + +objects_array_t * +objects_array_with_allocs_with_callbacks (objects_allocs_t allocs, + objects_callbacks_t callbacks) +{ + return objects_array_init_with_callbacks (objects_array_alloc_with_allocs (allocs), + callbacks); +} + +objects_array_t * +objects_array_with_callbacks (objects_callbacks_t callbacks) +{ + return objects_array_init_with_callbacks (objects_array_alloc (), callbacks); +} + +objects_array_t * +objects_array_of_char_p (void) +{ + return objects_array_with_callbacks (objects_callbacks_for_char_p); +} + +objects_array_t * +objects_array_of_void_p (void) +{ + return objects_array_with_callbacks (objects_callbacks_for_void_p); +} + +objects_array_t * +objects_array_of_owned_void_p (void) +{ + return objects_array_with_callbacks (objects_callbacks_for_owned_void_p); +} + +objects_array_t * +objects_array_of_int (void) +{ + return objects_array_with_callbacks (objects_callbacks_for_int); +} + +objects_array_t * +objects_array_of_id (void) +{ + return objects_array_with_callbacks (objects_callbacks_for_id); +} + +/** Initializing **/ + +objects_array_t * +objects_array_init_with_callbacks (objects_array_t * array, objects_callbacks_t callbacks) +{ + if (array != NULL) + { + /* The default capacity is 15. */ + size_t capacity = 15; + + /* Record the element callbacks. */ + array->callbacks = objects_callbacks_standardize (callbacks); + + /* Initialize ARRAY's information. */ + array->element_count = 0; + array->slot_count = capacity + 1; + + /* Make some new slots. */ + array->slots = _objects_array_new_slots (array, capacity + 1); + + /* Get the sorted slots ready for later use. */ + array->sorted_slots = NULL; + } + + return array; +} + +objects_array_t * +objects_array_init (objects_array_t * array) +{ + return objects_array_init_with_callbacks (array, + objects_callbacks_standard()); +} + +objects_array_t * +objects_array_init_from_array (objects_array_t * array, objects_array_t * old_array) +{ + objects_array_enumerator_t enumerator; + size_t index; + void *element; + + /* Initialize ARRAY in the usual way. */ + objects_array_init_with_callbacks (array, + objects_array_element_callbacks (old_array)); + + /* Get an enumerator for OLD_ARRAY. */ + enumerator = objects_array_enumerator (old_array); + + /* Step through OLD_ARRAY's elements, putting them at the proper + * index in ARRAY. */ + while (objects_array_enumerator_next_index_and_element (&enumerator, + &index, &element)) + { + objects_array_at_index_put_element (array, index, element); + } + + return array; +} + +/** Destroying **/ + +void +objects_array_dealloc (objects_array_t * array) +{ + if (array != NULL) + { + /* Empty out ARRAY. */ + objects_array_empty (array); + + /* Free up its slots. */ + _objects_array_free_slots (array, array->slots); + + /* FIXME: What about ARRAY's sorted slots? */ + + /* Free up ARRAY itself. */ + _objects_array_dealloc (array); + } + + return; +} + +/** Searching **/ + +void * +objects_array_element_at_index (objects_array_t * array, size_t index) +{ + objects_array_bucket_t *bucket = _objects_array_bucket_for_index (array, index); + + if (bucket != NULL) + return bucket->element; + else + /* If `bucket' is NULL, then the requested index is unused. */ + /* There's no bucket, so... */ + return objects_array_not_an_element_marker (array); +} + +size_t +objects_array_index_of_element (objects_array_t * array, void *element) +{ + size_t i; + + for (i = 0; i < array->slot_count; ++i) + { + objects_array_bucket_t *bucket = array->slots[i]; + + if (bucket != NULL) + if (objects_is_equal (objects_array_element_callbacks (array), + bucket->element, + element, + array)) + return bucket->index; + } + + return i; +} + +int +objects_array_contains_element (objects_array_t * array, void *element) +{ + /* Note that this search is quite inefficient. */ + return objects_array_index_of_element (array, element) < (array->slot_count); +} + +void ** +objects_array_all_elements (objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (void **) objects_calloc (objects_array_allocs (array), + count + 1, + sizeof (void *)); + + enumerator = objects_array_enumerator (array); + + for (i = 0; i < count; ++i) + objects_array_enumerator_next_element (&enumerator, elements + i); + + elements[i] = objects_array_not_an_element_marker (array); + + /* We're done, so heave it back. */ + return elements; +} + +void ** +objects_array_all_elements_ascending (objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (void **) objects_calloc (objects_array_allocs (array), + count + 1, + sizeof (void *)); + + enumerator = objects_array_ascending_enumerator (array); + + for (i = 0; i < count; ++i) + objects_array_enumerator_next_element (&enumerator, elements + i); + + elements[i] = objects_array_not_an_element_marker (array); + + /* We're done, so heave it back. */ + return elements; +} + +void ** +objects_array_all_elements_descending (objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (void **) objects_calloc (objects_array_allocs (array), + count + 1, + sizeof (void *)); + + enumerator = objects_array_descending_enumerator (array); + + for (i = 0; i < count; ++i) + objects_array_enumerator_next_element (&enumerator, elements + i); + + elements[i] = objects_array_not_an_element_marker (array); + + /* We're done, so heave it back. */ + return elements; +} + +/** Removing **/ + +void +objects_array_remove_element_at_index (objects_array_t * array, size_t index) +{ + objects_array_bucket_t *bucket; + + /* Get the bucket that might be there. */ + bucket = _objects_array_bucket_for_index (array, index); + + /* If there's a bucket at the index, then we empty its slot out. */ + if (bucket != NULL) + _objects_array_empty_slot (array, _objects_array_slot_for_index (array, index)); + + /* Finally, we return. */ + return; +} + +void +objects_array_remove_element_known_present (objects_array_t * array, + void *element) +{ + objects_array_remove_element_at_index (array, + objects_array_index_of_element (array, + element)); + return; +} + +void +objects_array_remove_element (objects_array_t * array, void *element) +{ + if (objects_array_contains_element (array, element)) + objects_array_remove_element_known_present (array, element); + + return; +} + +/** Adding **/ + +void * +objects_array_at_index_put_element (objects_array_t * array, + size_t index, + void *element) +{ + objects_array_bucket_t *bucket; + + /* Clean out anything that's already there. */ + objects_array_remove_element_at_index (array, index); + + /* Make a bucket for our information. */ + bucket = _objects_array_new_bucket (array, index, element); + + /* Put our bucket in the array. */ + _objects_array_insert_bucket (array, bucket); + + return element; +} + +/** Enumerating **/ + +objects_array_enumerator_t +objects_array_ascending_enumerator (objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + + enumerator.array = array; + enumerator.is_sorted = 1; + enumerator.is_ascending = 1; + enumerator.index = 0; + + _objects_array_make_sorted_slots (array); + + return enumerator; +} + +objects_array_enumerator_t +objects_array_descending_enumerator (objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + + enumerator.array = array; + enumerator.is_sorted = 1; + enumerator.is_ascending = 0; + /* The `+ 1' is so that we have `0' as a known ending condition. + * See `_objects_array_enumerator_next_bucket()'. */ + enumerator.index = array->element_count + 1; + + _objects_array_make_sorted_slots (array); + + return enumerator; +} + +objects_array_enumerator_t +objects_array_enumerator (objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + + enumerator.array = array; + enumerator.is_sorted = 0; + enumerator.is_ascending = 0; + enumerator.index = 0; + + return enumerator; +} + +int +objects_array_enumerator_next_index_and_element (objects_array_enumerator_t * enumerator, + size_t * index, + void **element) +{ + objects_array_bucket_t *bucket; + + bucket = _objects_array_enumerator_next_bucket (enumerator); + + if (bucket != NULL) + { + if (element != NULL) + *element = bucket->element; + if (index != NULL) + *index = bucket->index; + return 1; + } + else + { + if (element != NULL) + *element = objects_array_not_an_element_marker (enumerator->array); + if (index != NULL) + *index = 0; + return 0; + } +} + +int +objects_array_enumerator_next_element (objects_array_enumerator_t * enumerator, + void **element) +{ + return objects_array_enumerator_next_index_and_element (enumerator, + NULL, + element); +} + +int +objects_array_enumerator_next_index (objects_array_enumerator_t * enumerator, + size_t * index) +{ + return objects_array_enumerator_next_index_and_element (enumerator, + index, + NULL); +} + +/** Comparing **/ + +int +objects_array_is_equal_to_array (objects_array_t * array1, objects_array_t * array2) +{ + size_t a, b; + void *m, *n; + objects_array_enumerator_t e, f; + + a = objects_array_count (array1); + b = objects_array_count (array2); + + if (a < b) + return (b - a); + if (a > b) + return (a - b); + + /* Get ascending enumerators for each of the two arrays. */ + e = objects_array_ascending_enumerator (array1); + e = objects_array_ascending_enumerator (array1); + + while (objects_array_enumerator_next_index_and_element (&e, &a, &m) + && objects_array_enumerator_next_index_and_element (&f, &b, &n)) + { + int c, d; + + if (a < b) + return (b - a); + if (a > b) + return (a - b); + + c = objects_compare (objects_array_element_callbacks (array1), m, n, array1); + if (c != 0) + return c; + + d = objects_compare (objects_array_element_callbacks (array2), n, m, array2); + if (d != 0) + return d; + } + + return 0; +} + +/** Mapping **/ + +objects_array_t * +objects_array_map_elements (objects_array_t * array, + void *(*fcn) (void *, void *), + void *user_data) +{ + /* FIXME: Code this. */ + return array; +} + +/** Miscellaneous **/ + +objects_hash_t * +objects_hash_init_from_array (objects_hash_t * hash, objects_array_t * array) +{ + objects_array_enumerator_t enumerator; + void *element; + + /* NOTE: If ARRAY contains multiple elements of the same equivalence + * class, it is indeterminate which will end up in HASH. This + * shouldn't matter, though. */ + enumerator = objects_array_enumerator (array); + + /* Just walk through ARRAY's elements and add them to HASH. */ + while (objects_array_enumerator_next_element (&enumerator, &element)) + objects_hash_add_element (hash, element); + + return hash; +} + +// objects_chash_t * +// objects_chash_init_from_array (objects_chash_t * chash, objects_array_t * array) +// { +// objects_array_enumerator_t enumerator; +// void *element; +// +// /* NOTE: If ARRAY contains multiple elements of the same equivalence +// * class, it is indeterminate which will end up in CHASH. This +// * shouldn't matter, though. */ +// enumerator = objects_array_enumerator (array); +// +// /* Just walk through ARRAY's elements and add them to CHASH. */ +// while (objects_array_enumerator_next_element (&enumerator, &element)) +// objects_chash_add_element (chash, element); +// +// return chash; +// } + diff --git a/Source/atoz.m b/Source/atoz.m new file mode 100644 index 000000000..18658c1f0 --- /dev/null +++ b/Source/atoz.m @@ -0,0 +1,77 @@ +/* Handling the interface between allocs and zones. + * Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Oct 15 10:34:02 EDT 1994 + * Updated: Sat Feb 10 15:20:41 EST 1996 + * Serial: 96.02.10.02 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Handling Libobjects Allocs **/ + +void * +_objects_zone_malloc(size_t s, void *z) +{ + return NSZoneMalloc((NSZone *)z, s); +} + +void * +_objects_zone_calloc(size_t n, size_t s, void *z) +{ + return NSZoneCalloc((NSZone *)z, n, s); +} + +void * +_objects_zone_realloc(void *p, size_t s, void *z) +{ + return NSZoneRealloc((NSZone *)z, p, s); +} + +void +_objects_zone_free(void *p, void *z) +{ + NSZoneFree((NSZone *)z, p); + return; +} + +objects_allocs_t +objects_allocs_for_zone(NSZone *zone) +{ + objects_allocs_t allocs = + { + _objects_zone_malloc, + _objects_zone_calloc, + _objects_zone_realloc, + _objects_zone_free, + zone + }; + + return allocs; +} + diff --git a/Source/callbacks-char_p.c b/Source/callbacks-char_p.c new file mode 100644 index 000000000..51d2145eb --- /dev/null +++ b/Source/callbacks-char_p.c @@ -0,0 +1,129 @@ +/* Callbacks for strings of `char'. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 22:04:38 EST 1996 + * Updated: Sun Feb 11 01:40:09 EST 1996 + * Serial: 96.02.11.05 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +objects_callbacks_t objects_callbacks_for_char_p = +{ + (objects_hash_func_t) objects_char_p_hash, + (objects_compare_func_t) objects_char_p_compare, + (objects_is_equal_func_t) objects_char_p_is_equal, + (objects_retain_func_t) objects_char_p_retain, + (objects_release_func_t) objects_char_p_release, + (objects_describe_func_t) objects_char_p_describe, + 0 +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_char_p_hash(void *cptr) +{ + register char *s = (char *) cptr; + register size_t h = 0; + register size_t c = 0; + + while (*s != '\0') + h ^= *(s++) << (c++); + + return h; +} + +int +objects_char_p_compare(void *cptr, void *dptr) +{ + register char *s = (char *) cptr; + register char *t = (char *) dptr; + + if (s == t) + { + return 0; + } + else + { + register char c; + register char d; + + while ((c = *(s++)) == (d = *(s++))) + if (c == '\0') + return 0; + + return (c - d); + } +} + +/* Determines whether or not CPTR is the same (`NUL'-terminated) + * character string as DPTR. Returns true if CPTR and DPTR are the same, + * and false otherwise. Note that we are performing no + * internationalization here. CPTR and DPTR are taken to be C strings + * in the default (seven or) eight bit character encoding. */ +int +objects_char_p_is_equal(register void *cptr, register void *dptr) +{ + register char *s = (char *) cptr; + register char *t = (char *) dptr; + + if (s == t) + { + return 1; + } + else + { + register char c; + register char d; + + while ((c = *(s++)) == (d = *(t++))) + if (c == '\0') + return 1; + + return 0; + } +} + +void * +objects_char_p_retain(void *cptr) +{ + return cptr; +} + +void +objects_char_p_release(void *cptr) +{ + return; +} + +void * +objects_char_p_describe(void *cptr) +{ + /* FIXME: Code this. But first, figure out what it should do, OK? */ + return 0; +} + diff --git a/Source/callbacks-id.m b/Source/callbacks-id.m new file mode 100644 index 000000000..7089afd5d --- /dev/null +++ b/Source/callbacks-id.m @@ -0,0 +1,93 @@ +/* Callbacks for the Objective-C object type. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 15:55:51 EST 1996 + * Updated: Sun Feb 11 01:42:20 EST 1996 + * Serial: 96.02.11.05 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +objects_callbacks_t objects_callbacks_for_id = +{ + (objects_hash_func_t) objects_id_hash, + (objects_compare_func_t) objects_id_compare, + (objects_is_equal_func_t) objects_id_is_equal, + (objects_retain_func_t) objects_id_retain, + (objects_release_func_t) objects_id_release, + (objects_describe_func_t) objects_id_describe, + 0 +}; + +/**** Function Implementations ***********************************************/ + +/* FIXME: It sure would be nice if we had a way of checking whether + * or not these objects responded to the messages we're sending them here. + * We need a way that is independent of whether we have GNUStep objects, + * NEXTSTEP objects, or GNU objects. We could certainly just use the + * same trick that the `respondsToSelector:' method uses, but I'd hoped + * that there was already a built-in call to do this sort of thing. */ + +size_t +objects_id_hash(void *obj) +{ + return (size_t)[(id)obj hash]; +} + +int +objects_id_compare(void *obj, void *jbo) +{ + return (int)[(id)obj compare:(id)jbo]; +} + +int +objects_id_is_equal(void *obj, void *jbo) +{ + return (int)[(id)obj isEqual:(id)jbo]; +} + +void * +objects_id_retain(void *obj) +{ + return [(id)obj retain]; +} + +void +objects_id_release(void *obj) +{ + [(id)obj release]; + return; +} + +void * +objects_id_describe(void *obj) +{ + /* FIXME: Harrumph. Make all of these describe functions live + * in harmony. Please. */ + return [(id)obj describe]; +} + diff --git a/Source/callbacks-int.c b/Source/callbacks-int.c new file mode 100644 index 000000000..b4bb6cc99 --- /dev/null +++ b/Source/callbacks-int.c @@ -0,0 +1,84 @@ +/* Callbacks for `int' (and smaller) things. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 15:55:51 EST 1996 + * Updated: Sun Feb 11 01:47:14 EST 1996 + * Serial: 96.02.11.03 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +objects_callbacks_t objects_callbacks_for_int = +{ + (objects_hash_func_t) objects_int_hash, + (objects_compare_func_t) objects_int_compare, + (objects_is_equal_func_t) objects_int_is_equal, + (objects_retain_func_t) objects_int_retain, + (objects_release_func_t) objects_int_release, + (objects_describe_func_t) objects_int_describe, + 0 +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_int_hash(void *i) +{ + return (size_t)((int)i); +} + +int +objects_int_compare(void *i, void *j) +{ + return ((int)i) - ((int)j); +} + +int +objects_int_is_equal(void *i, void *j) +{ + return ((int)i) == ((int)j); +} + +void * +objects_int_retain(void *i) +{ + return i; +} + +void +object_int_release(void *i) +{ + return; +} + +void * +objects_int_describe(void *i) +{ + /* FIXME: Code this. */ + return 0; +} + + diff --git a/Source/callbacks-int_p.c b/Source/callbacks-int_p.c new file mode 100644 index 000000000..205cda5a1 --- /dev/null +++ b/Source/callbacks-int_p.c @@ -0,0 +1,87 @@ +/* Callbacks for pointers to `int' and (maybe) structures whose first + * field is an `int'. Maybe. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 15:55:51 EST 1996 + * Updated: Sun Feb 11 01:49:55 EST 1996 + * Serial: 96.02.11.04 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +objects_callbacks_t objects_callbacks_for_int_p = +{ + (objects_hash_func_t) objects_int_p_hash, + (objects_compare_func_t) objects_int_p_compare, + (objects_is_equal_func_t) objects_int_p_is_equal, + (objects_retain_func_t) objects_int_p_retain, + (objects_release_func_t) objects_int_p_release, + (objects_describe_func_t) objects_int_p_describe, + 0 +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_int_p_hash(void *iptr) +{ + return (size_t)(*((int *)iptr)); +} + +/* FIXME: Are these next two correct? These seem rather useless to me. */ + +int +objects_int_p_compare(void *iptr, void *jptr) +{ + return *((int *)iptr) - *((int *)jptr); +} + +int +objects_int_p_is_equal(void *iptr, void *jptr) +{ + return *((int *)iptr) == *((int *)jptr); +} + +void * +objects_int_p_retain(void *iptr) +{ + return iptr; +} + +void +objects_int_p_release(void *iptr) +{ + return; +} + +void * +objects_int_p_describe(void *iptr) +{ + /* FIXME: Code this. */ + return 0; +} + + diff --git a/Source/callbacks-void_p.c b/Source/callbacks-void_p.c new file mode 100644 index 000000000..5f2576afc --- /dev/null +++ b/Source/callbacks-void_p.c @@ -0,0 +1,129 @@ +/* Callbacks for pointers to `void'. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 22:04:38 EST 1996 + * Updated: Sun Feb 11 01:09:34 EST 1996 + * Serial: 96.02.11.03 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +objects_callbacks_t objects_callbacks_for_void_p = +{ + (objects_hash_func_t) objects_void_p_hash, + (objects_compare_func_t) objects_void_p_compare, + (objects_is_equal_func_t) objects_void_p_is_equal, + (objects_retain_func_t) objects_void_p_retain, + (objects_release_func_t) objects_void_p_release, + 0 +}; + +objects_callbacks_t objects_callbacks_for_owned_void_p = +{ + (objects_hash_func_t) objects_owned_void_p_hash, + (objects_compare_func_t) objects_owned_void_p_compare, + (objects_is_equal_func_t) objects_owned_void_p_is_equal, + (objects_retain_func_t) objects_owned_void_p_retain, + (objects_release_func_t) objects_owned_void_p_release, + 0 +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_void_p_hash(void *cptr) +{ + return ((size_t) cptr)/4; +} + +int +objects_void_p_compare(void *cptr, void *dptr) +{ + if (cptr == dptr) + return 0; + else if (cptr < dptr) + return -1; + else /* if (cptr > dptr) */ + return 1; +} + +int +objects_void_p_is_equal(register void *cptr, register void *dptr) +{ + return (cptr == dptr); +} + +void * +objects_void_p_retain(void *cptr) +{ + return cptr; +} + +void +objects_void_p_release(void *cptr) +{ + return; +} + +size_t +objects_owned_void_p_hash(void *cptr) +{ + /* We divide by 4 because many machines align + * memory on word boundaries. */ + return ((size_t) cptr)/4; +} + +int +objects_owned_void_p_compare(void *cptr, void *dptr) +{ + if (cptr == dptr) + return 0; + else if (cptr < dptr) + return -1; + else /* if (cptr > dptr) */ + return 1; +} + +int +objects_owned_void_p_is_equal(register void *cptr, register void *dptr) +{ + return (cptr == dptr); +} + +void * +objects_owned_void_p_retain(void *cptr) +{ + return cptr; +} + +void +objects_owned_void_p_release(void *cptr) +{ + free(cptr); + return; +} + + diff --git a/Source/callbacks.c b/Source/callbacks.c new file mode 100644 index 000000000..8612a3773 --- /dev/null +++ b/Source/callbacks.c @@ -0,0 +1,141 @@ +/* Handling various types in a uniform manner. + * Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sun Oct 9 13:14:41 EDT 1994 + * Updated: Sun Feb 11 01:33:41 EST 1996 + * Serial: 96.02.10.07 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* WARNING: Don't change this unless you know what you're getting into! */ +static objects_callbacks_t ___objects_callbacks_standard = +{ + (objects_hash_func_t) objects_void_p_hash, + (objects_compare_func_t) objects_void_p_compare, + (objects_is_equal_func_t) objects_void_p_is_equal, + (objects_retain_func_t) objects_void_p_retain, + (objects_release_func_t) objects_void_p_release, + (objects_describe_func_t) objects_void_p_describe, + 0 +}; + +/**** Function Implementations ***********************************************/ + +/** Getting the standard callbacks **/ + +objects_callbacks_t +objects_callbacks_standard(void) +{ + return ___objects_callbacks_standard; +} + +/** Standardizing callbacks **/ + +objects_callbacks_t +objects_callbacks_standardize(objects_callbacks_t callbacks) +{ + if (callbacks.hash == 0) + callbacks.hash = objects_callbacks_standard().hash; + if (callbacks.compare == 0 && callbacks.is_equal == 0) + { + callbacks.compare = objects_callbacks_standard().compare; + callbacks.is_equal = objects_callbacks_standard().is_equal; + } + if (callbacks.retain == 0) + callbacks.retain = objects_callbacks_standard().retain; + if (callbacks.release == 0) + callbacks.release = objects_callbacks_standard().release; + + return callbacks; +} + +/** Using callbacks **/ + +size_t +objects_hash (objects_callbacks_t callbacks, void *thing, void *user_data) +{ + if (callbacks.hash != 0) + return callbacks.hash(thing, user_data); + else + return objects_callbacks_standard().hash(thing, user_data); +} + +int +objects_compare (objects_callbacks_t callbacks, + void *thing1, + void *thing2, + void *user_data) +{ + if (callbacks.compare != 0) + return callbacks.compare(thing1, thing2, user_data); + else if (callbacks.is_equal != 0) + return !(callbacks.is_equal(thing1, thing2, user_data)); + else + return objects_callbacks_standard().compare(thing1, thing2, user_data); +} + +int +objects_is_equal (objects_callbacks_t callbacks, + void *thing1, + void *thing2, + void *user_data) +{ + if (callbacks.is_equal != 0) + return callbacks.is_equal(thing1, thing2, user_data); + else if (callbacks.compare != 0) + return !(callbacks.compare(thing1, thing2, user_data)); + else + return objects_callbacks_standard().is_equal(thing1, thing2, user_data); +} + +void * +objects_retain (objects_callbacks_t callbacks, void *thing, void *user_data) +{ + if (callbacks.retain != 0) + return callbacks.retain(thing, user_data); + else + return objects_callbacks_standard().retain(thing, user_data); +} + +void +objects_release (objects_callbacks_t callbacks, void *thing, void *user_data) +{ + if (callbacks.release != 0) + callbacks.release(thing, user_data); + else + objects_callbacks_standard().release(thing, user_data); + return; +} + +void * +objects_describe(objects_callbacks_t callbacks, void *thing, void *user_data) +{ + if (callbacks.release != 0) + return callbacks.describe(thing, user_data); + else + return objects_callbacks_standard().describe(thing, user_data); +} + diff --git a/Source/data.c b/Source/data.c new file mode 100644 index 000000000..8a917dc11 --- /dev/null +++ b/Source/data.c @@ -0,0 +1,1026 @@ +/* A (pretty good) implementation of a self-contained data object, + * complete with checksum and encoding/decoding capability. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Fri Nov 24 21:46:14 EST 1995 + * Updated: Sat Feb 10 16:12:17 EST 1996 + * Serial: 96.02.10.07 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/**** Function Implementations ***********************************************/ + +/** Hashing **/ + +size_t +objects_data_hash (objects_data_t * data) +{ + /* FIXME: Code this. */ + return 0; +} + +/** Creating **/ + +objects_data_t * +objects_data_alloc (void) +{ + return objects_data_alloc_with_allocs (objects_allocs_standard ()); +} + +objects_data_t * +objects_data_alloc_with_allocs (objects_allocs_t allocs) +{ + objects_data_t *data; + + /* Make a new data structure. */ + data = _objects_data_alloc_with_allocs (allocs); + + return data; +} + +objects_data_t * +objects_data_new (void) +{ + return objects_data_new_with_allocs (objects_allocs_standard ()); +} + +objects_data_t * +objects_data_new_with_allocs (objects_allocs_t allocs) +{ + return objects_data_init (objects_data_alloc_with_allocs (allocs)); +} + +objects_data_t * +_objects_data_with_allocs_with_contents_of_file (objects_allocs_t allocs, + const char *file) +{ + return _objects_data_init_with_contents_of_file (objects_data_alloc_with_allocs (allocs), + file); +} + +objects_data_t * +_objects_data_with_contents_of_file (const char *file) +{ + return _objects_data_with_allocs_with_contents_of_file (objects_allocs_standard (), + file); +} + +objects_data_t * +objects_data_with_buffer_of_length (void *buffer, size_t length) +{ + return objects_data_with_allocs_with_buffer_of_length (objects_allocs_standard (), + buffer, length); +} + +objects_data_t * +objects_data_with_allocs_with_buffer_of_length (objects_allocs_t allocs, + void *buffer, + size_t length) +{ + return objects_data_init_with_buffer_of_length (objects_data_alloc_with_allocs (allocs), + buffer, length); +} + +/** Destroying **/ + +void +objects_data_dealloc (objects_data_t * data) +{ + if (data != NULL) + { + /* Free up DATA's buffer if we've used it. */ + if (data->buffer != NULL) + objects_free (objects_data_allocs (data), data->buffer); + + /* Free up DATA itself. */ + _objects_data_dealloc (data); + } + + return; +} + +/** Initializing **/ + +objects_data_t * +objects_data_init (objects_data_t * data) +{ + return objects_data_init_with_buffer_of_length (data, NULL, 0); +} + +objects_data_t * +_objects_data_init_with_contents_of_file (objects_data_t * data, const char *file) +{ + objects_data_t *new_data; + void *buffer; + FILE *f; + long int length; + long int d; + int c; + + f = fopen (file, "r"); + + if (f == NULL) /* We failed to open the file. */ + goto failure; + + /* Seek to the end of the file. */ + c = fseek (f, 0L, SEEK_END); + + if (c != 0) /* Something went wrong; though I + * don't know what. */ + goto failure; + + /* Determine the length of the file (having seeked to the end of the + * file) by calling ftell(). */ + length = ftell (f); + + if (length == -1) /* I can't imagine what could go + * wrong, but here we are. */ + goto failure; + + /* Set aside the space we'll need. */ + buffer = objects_malloc (objects_data_allocs (data), length); + + if (buffer == NULL) /* Out of memory, I guess. */ + goto failure; + + /* Rewind the file pointer to the beginning, preparing to read in + * the file. */ + c = fseek (f, 0L, SEEK_SET); + + if (c != 0) /* Oh, No. */ + goto failure; + + /* Update the change time. */ + _objects_data_set_change_time (data); + + /* Now we read FILE into BUFFER one (unsigned) byte at a time. + * FIXME: We should probably be more careful to check that we don't + * get an EOF. But what would this mean? That the file had been + * changed in the middle of all this. FIXME: So maybe we should + * think about locking the file? */ + for (d = 0; d < length; d++) + ((unsigned char *) buffer)[d] = (unsigned char) fgetc (f); + + /* success: */ + new_data = objects_data_init_with_buffer_of_length (data, buffer, length); + + /* Free up BUFFER, since we're done with it. */ + objects_free (objects_data_allocs (data), buffer); + + return new_data; + + /* Just in case the failure action needs to be changed. */ +failure: + return NULL; +} + +objects_data_t * +objects_data_init_with_buffer_of_length (objects_data_t * data, + void *buffer, + size_t length) +{ + if (data != NULL) + { + data->buffer = NULL; + data->length = 0; + data->capacity = 0; + + objects_data_set_buffer_of_length (data, buffer, length); + } + + return data; +} + +objects_data_t * +objects_data_init_with_subrange_of_data (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * old_data) +{ + if (data != NULL) + { + /* Make sure we don't step out of bounds. */ + location = MIN (location, old_data->length); + length = MIN (old_data->length - location, length); + + /* Copy over the contents. */ + objects_data_init_with_buffer_of_length (data, old_data->buffer + location, + old_data->length); + } + + return data; +} + +/** Statistics **/ + +size_t +objects_data_capacity (objects_data_t * data) +{ + /* Update the access time. */ + _objects_data_set_access_time (data); + + return data->capacity; +} + +/* Obtain DATA's length. */ +size_t +objects_data_length (objects_data_t * data) +{ + /* Update the access time. */ + _objects_data_set_access_time (data); + + return data->length; +} + +/* Obtain a read-only copy of DATA's buffer. */ +const void * +objects_data_buffer (objects_data_t * data) +{ + /* Update the access time. */ + _objects_data_set_access_time (data); + + return data->buffer; +} + +/* Obtain DATA's capacity through reference. */ +size_t +objects_data_get_capacity (objects_data_t * data, size_t * capacity) +{ + /* Update the access time. */ + _objects_data_set_access_time (data); + + if (capacity != NULL) + *capacity = data->capacity; + + return data->capacity; +} + +/* Obtain DATA's length through reference. */ +size_t +objects_data_get_length (objects_data_t * data, size_t * length) +{ + /* Update the access time. */ + _objects_data_set_access_time (data); + + if (length != NULL) + *length = data->length; + + return data->length; +} + +/* Copy DATA's buffer into BUFFER. It is assumed that BUFFER is large + * enough to contain DATA's buffer. */ +size_t +objects_data_get_buffer (objects_data_t * data, void *buffer) +{ + return objects_data_get_buffer_of_subrange (data, buffer, 0, data->length); +} + +/* Copy no more that LENGTH of DATA's buffer into BUFFER. Returns the + * amount actually copied. */ +size_t +objects_data_get_buffer_of_length (objects_data_t * data, void *buffer, size_t length) +{ + return objects_data_get_buffer_of_subrange (data, buffer, 0, length); +} + +/* Copy a subrange of DATA's buffer into BUFFER. As always, it is + * assumed that BUFFER is large enough to contain everything. We + * return the size of the data actually copied into BUFFER. */ +size_t +objects_data_get_buffer_of_subrange (objects_data_t * data, + void *buffer, + size_t location, + size_t length) +{ + size_t real_length; + + /* Update the access time. */ + _objects_data_set_access_time (data); + + /* Figure out how much we really can copy. */ + real_length = MIN (data->length - location, length); + + /* Copy over the data. */ + memmove (buffer, data->buffer + location, real_length); + + /* Tell how much we actually copied. */ + return real_length; +} + +size_t +objects_data_set_capacity (objects_data_t * data, size_t capacity) +{ + size_t cap; + + /* Update the change time. */ + _objects_data_set_change_time (data); + + /* Over shoot a little. */ + cap = objects_next_power_of_two (capacity); + + if (data->buffer == NULL) + data->buffer = objects_malloc (objects_data_allocs (data), cap); + else /* (data->buffer != NULL) */ + data->buffer = objects_realloc (objects_data_allocs (data), data->buffer, cap); + + /* FIXME: Check for failure of the allocs above. */ + + /* DATA needs to know that it can hold CAP's worth of stuff. */ + data->capacity = cap; + + /* Make sure that DATA's length is no greater than its capacity. */ + data->length = MIN (data->length, data->capacity); + + return cap; +} + +size_t +objects_data_increase_capacity (objects_data_t * data, size_t capacity) +{ + return objects_data_set_capacity (data, objects_data_capacity (data) + capacity); +} + +size_t +objects_data_decrease_capacity (objects_data_t * data, size_t capacity) +{ + size_t old_capacity = objects_data_capacity (data); + + return objects_data_set_capacity (data, old_capacity - MIN (capacity, + old_capacity)); +} + +size_t +objects_data_set_length (objects_data_t * data, size_t length) +{ + /* Update the change time. */ + _objects_data_set_change_time (data); + + /* The only thing we need to be careful of is that DATA's length is + * no greater than its capacity. */ + return data->length = MIN (length, objects_data_capacity (data)); +} + +size_t +objects_data_set_buffer_of_subrange (objects_data_t * data, + void *buffer, + size_t location, + size_t length) +{ + /* Arrange for DATA to have space for LENGTH amount of information. */ + objects_data_set_capacity (data, length); + + /* Copy the stuff in BUFFER over to DATA. */ + memmove (data->buffer, buffer + location, length); + + /* Make sure DATA knows how much it's holding. */ + objects_data_set_length (data, length); + + return length; +} + +size_t +objects_data_set_buffer_of_length (objects_data_t * data, void *buffer, size_t length) +{ + return objects_data_set_buffer_of_subrange (data, buffer, 0, length); +} + +void +objects_data_get_md5_checksum (objects_data_t * data, char *buffer) +{ + if (buffer != NULL) + { + char cksum[17]; + + /* Perform the MD5 checksum on DATA's buffer. */ + md5_buffer ((const char *) (data->buffer), data->length, cksum); + + /* Copy CKSUM into BUFFER. */ + strcpy (buffer, cksum); + } + + return; +} + +/** Copying **/ + +objects_data_t * +objects_data_copy (objects_data_t * data) +{ + return objects_data_copy_with_allocs (data, objects_data_allocs (data)); +} + +objects_data_t * +objects_data_copy_of_subrange (objects_data_t * data, size_t location, size_t length) +{ + return objects_data_copy_of_subrange_with_allocs (data, location, length, + objects_data_allocs (data)); +} + +objects_data_t * +objects_data_copy_with_allocs (objects_data_t * data, objects_allocs_t allocs) +{ + return objects_data_copy_of_subrange_with_allocs (data, 0, data->length, allocs); +} + +objects_data_t * +objects_data_copy_of_subrange_with_allocs (objects_data_t * data, + size_t location, + size_t length, + objects_allocs_t allocs) +{ + objects_data_t *copy; + + /* Make a low-level copy. */ + copy = _objects_data_copy_with_allocs (data, allocs); + + objects_data_init_with_subrange_of_data (copy, location, length, data); + + return copy; +} + +/** Replacing **/ + +/* Note that we cannot do any bounds checking on BUFFER. */ +objects_data_t * +objects_data_replace_subrange_with_subrange_of_buffer (objects_data_t * data, + size_t location, + size_t length, + size_t buf_location, + size_t buf_length, + void *buffer) +{ + /* Update the change time. */ + _objects_data_set_change_time (data); + + /* Make sure we're inside DATA. */ + location = MIN (location, data->length); + length = MIN (data->length - location, length); + + if (buf_length > length) + { + /* Increase DATA's capacity. */ + objects_data_increase_capacity (data, buf_length - length); + + /* Move the tail of DATA's buffer over BUF_LENGTH. */ + memmove (data->buffer + location + buf_length, + data->buffer + location + length, + data->length - location - length); + + /* Copy the subrange of BUFFER into DATA. */ + memmove (data->buffer + location, buffer + buf_location, buf_length); + + /* Update DATA's length. */ + objects_data_set_length (data, data->length + buf_length - length); + } + else + /* (buf_length <= length) */ + { + /* Copy the subrange of BUFFER into DATA. */ + memmove (data->buffer + location, buffer + buf_location, buf_length); + + /* Move the tail of DATA's buffer over BUF_LENGTH. */ + memmove (data->buffer + location + buf_length, + data->buffer + location + length, + data->length - location - length); + + /* Decrease DATA's length to accomodate BUF_LENGTH's worth of BUFFER. */ + objects_data_decrease_capacity (data, length - buf_length); + + /* Update DATA's length. */ + objects_data_set_length (data, data->length - length + buf_length); + } + + return data; +} + +objects_data_t * +objects_data_replace_subrange_with_subrange_of_data (objects_data_t * data, + size_t location, + size_t length, + size_t other_location, + size_t other_length, + objects_data_t * other_data) +{ + /* Update OTHER_DATA's access time. */ + _objects_data_set_access_time (other_data); + + /* Make sure we're inside DATA. */ + other_location = MIN (other_location, other_data->length); + other_length = MIN (other_data->length - other_location, other_length); + + /* Copy away. */ + return objects_data_replace_subrange_with_subrange_of_buffer (data, location, + length, + other_location, + other_length, + other_data->buffer); +} + +objects_data_t * +objects_data_replace_subrange_with_data (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data) +{ + /* Update OTHER_DATA's access time. */ + _objects_data_set_access_time (other_data); + + /* Copy away. */ + return objects_data_replace_subrange_with_subrange_of_buffer (data, location, + length, + 0, + other_data->length, + other_data->buffer); +} + +/** Appending **/ + +objects_data_t * +objects_data_append_data (objects_data_t * data, objects_data_t * other_data) +{ + return objects_data_replace_subrange_with_subrange_of_data (data, + data->length, + 0, 0, + other_data->length, + other_data); +} + +objects_data_t * +objects_data_append_subrange_of_data (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data) +{ + return objects_data_replace_subrange_with_subrange_of_data (data, data->length, + 0, location, length, + other_data); +} + +objects_data_t * +objects_data_append_data_repeatedly (objects_data_t * data, + objects_data_t * other_data, + size_t num_times) +{ + /* FIXME: Do this more efficiently. You know how. */ + while (num_times--) + objects_data_append_data (data, other_data); + + return data; +} + +objects_data_t * +objects_data_append_subrange_of_data_repeatedly (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data, + size_t num_times) +{ + /* FIXME: Do this more efficiently. You know how. */ + while (num_times--) + objects_data_append_subrange_of_data (data, location, length, other_data); + + return data; +} + +/** Prepending **/ + +objects_data_t * +objects_data_prepend_data (objects_data_t * data, objects_data_t * other_data) +{ + return objects_data_replace_subrange_with_subrange_of_data (data, + 0, 0, 0, + other_data->length, + other_data); +} + +objects_data_t * +objects_data_prepend_subrange_of_data (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data) +{ + return objects_data_replace_subrange_with_subrange_of_data (data, + 0, 0, location, + length, + other_data); +} + +objects_data_t * +objects_data_prepend_data_repeatedly (objects_data_t * data, + objects_data_t * other_data, + size_t num_times) +{ + /* FIXME: Do this more efficiently. You know how. */ + while (num_times--) + objects_data_prepend_data (data, other_data); + + return data; +} + +objects_data_t * +objects_data_prepend_subrange_of_data_repeatedly (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data, + size_t num_times) +{ + /* FIXME: Do this more efficiently. You know how. */ + while (num_times--) + objects_data_prepend_subrange_of_data (data, location, length, other_data); + + return data; +} + +/** Concatenating **/ + +objects_data_t * +objects_data_concatenate_data (objects_data_t * data, + objects_data_t * other_data) +{ + return objects_data_concatenate_data_with_allocs (data, other_data, + objects_data_allocs (data)); +} + +objects_data_t * +objects_data_concatenate_data_with_allocs (objects_data_t * data, + objects_data_t * other_data, + objects_allocs_t allocs) +{ + objects_data_t *new_data; + + /* Make a copy of DATA. */ + new_data = objects_data_copy_with_allocs (data, allocs); + + /* Append OTHER_DATA to DATA. */ + objects_data_append_data (data, other_data); + + /* Return the concatenation. */ + return new_data; +} + +objects_data_t * +objects_data_concatenate_subrange_of_data (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data) +{ + return objects_data_concatenate_subrange_of_data_with_allocs (data, location, + length, + other_data, + objects_data_allocs (data)); +} + +objects_data_t * +objects_data_concatenate_subrange_of_data_with_allocs (objects_data_t * data, + size_t location, + size_t length, + objects_data_t * other_data, + objects_allocs_t allocs) +{ + objects_data_t *new_data; + + /* Make a copy of DATA. */ + new_data = objects_data_copy_with_allocs (data, allocs); + + /* Append the subrange of OTHER_DATA to DATA. */ + objects_data_append_subrange_of_data (data, location, length, other_data); + + /* Return the concatenation. */ + return new_data; +} + +/** Reversing **/ + +objects_data_t * +objects_data_reverse_with_granularity (objects_data_t * data, size_t granularity) +{ + /* Update the change time. */ + _objects_data_set_change_time (data); + + if ((data->length % granularity) == 0) + { + size_t i; + objects_allocs_t allocs; + void *buffer; + + /* Remember the allocs that DATA use. */ + allocs = objects_data_allocs (data); + + /* Create a temporary buffer for to play wif. */ + buffer = objects_malloc (allocs, data->length); + + /* FIXME: Do some checking here, good man. */ + + /* Make a flipped copy of DATA in BUFFER. */ + for (i = 0; i < data->length; i += granularity) + memcpy (data->buffer + i, + data->buffer + data->length - (i + granularity), + granularity); + + /* Copy the reversed version back into DATA. */ + memcpy (data->buffer, buffer, data->length); + + /* Free the temporary buffer. */ + objects_free (allocs, buffer); + } + + return data; +} + +objects_data_t * +objects_data_reverse_by_int (objects_data_t * data) +{ + return objects_data_reverse_with_granularity (data, sizeof (int)); +} + +objects_data_t * +objects_data_reverse_by_char (objects_data_t * data) +{ + return objects_data_reverse_with_granularity (data, sizeof (char)); +} + +objects_data_t * +objects_data_reverse_by_void_p (objects_data_t * data) +{ + return objects_data_reverse_with_granularity (data, sizeof (void *)); +} + +/** Permuting **/ + +objects_data_t * +objects_data_permute_with_granularity (objects_data_t * data, size_t granularity) +{ + /* FIXME: Code this. */ +} + +objects_data_t * +objects_data_permute_with_no_fixed_points_with_granularity (objects_data_t * data, + size_t granularity) +{ + /* FIXME: Code this. */ +} + +/** Writing **/ + +int +_objects_data_write_to_file (objects_data_t * data, const char *file) +{ + FILE *f; + int c; + + /* Open the file (whether temp or real) for writing. */ + f = fopen (file, "w"); + + if (f == NULL) /* Something went wrong; we weren't + * even able to open the file. */ + goto failure; + + /* Update the access time. */ + _objects_data_set_access_time (data); + + /* Now we try and write DATA's buffer to the file. Here C is the + * number of bytes which were successfully written to the file in + * the fwrite() call. */ + /* FIXME: Do we need the `sizeof(char)' here? Is there any system + * where sizeof(char) isn't just 1? Or is it guaranteed to be 8 + * bits? */ + c = fwrite (data->buffer, sizeof (char), data->length, f); + + if (c < data->length) /* We failed to write everything for + * some reason. */ + goto failure; + + /* We're done, so close everything up. */ + c = fclose (f); + + if (c != 0) /* I can't imagine what went wrong + * closing the file, but we got here, + * so we need to deal with it. */ + goto failure; + + /* success: */ + return 1; + + /* Just in case the failure action needs to be changed. */ +failure: + return 0; +} + +/** Encoding **/ + +objects_data_encoding_t +objects_data_guess_data_encoding (objects_data_t * data) +{ +} + +/* FIXME: I don't quite know how to deal with the following paragraph + * of the base64 specification from RFC 1521: "Care must be taken to + * use the proper octets for line breaks if base64 encoding is applied + * directly to text material that has not been converted to canonical + * form. In particular, text line breaks must be converted into CRLF + * sequences prior to base64 encoding. The important thing to note is + * that this may be done directly by the encoder rather than in a + * prior canonicalization step in some implementations." I think that + * what I am doing is acceptable, but just wanted to note this + * possible glitch for my sanity's sake. */ +objects_data_t * +_objects_data_encode_with_base64 (objects_data_t * data) +{ + unsigned char *buffer; + unsigned char *d_buffer; + size_t d_length, length, blocks, extras; + size_t c, i, j; + unsigned char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + void do_base64_block (unsigned char *in, unsigned char *out) + { + out[0] = base64[(in[0] >> 2)]; + out[1] = base64[((in[0] & 0x03) << 4) + (in[1] >> 4)]; + out[2] = base64[((in[1] & 0x0f) << 2) + (in[2] << 6)]; + out[3] = base64[(in[2] & 0x3f)]; + } + + /* Do some figuring. WARNING: We assume throughout that there are + * precisely eight bits in an unsigned char. Is this ever a + * problem? FIXME: Can we assume that `sizeof(unsigned char)' is + * one? */ + d_length = (data->length) / sizeof (unsigned char); + blocks = d_length / 3; + extras = d_length % 3; + length = (blocks + ((extras + 1) / 2)) * 4; + + /* Make room to put the encoded information temporarily. */ + buffer = (unsigned char *) objects_malloc (objects_data_allocs (data), length); + d_buffer = (unsigned char *) (data->buffer); + + /* Translate blocks of three eights into blocks of four sixes. */ + for (c = 0; c < blocks; ++c, j += 4, i += 3) + do_base64_block (d_buffer + i, buffer + j); + + /* Now we need to worry about any stragglers... */ + if (extras != 0) + { + /* Pad with zeros. */ + unsigned char in_block[3] = + {0x00, 0x00, 0x00}; + + /* Copy over the stragglers into our temporary buffer. */ + for (c = 0; c < extras; ++c) + in_block[c] = d_buffer[i + c]; + + /* Base64-ize the temporary buffer into the end of BUFFER. */ + do_base64_block (in_block, buffer + j); + + /* Pad the end of BUFFER with the base64 pad character '='. */ + for (c = 3; c > extras; --c) + buffer[j + c] = base64[64]; + } + + /* Copy the encoded buffer back into DATA. */ + objects_data_set_buffer_of_length (data, buffer, length); + + return data; +} + +objects_data_t * +_objects_data_encode_with_quoted_printable (objects_data_t * data) +{ + objects_data_t *other_data; + unsigned char *d_buffer; + size_t d_length, c; + + /* Remember DATA's characteristics. */ + d_buffer = (unsigned char *) (data->buffer); + d_length = (data->length) / sizeof (unsigned char); + + /* Create another flexible buffer. */ + other_data = objects_data_new_with_allocs (objects_data_allocs (data)); + + for (c = 0; c < d_length; ++c) + { + switch (d_buffer[c]) + { + case 0x21 ... 0x3c: + case 0x3e ... 0xfe: + break; + case 0x0a: + default: + } + } + + /* Get rid of our temporary storage. */ + objects_data_dealloc (other_data); + + /* FIXME: Finish this. */ + + return data; +} + +objects_data_t * +_objects_data_encode_with_x_uuencode (objects_data_t * data) +{ + /* FIXME: Code this. */ + return data; +} + +objects_data_t * +objects_data_encode_with_data_encoding (objects_data_t * data, objects_data_encoding_t enc) +{ + switch (enc) + { + case objects_data_encoding_base64: + return _objects_data_encode_with_base64 (data); + break; + case objects_data_encoding_quoted_printable: + return _objects_data_encode_with_quoted_printable (data); + break; + case objects_data_encoding_x_uuencode: + return _objects_data_encode_with_x_uuencode (data); + break; + case objects_data_encoding_unknown: + case objects_data_encoding_binary: + case objects_data_encoding_7bit: + case objects_data_encoding_8bit: + default: + return data; + break; + } + + return data; +} + +objects_data_t * +_objects_data_decode_with_base64 (objects_data_t * data) +{ + /* FIXME: Code this. */ + return data; +} + +objects_data_t * +_objects_data_decode_with_quoted_printable (objects_data_t * data) +{ + /* FIXME: Code this. */ + return data; +} + +objects_data_t * +_objects_data_decode_with_x_uuencode (objects_data_t * data) +{ + /* FIXME: Code this. */ + return data; +} + +objects_data_t * +objects_data_decode_with_data_encoding (objects_data_t * data, objects_data_encoding_t enc) +{ + switch (enc) + { + case objects_data_encoding_base64: + return _objects_data_decode_with_base64 (data); + break; + case objects_data_encoding_quoted_printable: + return _objects_data_decode_with_quoted_printable (data); + break; + case objects_data_encoding_x_uuencode: + return _objects_data_decode_with_x_uuencode (data); + break; + case objects_data_encoding_unknown: + case objects_data_encoding_binary: + case objects_data_encoding_7bit: + case objects_data_encoding_8bit: + default: + return data; + break; + } + + return data; +} + diff --git a/Source/hash.c b/Source/hash.c new file mode 100644 index 000000000..624f6baf5 --- /dev/null +++ b/Source/hash.c @@ -0,0 +1,1037 @@ +/* A (pretty good) hash table implementation. + * Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: ??? ??? ?? ??:??:?? ??? 1993 + * Updated: Sat Feb 10 14:54:14 EST 1996 + * Serial: 96.02.10.05 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Behind-the-Scenes functions **/ + +objects_hash_bucket_t * +_objects_hash_pick_bucket_for_element (objects_hash_t *hash, + objects_hash_bucket_t *buckets, + size_t bucket_count, + void *element) +{ + return buckets + (objects_hash (objects_hash_element_callbacks (hash), + element, hash) % bucket_count); +} + +objects_hash_bucket_t * +_objects_hash_pick_bucket_for_node (objects_hash_t * hash, + objects_hash_bucket_t * buckets, + size_t bucket_count, + objects_hash_node_t * node) +{ + return buckets + (objects_hash (objects_hash_element_callbacks (hash), + node->element, hash) % bucket_count); +} + +objects_hash_bucket_t * +_objects_hash_bucket_for_element (objects_hash_t * hash, void *element) +{ + return _objects_hash_pick_bucket_for_element (hash, hash->buckets, + hash->bucket_count, element); +} + +objects_hash_bucket_t * +_objects_hash_bucket_for_node (objects_hash_t * hash, objects_hash_node_t * node) +{ + return _objects_hash_pick_bucket_for_node (hash, hash->buckets, + hash->bucket_count, node); +} + +void +_objects_hash_link_node_into_bucket (objects_hash_bucket_t * bucket, + objects_hash_node_t * node) +{ + if (bucket->first_node != NULL) + bucket->first_node->prev_in_bucket = node; + + node->next_in_bucket = bucket->first_node; + + bucket->first_node = node; + + return; +} + +void +_objects_hash_unlink_node_from_its_bucket (objects_hash_node_t * node) +{ + if (node == node->bucket->first_node) + node->bucket->first_node = node->next_in_bucket; + + if (node->prev_in_bucket != NULL) + node->prev_in_bucket->next_in_bucket = node->next_in_bucket; + if (node->next_in_bucket != NULL) + node->next_in_bucket->prev_in_bucket = node->prev_in_bucket; + + node->prev_in_bucket = node->next_in_bucket = NULL; + + return; +} + +void +_objects_hash_link_node_into_hash (objects_hash_t * hash, objects_hash_node_t * node) +{ + if (hash->first_node != NULL) + hash->first_node->prev_in_hash = node; + + node->next_in_hash = hash->first_node; + + hash->first_node = node; + + return; +} + +void +_objects_hash_unlink_node_from_its_hash (objects_hash_node_t * node) +{ + if (node == node->hash->first_node) + node->hash->first_node = node->next_in_hash; + + if (node->prev_in_hash != NULL) + node->prev_in_hash->next_in_hash = node->next_in_hash; + if (node->next_in_hash != NULL) + node->next_in_hash->prev_in_hash = node->prev_in_hash; + + node->prev_in_hash = node->next_in_hash = NULL; + + return; +} + +void +_objects_hash_add_node_to_bucket (objects_hash_bucket_t * bucket, objects_hash_node_t * node) +{ + if (bucket != NULL) + { + _objects_hash_link_node_into_bucket (bucket, node); + + node->bucket = bucket; + + bucket->node_count += 1; + bucket->element_count += 1; + } + + return; +} + +void +_objects_hash_add_node_to_its_bucket (objects_hash_t * hash, objects_hash_node_t * node) +{ + _objects_hash_add_node_to_bucket (_objects_hash_bucket_for_node (hash, node), node); + return; +} + +void +_objects_hash_add_node_to_hash (objects_hash_t * hash, objects_hash_node_t * node) +{ + if (hash != NULL) + { + _objects_hash_add_node_to_its_bucket (hash, node); + + _objects_hash_link_node_into_hash (hash, node); + + node->hash = hash; + + hash->node_count += 1; + hash->element_count += 1; + } + + return; +} + +void +_objects_hash_remove_node_from_its_bucket (objects_hash_node_t * node) +{ + if (node->bucket != NULL) + { + node->bucket->node_count -= 1; + node->bucket->element_count -= 1; + + _objects_hash_unlink_node_from_its_bucket (node); + } + + return; +} + +void +_objects_hash_remove_node_from_its_hash (objects_hash_node_t * node) +{ + if (node->hash != NULL) + { + node->hash->node_count -= 1; + node->hash->element_count -= 1; + + _objects_hash_unlink_node_from_its_hash (node); + } + + _objects_hash_remove_node_from_its_bucket (node); + + return; +} + +objects_hash_bucket_t * +_objects_hash_new_buckets (objects_hash_t *hash, size_t bucket_count) +{ + return (objects_hash_bucket_t *) objects_calloc (objects_hash_allocs (hash), + bucket_count, + sizeof (objects_hash_bucket_t)); +} + +void +_objects_hash_free_buckets (objects_hash_t *hash, objects_hash_bucket_t *buckets) +{ + if (buckets != NULL) + objects_free (objects_hash_allocs (hash), buckets); + return; +} + +void +_objects_hash_remangle_buckets (objects_hash_t * hash, + objects_hash_bucket_t * old_buckets, + size_t old_bucket_count, + objects_hash_bucket_t * new_buckets, + size_t new_bucket_count) +{ + size_t i; + objects_hash_node_t *node; + + for (i = 0; i < old_bucket_count; i++) + { + while ((node = old_buckets[i].first_node) != NULL) + { + _objects_hash_remove_node_from_its_bucket (node); + _objects_hash_add_node_to_bucket (_objects_hash_pick_bucket_for_node (hash, + new_buckets, + new_bucket_count, + node), + node); + } + } + + /* And that's that. */ + return; +} + +objects_hash_node_t * +_objects_hash_new_node (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + /* Allocate the space for a new node. */ + node = (objects_hash_node_t *) objects_malloc (objects_hash_allocs (hash), + sizeof (objects_hash_node_t)); + + if (node != NULL) + { + /* Retain ELEMENT. (It's released in `_objects_hash_free_node()'.) */ + objects_retain (objects_hash_element_callbacks (hash), element, hash); + + /* Remember ELEMENT. */ + node->element = element; + + /* Zero out the various pointers. */ + node->hash = NULL; + node->bucket = NULL; + node->next_in_bucket = NULL; + node->next_in_hash = NULL; + node->prev_in_bucket = NULL; + node->prev_in_hash = NULL; + } + + return node; +} + +void +_objects_hash_free_node (objects_hash_node_t * node) +{ + if (node != NULL) + { + objects_hash_t *hash; + + /* Remember NODE's hash. */ + hash = node->hash; + + /* Release ELEMENT. (It's retained in `_objects_hash_new_node()'.) */ + objects_release (objects_hash_element_callbacks (hash), node->element, hash); + + /* Actually free the space hash aside for NODE. */ + objects_free (objects_hash_allocs (hash), node); + } + + /* And just return. */ + return; +} + +objects_hash_node_t * +_objects_hash_node_for_element (objects_hash_t * hash, void *element) +{ + objects_hash_bucket_t *bucket; + objects_hash_node_t *node; + + /* Find the bucket in which the node for ELEMENT would be. */ + bucket = _objects_hash_bucket_for_element (hash, element); + + /* Run through the nodes in BUCKET until we find one whose element + * matches ELEMENT. */ + for (node = bucket->first_node; + (node != NULL) && !objects_is_equal (objects_hash_element_callbacks (hash), + element, + node->element, + hash); + node = node->next_in_bucket); + + /* Note that if none of the nodes' elements matches ELEMENT, then we + * naturally return `NULL'. */ + return node; +} + +objects_hash_node_t * +_objects_hash_enumerator_next_node (objects_hash_enumerator_t * enumerator) +{ + objects_hash_node_t *node; + + /* Remember ENUMERATOR's current node. */ + node = enumerator->node; + + /* If NODE is a real node, then we need to increment ENUMERATOR's + * current node to the next node in ENUMERATOR's hash. */ + if (node != NULL) + enumerator->node = enumerator->node->next_in_hash; + + /* Send back NODE. */ + return node; +} + +/** Hashing **/ + +size_t +objects_hash_hash (objects_hash_t * hash) +{ + /* FIXME: Code this. */ + return 0; +} + +/** Resizing **/ + +size_t +objects_hash_resize (objects_hash_t * hash, size_t new_capacity) +{ + objects_hash_bucket_t *new_buckets; + + /* Round NEW_CAPACITY up to the next power of two. */ + new_capacity = objects_next_power_of_two (new_capacity); + + /* Make a new hash of buckets. */ + new_buckets = _objects_hash_new_buckets (hash, new_capacity); + + if (new_buckets != NULL) + { + _objects_hash_remangle_buckets (hash, + hash->buckets, + hash->bucket_count, + new_buckets, + new_capacity); + + _objects_hash_free_buckets (hash, hash->buckets); + + hash->buckets = new_buckets; + hash->bucket_count = new_capacity; + + } + + /* Return the new capacity. */ + return hash->bucket_count; +} + +size_t +objects_hash_rightsize (objects_hash_t * hash) +{ + /* FIXME: Now, this is a guess, based solely on my intuition. If + * anyone knows of a better ratio (or other test, for that matter) + * and can provide evidence of its goodness, please get in touch + * with me, Albin L. Jones . */ + + if (3 * hash->node_count > 4 * hash->bucket_count) + { + return objects_hash_resize (hash, hash->bucket_count + 1); + } + else + { + return hash->bucket_count; + } +} + +/** Statistics **/ + +size_t +objects_hash_count (objects_hash_t * hash) +{ + return hash->element_count; +} + +size_t +objects_hash_capacity (objects_hash_t * hash) +{ + return hash->bucket_count; +} + +int +objects_hash_check (objects_hash_t * hash) +{ + /* FIXME: Code this. */ + return 0; +} + +/** Searching **/ + +int +objects_hash_contains_element (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + node = _objects_hash_node_for_element (hash, element); + + return node != NULL; +} + +void * +objects_hash_element (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + /* Try and find the node for ELEMENT. */ + node = _objects_hash_node_for_element (hash, element); + + if (node != NULL) + return node->element; + else + return objects_hash_not_an_element_marker (hash); +} + +void ** +objects_hash_all_elements (objects_hash_t * hash) +{ + size_t j; + void **array; + objects_hash_enumerator_t enumerator; + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `objects_free(objects_hash_allocs(HASH), ARRAY)' */ + array = (void **) objects_calloc (objects_hash_allocs (hash), + hash->node_count + 1, + sizeof (void *)); + + /* ENUMERATOR is an enumerator for HASH. */ + enumerator = objects_hash_enumerator (hash); + + /* Now we enumerate through the elements of HASH, adding them + * one-by-one to ARRAY. */ + for (j = 0; j < hash->node_count; j++) + objects_hash_enumerator_next_element (&enumerator, array + j); + + /* We terminate ARRAY with the `not_an_element_marker' for HASH. */ + array[j] = objects_hash_not_an_element_marker (hash); + + /* And we're done. */ + return array; +} + +int +objects_hash_is_empty (objects_hash_t * hash) +{ + return objects_hash_count (hash) == 0; +} + +/** Enumerating **/ + +/* WARNING: You should not alter a hash while an enumeration is + * in progress. The results of doing so are reasonably unpredictable. + * With that in mind, read the following warnings carefully. But + * remember, DON'T MESS WITH A HASH WHILE YOU'RE ENUMERATING IT. */ + +/* IMPORTANT WARNING: Hash enumerators, as I have hash them up, have a + * wonderous property. Namely, that, while enumerating, one may add + * new elements (i.e., new nodes) to the hash while an enumeration is + * in progress (i.e., after `objects_hash_enumerator()' has been called), and + * the enumeration remains the same. */ + +/* WARNING: The above warning should not, in any way, be taken as + * assurance that this property of hash enumerators will be preserved + * in future editions of the library. I'm still thinking about + * this. */ + +/* IMPORTANT WARNING: Enumerators have yet another wonderous property. + * Once a node has been returned by + * `_objects_hash_enumerator_next_node()', it may be removed from the hash + * without effecting the rest of the current enumeration. For + * example, to clean all of the nodes out of a hash, the following code + * would work: + * + * void + * empty_my_hash(objects_hash_t *hash) + * { + * objects_hash_enumerator_t enumerator = objects_hash_enumerator(hash); + * objects_hash_node_t *node; + * + * while ((node = _objects_hash_enumerator_next_node(&enumerator)) != NULL) + * { + * _objects_hash_remove_node_from_its_hash(node); + * _objects_hash_free_node(node); + * } + * + * return; + * } + * + * (In fact, this is the code currently being used below in the + * function `objects_hash_empty()'.) But again, this is not to be taken + * as an assurance that this behaviour will persist in future versions + * of the library. */ + +/* EXTREMELY IMPORTANT WARNING: The purpose of this warning is point + * out that, at this time, various (i.e., many) functions depend on + * the behaviours outlined above. So be prepared for some serious + * breakage when you go fudging around with these things. */ + +objects_hash_enumerator_t +objects_hash_enumerator (objects_hash_t * hash) +{ + objects_hash_enumerator_t enumerator; + + /* Make sure ENUMERATOR knows its hash. */ + enumerator.hash = hash; + + /* Start ENUMERATOR at HASH's first node. */ + enumerator.node = hash->first_node; + + return enumerator; +} + +int +objects_hash_enumerator_next_element (objects_hash_enumerator_t * enumerator, + void **element) +{ + objects_hash_node_t *node; + + /* Get the next node in the enumeration represented by + * ENUMERATOR. */ + node = _objects_hash_enumerator_next_node (enumerator); + + if (node != NULL) + { + /* NODE is real, so we pull the information out of it that we + * need. Note that we check to see whether ELEMENT and COUNT are + * non-`NULL'. */ + if (element != NULL) + *element = node->element; + + /* Since we weren't at the end of our enumeration, we return + * ``true''. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the bogus element indicator + * and a zero count. */ + if (element != NULL) + *element = objects_hash_not_an_element_marker (enumerator->hash); + + /* Since we're at the end of the enumeration, we return + * ``false''. */ + return 0; + } +} + +/** Adding **/ + +void * +objects_hash_add_element_known_absent (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + node = _objects_hash_new_node (hash, element); + + if (node != NULL) + { + /* Actually add NODE to HASH. */ + _objects_hash_add_node_to_hash (hash, node); + + return node->element; + } + else + { + return objects_hash_not_an_element_marker (hash); + } +} + +void * +objects_hash_add_element (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + /* First, we check for ELEMENT in HASH. */ + node = _objects_hash_node_for_element (hash, element); + + if (node == NULL) + { + /* ELEMENT isn't in HASH, so we can add it with impunity. */ + return objects_hash_add_element_known_absent (hash, element); + } + else + { + objects_retain (objects_hash_element_callbacks (hash), element, hash); + objects_release (objects_hash_element_callbacks (hash), node->element, hash); + return node->element = element; + } +} + +void * +objects_hash_add_element_if_absent (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + /* First, we check for ELEMENT in HASH. */ + node = _objects_hash_node_for_element (hash, element); + + if (node == NULL) + { + /* ELEMENT isn't in HASH, so we can add it with impunity. */ + return objects_hash_add_element_known_absent (hash, element); + } + + return node->element; +} + +/** Removing **/ + +void +objects_hash_remove_element (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + node = _objects_hash_node_for_element (hash, element); + + if (node != NULL) + { + /* Pull NODE out of HASH. */ + _objects_hash_remove_node_from_its_hash (node); + + /* Free up NODE. */ + _objects_hash_free_node (node); + } + + return; +} + +/** Emptying **/ + +void +objects_hash_empty (objects_hash_t * hash) +{ + objects_hash_enumerator_t enumerator; + objects_hash_node_t *node; + + /* Get an element enumerator for HASH. */ + enumerator = objects_hash_enumerator (hash); + + /* Just step through the nodes of HASH and wipe them out, one after + * another. Don't try this at home, kids! Note that, under + * ordinary circumstances, this would be a verboten use of + * hash enumerators. See the warnings with the enumerator + * functions for more details. */ + while ((node = _objects_hash_enumerator_next_node (&enumerator)) != NULL) + { + _objects_hash_remove_node_from_its_hash (node); + _objects_hash_free_node (node); + } + + /* And return. */ + return; +} + +/** Creating **/ + +objects_hash_t * +objects_hash_alloc_with_allocs (objects_allocs_t allocs) +{ + objects_hash_t *hash; + + /* Get a new hash. */ + hash = _objects_hash_alloc_with_allocs (allocs); + + return hash; +} + +objects_hash_t * +objects_hash_alloc (void) +{ + return objects_hash_alloc_with_allocs (objects_allocs_standard()); +} + +objects_hash_t * +objects_hash_with_callbacks (objects_callbacks_t callbacks) +{ + return objects_hash_init_with_callbacks (objects_hash_alloc (), callbacks); +} + +objects_hash_t * +objects_hash_with_allocs_with_callbacks (objects_allocs_t allocs, + objects_callbacks_t callbacks) +{ + return objects_hash_init_with_callbacks (objects_hash_alloc_with_allocs (allocs), + callbacks); +} + +objects_hash_t * +objects_hash_with_allocs (objects_allocs_t allocs) +{ + return objects_hash_init (objects_hash_alloc_with_allocs (allocs)); +} + +objects_hash_t * +objects_hash_of_char_p (void) +{ + return objects_hash_with_callbacks (objects_callbacks_for_char_p); +} + +objects_hash_t * +objects_hash_of_void_p (void) +{ + return objects_hash_with_callbacks (objects_callbacks_for_void_p); +} + +objects_hash_t * +objects_hash_of_owned_void_p (void) +{ + return objects_hash_with_callbacks (objects_callbacks_for_owned_void_p); +} + +objects_hash_t * +objects_hash_of_int (void) +{ + return objects_hash_with_callbacks (objects_callbacks_for_int); +} + +objects_hash_t * +objects_hash_of_id (void) +{ + return objects_hash_with_callbacks (objects_callbacks_for_id); +} + +/** Initializing **/ + +objects_hash_t * +objects_hash_init_with_callbacks (objects_hash_t * hash, + objects_callbacks_t callbacks) +{ + if (hash != NULL) + { + size_t capacity = 10; + + /* Make a note of the callbacks and allocs for HASH. */ + hash->callbacks = objects_callbacks_standardize (callbacks); + + /* Zero out the various counts. */ + hash->node_count = 0; + hash->bucket_count = 0; + hash->element_count = 0; + + /* Zero out the pointers. */ + hash->first_node = NULL; + hash->buckets = NULL; + + /* Resize HASH to the given CAPACITY. */ + objects_hash_resize (hash, capacity); + } + + /* Return the newly initialized HASH. */ + return hash; +} + +objects_hash_t * +objects_hash_init (objects_hash_t * hash) +{ + return objects_hash_init_with_callbacks (hash, objects_callbacks_standard()); +} + +objects_hash_t * +objects_hash_init_from_hash (objects_hash_t * hash, objects_hash_t * old_hash) +{ + if (hash != NULL) + { + objects_hash_enumerator_t enumerator; + void *element; + + /* Make a note of the callbacks for HASH. */ + hash->callbacks = objects_hash_element_callbacks (hash); + + /* Zero out the various counts. */ + hash->node_count = 0; + hash->bucket_count = 0; + hash->element_count = 0; + + /* Zero out the pointers. */ + hash->first_node = NULL; + hash->buckets = NULL; + + /* Resize HASH to the given CAPACITY. */ + objects_hash_resize (hash, objects_hash_capacity (old_hash)); + + /* Get an element enumerator for OLD_HASH. */ + enumerator = objects_hash_enumerator (old_hash); + + /* Add OLD_HASH's elements to HASH, one at a time. */ + while (objects_hash_enumerator_next_element (&enumerator, &element)) + objects_hash_add_element_known_absent (hash, element); + } + + /* Return the newly initialized HASH. */ + return hash; +} + +/** Destroying **/ + +void +objects_hash_dealloc (objects_hash_t * hash) +{ + /* Remove all of HASH's elements. */ + objects_hash_empty (hash); + + /* Free up the bucket array. */ + _objects_hash_free_buckets (hash, hash->buckets); + + /* And finally, perform the ultimate sacrifice. */ + _objects_hash_dealloc (hash); + + return; +} + +/** Replacing **/ + +void +objects_hash_replace_element (objects_hash_t * hash, void *element) +{ + objects_hash_node_t *node; + + /* Lookup the node for ELEMENT. */ + node = _objects_hash_node_for_element (hash, element); + + if (node != NULL) + { + /* Remember: First retain the new element, then release the old + * element, just in case they're the same. */ + objects_retain (objects_hash_element_callbacks (hash), element, hash); + objects_release (objects_hash_element_callbacks (hash), node->element, hash); + node->element = element; + } + + return; +} + +/** Comparing **/ + +/* Returns true if HASH1 is a superset of HASH2. */ +int +objects_hash_contains_hash (objects_hash_t * hash1, objects_hash_t * hash2) +{ + objects_hash_enumerator_t enumerator; + void *element; + + enumerator = objects_hash_enumerator (hash2); + + while (objects_hash_enumerator_next_element (&enumerator, &element)) + if (!objects_hash_contains_element (hash1, element)) + return 0; + + return 1; +} + +/* Returns true if HASH1 is both a superset and a subset of HASH2. + * Checks to make sure HASH1 and HASH2 have the same number of + * elements first. */ +int +objects_hash_is_equal_to_hash (objects_hash_t * hash1, objects_hash_t * hash2) +{ + size_t a, b; + + /* Count HASH1 and HASH2. */ + a = objects_hash_count (hash1); + b = objects_hash_count (hash2); + + /* Check the counts. */ + if (a != b) + return 0; + + /* If the counts match, then we do an element by element check. */ + if (!objects_hash_contains_hash (hash1, hash2) + || !objects_hash_contains_hash (hash2, hash1)) + return 0; + + /* If we made it this far, HASH1 and HASH2 are the same. */ + return 1; +} + +/* Returns true if HASH and OTHER_HASH have at least one element in + * common. */ +int +objects_hash_intersects_hash (objects_hash_t * hash, objects_hash_t * other_hash) +{ + objects_hash_enumerator_t enumerator; + void *element; + + /* Get an element enumerator for OTHER_HASH. */ + enumerator = objects_hash_enumerator (other_hash); + + while (objects_hash_enumerator_next_element (&enumerator, &element)) + if (objects_hash_contains_element (hash, element)) + return 1; + + return 0; +} + +/** Copying **/ + +/* Returns a new copy of OLD_HASH, using NEW_ALLOCS. */ +objects_hash_t * +objects_hash_copy_with_allocs (objects_hash_t * old_hash, objects_allocs_t new_allocs) +{ + objects_hash_t *new_hash; + + /* Alloc the NEW_HASH, copying over the low-level stuff. */ + new_hash = _objects_hash_copy_with_allocs (old_hash, new_allocs); + + /* Initialize the NEW_HASH. */ + objects_hash_init_from_hash (new_hash, old_hash); + + /* Return the NEW_HASH. */ + return new_hash; +} + +/* Returns a new copy of OLD_HASH, using OLD_HASH's allocs. */ +objects_hash_t * +objects_hash_copy (objects_hash_t * old_hash) +{ + /* FIXME: Should I be using `objects_allocs_standard()', or + * `objects_hash_allocs(old_hash)'? */ + return objects_hash_copy_with_allocs (old_hash, objects_hash_allocs (old_hash)); +} + +/** Mapping **/ + +/* WARNING: The mapping function FCN must be one-to-one on elements of + * HASH. I.e., for reasons of efficiency, `objects_hash_map_elements()' + * makes no provision for the possibility that FCN maps two unequal + * elements of HASH to the same (or equal) elements. The better way + * to handle functions that aren't one-to-one is to create a new hash + * and transform the elements of the first to create the elements of + * the second. */ +objects_hash_t * +objects_hash_map_elements (objects_hash_t * hash, + void *(*fcn) (void *, void *), + void *user_data) +{ + objects_hash_enumerator_t enumerator; + objects_hash_node_t *node; + + enumerator = objects_hash_enumerator (hash); + + while ((node = _objects_hash_enumerator_next_node (&enumerator)) != NULL) + { + void *element; + + element = (*fcn) (node->element, user_data); + + /* Remember: First retain the new element, then release the old + * element. */ + objects_retain (objects_hash_element_callbacks (hash), element, hash); + objects_release (objects_hash_element_callbacks (hash), node->element, hash); + node->element = element; + } + + return hash; +} + +/** Miscellaneous **/ + +/* Removes the elements of HASH which do not occur in OTHER_HASH. */ +objects_hash_t * +objects_hash_intersect_hash (objects_hash_t * hash, objects_hash_t * other_hash) +{ + objects_hash_enumerator_t enumerator; + objects_hash_node_t *node; + + enumerator = objects_hash_enumerator (hash); + + while ((node = _objects_hash_enumerator_next_node (&enumerator)) != NULL) + if (!objects_hash_contains_element (other_hash, node->element)) + { + _objects_hash_remove_node_from_its_hash (node); + _objects_hash_free_node (node); + } + + return hash; +} + +/* Removes the elements of HASH which occur in OTHER_HASH. */ +objects_hash_t * +objects_hash_minus_hash (objects_hash_t * hash, objects_hash_t * other_hash) +{ + objects_hash_enumerator_t enumerator; + objects_hash_node_t *node; + + enumerator = objects_hash_enumerator (hash); + + while ((node = _objects_hash_enumerator_next_node (&enumerator)) != NULL) + if (objects_hash_contains_element (other_hash, node->element)) + { + _objects_hash_remove_node_from_its_hash (node); + _objects_hash_free_node (node); + } + + return hash; +} + +/* Adds to HASH those elements of OTHER_HASH not occurring in HASH. */ +objects_hash_t * +objects_hash_union_hash (objects_hash_t * hash, objects_hash_t * other_hash) +{ + objects_hash_enumerator_t enumerator; + void *element; + + enumerator = objects_hash_enumerator (other_hash); + + while (objects_hash_enumerator_next_element (&enumerator, &element)) + objects_hash_add_element (hash, element); + + return hash; +} + diff --git a/Source/list.c b/Source/list.c new file mode 100644 index 000000000..19bdb41dd --- /dev/null +++ b/Source/list.c @@ -0,0 +1,906 @@ +/* A (pretty good) list implementation. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Tue Sep 5 17:23:50 EDT 1995 + * Updated: Sat Feb 10 14:50:36 EST 1996 + * Serial: 96.02.10.03 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Background functions **/ + +inline void +_objects_list_remove_node_from_its_list (objects_list_node_t * node) +{ + if (node->list->first_node == node) + node->list->first_node = node->next_in_list; + if (node->list->last_node == node) + node->list->last_node = node->prev_in_list; + if (node->next_in_list != NULL) + node->next_in_list->prev_in_list = node->prev_in_list; + if (node->prev_in_list != NULL) + node->prev_in_list->next_in_list = node->next_in_list; + + node->list->node_count -= 1; + node->list->element_count -= 1; + + return; +} + +objects_list_node_t * +_objects_list_new_node (objects_list_t * list, void *element) +{ + objects_list_node_t *node; + + node = objects_malloc (objects_list_allocs (list), sizeof (objects_list_node_t)); + + if (node != NULL) + { + node->list = list; + node->next_in_list = NULL; + node->prev_in_list = NULL; + objects_retain (objects_list_element_callbacks (list), element, list); + node->element = element; + } + + return node; +} + +void +_objects_list_free_node (objects_list_t * list, objects_list_node_t * node) +{ + objects_release (objects_list_element_callbacks (node->list), node->element, node->list); + objects_free (objects_list_allocs (list), node); + return; +} + +inline objects_list_node_t * +_objects_list_nth_node (objects_list_t * list, long int n) +{ + objects_list_node_t *node; + + if (n < 0) + { + node = list->last_node; + ++n; + + while (node != NULL && n != 0) + { + node = node->prev_in_list; + ++n; + } + } + else + /* (n >= 0) */ + { + node = list->first_node; + + while (node != NULL && n != 0) + { + node = node->next_in_list; + --n; + } + } + + return node; +} + +inline objects_list_node_t * +_objects_list_nth_node_for_element (objects_list_t * list, + long int n, + void *element) +{ + objects_list_node_t *node; + + if (n < 0) + { + node = list->last_node; + + ++n; + + while (node != NULL && n != 0) + { + if (objects_is_equal (objects_list_element_callbacks (list), element, node->element, list)) + ++n; + if (n != 0) + node = node->prev_in_list; + } + } + else + { + node = list->first_node; + + while (node != NULL && n != 0) + { + if (objects_is_equal (objects_list_element_callbacks (list), element, node->element, list)) + --n; + if (n != 0) + node = node->next_in_list; + } + } + + return node; +} + +inline objects_list_node_t * +_objects_list_enumerator_next_node (objects_list_enumerator_t * enumerator) +{ + objects_list_node_t *node; + + /* Remember ENUMERATOR's current node. */ + node = enumerator->node; + + /* If NODE is a real node, then we need to increment ENUMERATOR's + * current node to the next node in ENUMERATOR's list. */ + if (node != NULL) + { + if (enumerator->forward) + enumerator->node = enumerator->node->next_in_list; + else /* (!enumerator->forward) */ + enumerator->node = enumerator->node->prev_in_list; + } + + /* Send back NODE. */ + return node; +} + +/** Gathering statistics **/ + +size_t +objects_list_count (objects_list_t * list) +{ + return list->element_count; +} + +size_t +objects_list_capacity (objects_list_t * list) +{ + return list->element_count; +} + +int +objects_list_check (objects_list_t * list) +{ + return 0; +} + +int +objects_list_contains_element (objects_list_t * list, void *element) +{ + objects_list_enumerator_t enumerator; + void *member; + + objects_list_enumerator (list); + + while (objects_list_enumerator_next_element (&enumerator, &member)) + if (objects_compare (objects_list_element_callbacks (list), element, member, list)) + return 1; + + return 0; +} + +int +objects_list_is_empty (objects_list_t * list) +{ + return objects_list_count (list) == 0; +} + +/** Enumerating **/ + +objects_list_enumerator_t +objects_list_enumerator (objects_list_t * list) +{ + return objects_list_forward_enumerator (list); +} + +objects_list_enumerator_t +objects_list_forward_enumerator (objects_list_t * list) +{ + objects_list_enumerator_t enumerator; + + /* Update the access time. */ + _objects_list_set_access_time (list); + + /* Make sure ENUMERATOR knows its list. */ + enumerator.list = list; + + /* Start ENUMERATOR at LIST's first node. */ + enumerator.node = list->first_node; + + /* ENUMERATOR walks forward. */ + enumerator.forward = 1; + + return enumerator; +} + +objects_list_enumerator_t +objects_list_reverse_enumerator (objects_list_t * list) +{ + objects_list_enumerator_t enumerator; + + /* Make sure ENUMERATOR knows its list. */ + enumerator.list = list; + + /* Start ENUMERATOR at LIST's first node. */ + enumerator.node = list->last_node; + + /* ENUMERATOR walks backward. */ + enumerator.forward = 0; + + return enumerator; +} + +int +objects_list_enumerator_next_element (objects_list_enumerator_t * enumerator, + void **element) +{ + objects_list_node_t *node; + + /* Try and get the next node in the enumeration represented by + * ENUMERATOR. */ + node = _objects_list_enumerator_next_node (enumerator); + + if (node != NULL) + { + /* If NODE is real, then return the element it contains. */ + if (element != NULL) + *element = node->element; + + /* Indicate that the enumeration continues. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the ``bogus'' indicator. */ + if (element != NULL) + *element = objects_list_not_an_element_marker (enumerator->list); + + /* Indicate that the enumeration is over. */ + return 0; + } +} + +/** Searching **/ + +void * +objects_list_element (objects_list_t * list, void *element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, 0, element); + + if (node != NULL) + return node->element; + else + return objects_list_not_an_element_marker (list); +} + +void * +objects_list_nth_element (objects_list_t * list, long int n) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node (list, n); + + if (node != NULL) + return node->element; + else + return objects_list_not_an_element_marker (list); +} + +void * +objects_list_first_element (objects_list_t * list) +{ + if (list->first_node != NULL) + return list->first_node->element; + else + return objects_list_not_an_element_marker (list); +} + +void * +objects_list_last_element (objects_list_t * list) +{ + if (list->last_node != NULL) + return list->last_node->element; + else + return objects_list_not_an_element_marker (list); +} + +/** Obtaining elements **/ + +void ** +objects_list_all_elements (objects_list_t * list) +{ + objects_list_enumerator_t enumerator; + void **array; + size_t i; + + array = objects_calloc (objects_list_allocs (list), + objects_list_count (list) + 1, + sizeof (void *)); + + for (i = 0; objects_list_enumerator_next_element (&enumerator, array + i); ++i); + + return array; +} + +/** Adding elements **/ + +void * +objects_list_append_element (objects_list_t * list, void *element) +{ + return objects_list_at_index_insert_element (list, -1, element); +} + +void * +objects_list_append_element_if_absent (objects_list_t * list, void *element) +{ + return objects_list_at_index_insert_element_if_absent (list, -1, element); +} + +void * +objects_list_prepend_element (objects_list_t * list, void *element) +{ + return objects_list_at_index_insert_element (list, 0, element); +} + +void * +objects_list_prepend_element_if_absent (objects_list_t * list, void *element) +{ + return objects_list_at_index_insert_element_if_absent (list, 0, element); +} + +void * +objects_list_at_index_insert_element (objects_list_t * list, + long int n, + void *element) +{ + objects_list_node_t *anode, *bnode, *new_node, *node; + + node = _objects_list_nth_node (list, n); + new_node = _objects_list_new_node (list, element); + + if (new_node == NULL) + objects_abort (); + + if (n < 0) + { + if (node == NULL) + { + anode = NULL; + bnode = list->first_node; + } + else + /* (node != NULL) */ + { + anode = node; + bnode = node->next_in_list; + } + } + else + /* (n >= 0) */ + { + if (node == NULL) + { + anode = list->last_node; + bnode = NULL; + } + else + /* (node != NULL) */ + { + anode = node->prev_in_list; + bnode = node; + } + } + + new_node->prev_in_list = anode; + new_node->next_in_list = bnode; + + if (anode != NULL) + anode->next_in_list = new_node; + if (bnode != NULL) + bnode->prev_in_list = new_node; + + if (list->last_node == anode) + list->last_node = new_node; + if (list->first_node == bnode) + list->first_node = new_node; + + list->node_count += 1; + list->element_count += 1; + + return new_node->element; +} + +void * +objects_list_at_index_insert_element_if_absent (objects_list_t * list, + long int n, + void *element) +{ + if (!objects_list_contains_element (list, element)) + return objects_list_at_index_insert_element (list, n, element); + else + return objects_list_element (list, element); +} + +/** Removing elements **/ + +void +objects_list_remove_nth_occurrance_of_element (objects_list_t * list, + long int n, + void *element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, n, element); + + if (node != NULL) + { + _objects_list_remove_node_from_its_list (node); + _objects_list_free_node (list, node); + } + + return; +} + +void +objects_list_remove_element (objects_list_t * list, void *element) +{ + objects_list_remove_nth_occurrance_of_element (list, 0, element); + return; +} + +inline void +objects_list_remove_nth_element (objects_list_t * list, long int n) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node (list, n); + + if (node != NULL) + { + _objects_list_remove_node_from_its_list (node); + _objects_list_free_node (list, node); + } + + return; +} + +void +objects_list_remove_first_element (objects_list_t * list) +{ + objects_list_remove_nth_element (list, 0); + return; +} + +void +objects_list_remove_last_element (objects_list_t * list) +{ + objects_list_remove_nth_element (list, -1); + return; +} + +/** Emptying **/ + +void +objects_list_empty (objects_list_t * list) +{ + objects_list_enumerator_t enumerator; + objects_list_node_t *node; + + enumerator = objects_list_enumerator (list); + + while ((node = _objects_list_enumerator_next_node (&enumerator)) != NULL) + { + _objects_list_remove_node_from_its_list (node); + _objects_list_free_node (list, node); + } + + return; +} + +/** Replacing **/ + +void +objects_list_replace_nth_occurrance_of_element (objects_list_t * list, + long int n, + void *old_element, + void *new_element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, n, old_element); + + if (node != NULL) + { + objects_retain (objects_list_element_callbacks (list), new_element, list); + objects_release (objects_list_element_callbacks (list), node->element, list); + node->element = new_element; + } + + return; +} + +void +objects_list_replace_element (objects_list_t * list, + void *old_element, + void *new_element) +{ + objects_list_replace_nth_occurrance_of_element (list, 0, old_element, new_element); + return; +} + +void +objects_list_replace_nth_element (objects_list_t * list, + long int n, + void *new_element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node (list, n); + + if (node != NULL) + { + objects_retain (objects_list_element_callbacks (list), new_element, list); + objects_release (objects_list_element_callbacks (list), node->element, list); + node->element = new_element; + } + + return; +} + +void +objects_list_replace_first_element (objects_list_t * list, + void *new_element) +{ + objects_list_replace_nth_element (list, 0, new_element); + return; +} + +void +objects_list_replace_last_element (objects_list_t * list, + void *new_element) +{ + objects_list_replace_nth_element (list, -1, new_element); + return; +} + +/** Creating **/ + +objects_list_t * +objects_list_alloc_with_allocs (objects_allocs_t allocs) +{ + objects_list_t *list; + + list = _objects_list_alloc_with_allocs (allocs); + + return list; +} + +objects_list_t * +objects_list_alloc (void) +{ + return objects_list_alloc_with_allocs (objects_allocs_standard ()); +} + +objects_list_t * +objects_list (void) +{ + return objects_list_init (objects_list_alloc ()); +} + +objects_list_t * +objects_list_with_allocs (objects_allocs_t allocs) +{ + return objects_list_init (objects_list_alloc_with_allocs (allocs)); +} + +objects_list_t * +objects_list_with_allocs_with_callbacks (objects_allocs_t allocs, + objects_callbacks_t callbacks) +{ + return objects_list_init_with_callbacks (objects_list_alloc_with_allocs (allocs), + callbacks); +} + +objects_list_t * +objects_list_with_callbacks (objects_callbacks_t callbacks) +{ + return objects_list_init_with_callbacks (objects_list_alloc (), callbacks); +} + +objects_list_t * +objects_list_of_char_p (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_char_p); +} + +objects_list_t * +objects_list_of_int (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_int); +} + +objects_list_t * +objects_list_of_void_p (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_void_p); +} + +objects_list_t * +objects_list_of_owned_void_p (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_owned_void_p); +} + +objects_list_t * +objects_list_of_id (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_id); +} + +/** Initializing **/ + +objects_list_t * +objects_list_init (objects_list_t * list) +{ + return objects_list_init_with_callbacks (list, objects_callbacks_standard()); +} + +objects_list_t * +objects_list_init_with_callbacks (objects_list_t * list, objects_callbacks_t callbacks) +{ + if (list != NULL) + { + list->callbacks = callbacks; + list->element_count = 0; + list->node_count = 0; + list->first_node = NULL; + list->last_node = NULL; + } + + return list; +} + +objects_list_t * +objects_list_init_from_list (objects_list_t * list, objects_list_t * old_list) +{ + objects_list_enumerator_t enumerator; + void *element; + + if (list != NULL) + { + list->callbacks = objects_list_element_callbacks (old_list); + list->element_count = 0; + list->node_count = 0; + list->first_node = NULL; + list->last_node = NULL; + + if (old_list != NULL) + { + /* Get a forward enumerator for OLD_LIST. */ + enumerator = objects_list_forward_enumerator (old_list); + + /* Walk from the beginning to the end of OLD_LIST, and add each + * element to the end of LIST. */ + while (objects_list_enumerator_next_element (&enumerator, &element)) + objects_list_at_index_insert_element (list, -1, element); + } + } + + return list; +} + +/** Destroying **/ + +void +objects_list_dealloc (objects_list_t * list) +{ + /* Empty LIST out. */ + objects_list_empty (list); + + /* Get rid of LIST. */ + _objects_list_dealloc (list); + + return; +} + +/** Comparing **/ + +int +objects_list_is_equal_to_list (objects_list_t * list, objects_list_t * other_list) +{ + /* FIXME: Code this. */ + return 0; +} + +/** Concatenating **/ + +objects_list_t * +objects_list_append_list (objects_list_t * base_list, objects_list_t * suffix_list) +{ + return objects_list_at_index_insert_list (base_list, -1, suffix_list); +} + +objects_list_t * +objects_list_prepend_list (objects_list_t * base_list, objects_list_t * prefix_list) +{ + return objects_list_at_index_insert_list (base_list, 0, prefix_list); +} + +/* FIXME: I was lazy when I wrote this next one. It can easily be + * sped up. Do it. */ +objects_list_t * +objects_list_at_index_insert_list (objects_list_t * base_list, + long int n, + objects_list_t * infix_list) +{ + objects_list_enumerator_t enumerator; + void *element; + + if (n < 0) + enumerator = objects_list_forward_enumerator (infix_list); + else /* (n >= 0) */ + enumerator = objects_list_reverse_enumerator (infix_list); + + while (objects_list_enumerator_next_element (&enumerator, &element)) + objects_list_at_index_insert_element (base_list, n, element); + + return base_list; +} + +/** Copying **/ + +objects_list_t * +objects_list_copy (objects_list_t * old_list) +{ + /* FIXME: Should I be using `objects_allocs_standard()' or + * `objects_list_allocs(old_list)'? */ + return objects_list_copy_with_allocs (old_list, objects_list_allocs (old_list)); +} + +objects_list_t * +objects_list_copy_with_allocs (objects_list_t * old_list, objects_allocs_t allocs) +{ + objects_list_t *list; + + /* Allocate a new (low-level) copy of OLD_LIST. */ + list = _objects_list_copy_with_allocs (old_list, allocs); + + /* Fill it in. */ + return objects_list_init_from_list (list, old_list); +} + +/** Mapping **/ + +objects_list_t * +objects_list_map_elements (objects_list_t * list, + void *(*fcn) (void *, void *), + void *user_data) +{ + objects_list_enumerator_t enumerator; + objects_list_node_t *node; + + enumerator = objects_list_enumerator (list); + + while ((node = _objects_list_enumerator_next_node (&enumerator)) != NULL) + { + void *element; + + element = (*fcn) (node->element, user_data); + + /* NOTE: I'm accessing the callbacks directly for a little + * efficiency. */ + objects_retain (list->callbacks, element, list); + objects_release (list->callbacks, node->element, list); + + node->element = element; + } + + return list; +} + +/** Creating other collections from lists **/ + +objects_hash_t * +objects_hash_init_from_list (objects_hash_t * hash, objects_list_t * list) +{ + if (hash != NULL) + { + objects_list_enumerator_t enumerator; + void *element; + + /* Make a note of the callbacks for HASH. */ + hash->callbacks = objects_list_element_callbacks (list); + + /* Zero out the various counts. */ + hash->node_count = 0; + hash->bucket_count = 0; + hash->element_count = 0; + + /* Zero out the pointers. */ + hash->first_node = NULL; + hash->buckets = NULL; + + /* Resize HASH to the given CAPACITY. */ + objects_hash_resize (hash, objects_list_capacity (list)); + + /* Get an element enumerator for LIST. */ + enumerator = objects_list_enumerator (list); + + /* Add LIST's elements to HASH, one at a time. Note that if LIST + * contains multiple elements from the same equivalence class, it + * is indeterminate which will end up in HASH. But this shouldn't + * be a problem. */ + while (objects_list_enumerator_next_element (&enumerator, &element)) + objects_hash_add_element (hash, element); + } + + /* Return the newly initialized HASH. */ + return hash; +} + +// objects_chash_t * +// objects_chash_init_from_list (objects_chash_t * chash, objects_list_t * list) +// { +// if (chash != NULL) +// { +// objects_list_enumerator_t enumerator; +// void *element; +// +// /* Make a note of the callbacks for CHASH. */ +// chash->callbacks = objects_list_element_callbacks (list); +// +// /* Zero out the various counts. */ +// chash->node_count = 0; +// chash->bucket_count = 0; +// chash->element_count = 0; +// +// /* Zero out the pointers. */ +// chash->first_node = NULL; +// chash->buckets = NULL; +// +// /* Resize CHASH to the given CAPACITY. */ +// objects_chash_resize (chash, objects_list_capacity (list)); +// +// /* Get an element enumerator for LIST. */ +// enumerator = objects_list_enumerator (list); +// +// /* Add LIST's elements to CHASH, one at a time. Note that if LIST +// * contains multiple elements from the same equivalence class, it +// * is indeterminate which will end up in CHASH. But this shouldn't +// * be a problem. */ +// while (objects_list_enumerator_next_element (&enumerator, &element)) +// objects_chash_add_element (chash, element); +// } +// +// /* Return the newly initialized CHASH. */ +// return chash; +// } + diff --git a/Source/map.c b/Source/map.c new file mode 100644 index 000000000..db2288206 --- /dev/null +++ b/Source/map.c @@ -0,0 +1,1235 @@ +/* A (pretty good) map table implementation. + * Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: ??? ??? ?? ??:??:?? ??? 1993 + * Updated: Sat Feb 10 15:33:12 EST 1996 + * Serial: 96.02.10.03 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Background functions **/ + +objects_map_bucket_t * +_objects_map_pick_bucket_for_key (objects_map_t *map, + objects_map_bucket_t *buckets, + size_t bucket_count, + void *key) +{ + return buckets + (objects_hash (objects_map_key_callbacks (map), + key, map) % bucket_count); +} + +objects_map_bucket_t * +_objects_map_pick_bucket_for_node (objects_map_t * map, + objects_map_bucket_t * buckets, + size_t bucket_count, + objects_map_node_t * node) +{ + return buckets + (objects_hash (objects_map_key_callbacks (map), + node->key, map) % bucket_count); +} + +objects_map_bucket_t * +_objects_map_bucket_for_key (objects_map_t * map, void *key) +{ + return _objects_map_pick_bucket_for_key (map, map->buckets, + map->bucket_count, key); +} + +objects_map_bucket_t * +_objects_map_bucket_for_node (objects_map_t * map, objects_map_node_t * node) +{ + return _objects_map_pick_bucket_for_node (map, map->buckets, + map->bucket_count, node); +} + +void +_objects_map_link_node_into_bucket (objects_map_bucket_t * bucket, + objects_map_node_t * node) +{ + if (bucket->first_node != NULL) + bucket->first_node->prev_in_bucket = node; + + node->next_in_bucket = bucket->first_node; + + bucket->first_node = node; + + return; +} + +void +_objects_map_unlink_node_from_its_bucket (objects_map_node_t * node) +{ + if (node == node->bucket->first_node) + node->bucket->first_node = node->next_in_bucket; + + if (node->prev_in_bucket != NULL) + node->prev_in_bucket->next_in_bucket = node->next_in_bucket; + if (node->next_in_bucket != NULL) + node->next_in_bucket->prev_in_bucket = node->prev_in_bucket; + + node->prev_in_bucket = node->next_in_bucket = NULL; + + return; +} + +void +_objects_map_link_node_into_map (objects_map_t * map, objects_map_node_t * node) +{ + if (map->first_node != NULL) + map->first_node->prev_in_map = node; + + node->next_in_map = map->first_node; + + map->first_node = node; + + return; +} + +void +_objects_map_unlink_node_from_its_map (objects_map_node_t * node) +{ + if (node == node->map->first_node) + node->map->first_node = node->next_in_map; + + if (node->prev_in_map != NULL) + node->prev_in_map->next_in_map = node->next_in_map; + if (node->next_in_map != NULL) + node->next_in_map->prev_in_map = node->prev_in_map; + + node->prev_in_map = node->next_in_map = NULL; + + return; +} + +void +_objects_map_add_node_to_bucket (objects_map_bucket_t * bucket, objects_map_node_t * node) +{ + if (bucket != NULL) + { + _objects_map_link_node_into_bucket (bucket, node); + + node->bucket = bucket; + + bucket->node_count += 1; + bucket->element_count += 1; + } + + return; +} + +void +_objects_map_add_node_to_its_bucket (objects_map_t * map, objects_map_node_t * node) +{ + _objects_map_add_node_to_bucket (_objects_map_bucket_for_node (map, node), node); + return; +} + +void +_objects_map_add_node_to_map (objects_map_t * map, objects_map_node_t * node) +{ + if (map != NULL) + { + _objects_map_add_node_to_its_bucket (map, node); + + _objects_map_link_node_into_map (map, node); + + node->map = map; + + map->node_count += 1; + map->element_count += 1; + } + + return; +} + +void +_objects_map_remove_node_from_its_bucket (objects_map_node_t * node) +{ + if (node->bucket != NULL) + { + node->bucket->node_count -= 1; + node->bucket->element_count -= 1; + + _objects_map_unlink_node_from_its_bucket (node); + } + + return; +} + +void +_objects_map_remove_node_from_its_map (objects_map_node_t * node) +{ + if (node->map != NULL) + { + node->map->node_count -= 1; + node->map->element_count -= 1; + + _objects_map_unlink_node_from_its_map (node); + } + + _objects_map_remove_node_from_its_bucket (node); + + return; +} + +objects_map_bucket_t * +_objects_map_new_buckets (objects_map_t * map, size_t bucket_count) +{ + return (objects_map_bucket_t *) objects_calloc (objects_map_allocs (map), + bucket_count, + sizeof (objects_map_bucket_t)); +} + +void +_objects_map_free_buckets (objects_map_t * map, objects_map_bucket_t * buckets) +{ + if (buckets != NULL) + objects_free (objects_map_allocs (map), buckets); + return; +} + +void +_objects_map_remangle_buckets (objects_map_t * map, + objects_map_bucket_t * old_buckets, + size_t old_bucket_count, + objects_map_bucket_t * new_buckets, + size_t new_bucket_count) +{ + size_t i; + objects_map_node_t *node; + + for (i = 0; i < old_bucket_count; i++) + { + while ((node = old_buckets[i].first_node) != NULL) + { + _objects_map_remove_node_from_its_bucket (node); + _objects_map_add_node_to_bucket (_objects_map_pick_bucket_for_node (map, + new_buckets, + new_bucket_count, + node), + node); + } + } + + /* And that's that. */ + return; +} + +objects_map_node_t * +_objects_map_new_node (objects_map_t * map, void *key, void *value) +{ + objects_map_node_t *node; + + /* Allocate the space for a new node. */ + node = (objects_map_node_t *) objects_malloc (objects_map_allocs (map), + sizeof (objects_map_node_t)); + + if (node != NULL) + { + /* Retain KEY and VALUE. (They're released below in + * `_objects_map_free_node()'.) */ + objects_retain (objects_map_key_callbacks (map), key, map); + objects_retain (objects_map_value_callbacks (map), value, map); + + /* Remember KEY and VALUE. */ + node->key = key; + node->value = value; + + /* Zero out the various pointers. */ + node->map = NULL; + node->bucket = NULL; + node->next_in_bucket = NULL; + node->next_in_map = NULL; + node->prev_in_bucket = NULL; + node->prev_in_map = NULL; + } + + return node; +} + +void +_objects_map_free_node (objects_map_node_t * node) +{ + if (node != NULL) + { + objects_map_t *map; + + /* Remember NODE's map. */ + map = node->map; + + /* Release KEY and VALUE. (They're retained above in + * `_objects_map_new_node()'.) */ + objects_release (objects_map_key_callbacks (map), node->key, map); + objects_release (objects_map_value_callbacks (map), node->value, map); + + /* Actually free the space map aside for NODE. */ + objects_free (objects_map_allocs (map), node); + } + + /* And just return. */ + return; +} + +objects_map_node_t * +_objects_map_node_for_key (objects_map_t * map, void *key) +{ + objects_map_bucket_t *bucket; + objects_map_node_t *node; + + /* Find the bucket in which the node for KEY would be. */ + bucket = _objects_map_bucket_for_key (map, key); + + /* Run through the nodes in BUCKET until we find one whose element + * matches ELEMENT. */ + for (node = bucket->first_node; + (node != NULL) && !objects_is_equal (objects_map_key_callbacks (map), + key, + node->key, + map); + node = node->next_in_bucket); + + /* Note that if none of the nodes' elements matches ELEMENT, then we + * naturally return `NULL'. */ + return node; +} + +/** Resizing **/ + +size_t +objects_map_resize (objects_map_t * map, size_t new_capacity) +{ + objects_map_bucket_t *new_buckets; + + /* Round NEW_CAPACITY up to the next power of two. */ + new_capacity = objects_next_power_of_two (new_capacity); + + /* Make a new map of buckets. */ + new_buckets = _objects_map_new_buckets (map, new_capacity); + + if (new_buckets != NULL) + { + _objects_map_remangle_buckets (map, + map->buckets, + map->bucket_count, + new_buckets, + new_capacity); + + _objects_map_free_buckets (map, map->buckets); + + map->buckets = new_buckets; + map->bucket_count = new_capacity; + } + + /* Return the new capacity. */ + return map->bucket_count; +} + +size_t +objects_map_rightsize (objects_map_t * map) +{ + /* FIXME: Now, this is a guess, based solely on my intuition. If + * anyone knows of a better ratio (or other test, for that matter) + * and can provide evidence of its goodness, please get in touch + * with me, Albin L. Jones . */ + + if (3 * map->node_count > 4 * map->bucket_count) + { + return objects_map_resize (map, map->bucket_count + 1); + } + else + { + return map->bucket_count; + } +} + +/** Statistics **/ + +size_t +objects_map_count (objects_map_t * map) +{ + return map->element_count; +} + +size_t +objects_map_capacity (objects_map_t * map) +{ + return map->bucket_count; +} + +int +objects_map_check (objects_map_t * map) +{ + return 0; +} + +int +objects_map_is_empty (objects_map_t * map) +{ + return objects_map_count (map) == 0; +} + +/** Searching **/ + +/* FIXME: Note that the following function should have special + * consideration for the case where ELEMENT is the `not an element + * marker' for DICT. There is a problem, though; because of the union + * nature of the `void *' type, a simple comparison cannot be + * made. One cannot seem to be able to compare objects of union type; + * one must compare components of a union type. Now I could just + * check all the different components of ELEMENT versus that of the + * `not an element marker' for DICT, but that would be a collossal + * waste of time. Would it be enough to check, say, the `long double' + * (or whichever is the largest) component? It is unclear to me. But + * this should do for now. It does mean, though, that having members + * of the `not an element marker' equivalence class could throw things + * off if one checks the membership of DICT's `not an element marker' + * in DICT. */ + +int +objects_map_contains_key (objects_map_t * map, void *key) +{ + objects_map_node_t *node; + + node = _objects_map_node_for_key (map, key); + + if (node != NULL) + return 1; + else + return 0; +} + +int +objects_map_contains_value (objects_map_t * map, void *value) +{ + return 0; +} + +int +objects_map_key_and_value_at_key (objects_map_t * map, + void **old_key, + void **value, + void *key) +{ + objects_map_node_t *node; + + /* Try and find the node for KEY. */ + node = _objects_map_node_for_key (map, key); + + if (node != NULL) + { + if (old_key != NULL) + *old_key = node->key; + if (value != NULL) + *value = node->value; + return 1; + } + else + { + if (old_key != NULL) + *old_key = objects_map_not_a_key_marker (map); + if (value != NULL) + *value = objects_map_not_a_value_marker (map); + return 0; + } +} + +void * +objects_map_key (objects_map_t * map, void *key) +{ + void *old_key; + + objects_map_key_and_value_at_key (map, &old_key, NULL, key); + + return old_key; +} + +void * +objects_map_value_at_key (objects_map_t * map, void *key) +{ + void *value; + + objects_map_key_and_value_at_key (map, NULL, &value, key); + + return value; +} + +void ** +objects_map_all_keys_and_values (objects_map_t * map) +{ + size_t j; + void **array; + objects_map_enumerator_t enumerator; + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `objects_free(objects_map_allocs(DICT), ARRAY)' */ + array = (void **) objects_calloc (objects_map_allocs (map), + 2 * (map->node_count + 1), + sizeof (void *)); + + /* ENUMERATOR is an enumerator for DICT. */ + enumerator = objects_map_enumerator (map); + + /* Now we enumerate through the elements of DICT, adding them + * one-by-one to ARRAY. Note that this automagically puts the ``not + * a key/value markers'' at the end of ARRAY. */ + for (j = 0; + objects_map_enumerator_next_key_and_value (&enumerator, + array + j, + array + j + 1); + j += 2); + + /* And we're done. */ + return array; +} + +void ** +objects_map_all_keys (objects_map_t * map) +{ + size_t j; + void **array; + objects_map_enumerator_t enumerator; + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `objects_free(objects_map_allocs(DICT), ARRAY)' */ + array = (void **) objects_calloc (objects_map_allocs (map), + map->node_count + 1, + sizeof (void *)); + + /* ENUMERATOR is an enumerator for DICT. */ + enumerator = objects_map_enumerator (map); + + /* Now we enumerate through the elements of DICT, adding them + * one-by-one to ARRAY. Note that this automagically puts the ``not + * a key marker'' at the end of ARRAY. */ + for (j = 0; objects_map_enumerator_next_key (&enumerator, array + j); j++); + + /* And we're done. */ + return array; +} + +void ** +objects_map_all_values (objects_map_t * map) +{ + size_t j; + void **array; + objects_map_enumerator_t enumerator; + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `objects_free(objects_map_allocs(DICT), ARRAY)' */ + array = (void **) objects_calloc (objects_map_allocs (map), + map->node_count + 1, + sizeof (void *)); + + /* ENUMERATOR is an enumerator for DICT. */ + enumerator = objects_map_enumerator (map); + + /* Now we enumerate through the elements of DICT, adding them + * one-by-one to ARRAY. Note that this automagically puts the ``not + * a value marker'' at the end of ARRAY. */ + for (j = 0; objects_map_enumerator_next_value (&enumerator, array + j); j++); + + /* And we're done. */ + return array; +} + +/** Enumerating **/ + +/* WARNING: You should not alter a map while an enumeration is + * in progress. The results of doing so are reasonably unpremapable. + * With that in mind, read the following warnings carefully. But + * remember, DON'T MESS WITH A DICT WHILE YOU'RE ENUMERATING IT. */ + +/* IMPORTANT WARNING: Dict enumerators, as I have map them up, have a + * wonderous property. Namely, that, while enumerating, one may add + * new elements (i.e., new nodes) to the map while an enumeration is + * in progress (i.e., after `objects_map_enumerator()' has been called), and + * the enumeration remains the same. */ + +/* WARNING: The above warning should not, in any way, be taken as + * assurance that this property of map enumerators will be preserved + * in future editions of the library. I'm still thinking about + * this. */ + +/* IMPORTANT WARNING: Enumerators have yet another wonderous property. + * Once a node has been returned by `_map_next_node()', it may be + * removed from the map without effecting the rest of the current + * enumeration. For example, to clean all of the nodes out of a map, + * the following code would work: + * + * void + * empty_my_map(objects_map_t *map) + * { + * objects_map_enuemrator_t enumerator = objects_map_enumerator(map); + * objects_map_node_t *node; + * + * while ((node = _objects_map_next_node(&enumerator)) != NULL) + * { + * _objects_map_remove_node_from_its_map(node); + * _objects_map_free_node(node); + * } + * + * return; + * } + * + * (In fact, this is the code currently being used below in the + * function `map_delete_all_elements()'.) But again, this is not to be + * taken as an assurance that this behaviour will persist in future + * versions of the library. */ + +/* EXTREMELY IMPORTANT WARNING: The purpose of this warning is point + * out that, at this time, various (i.e., many) functions depend on + * the behaviours outlined above. So be prepared for some serious + * breakage when you go fudging around with these things. */ + +objects_map_enumerator_t +objects_map_enumerator (objects_map_t * map) +{ + objects_map_enumerator_t enumerator; + + /* Make sure ENUMERATOR knows its mapionary. */ + enumerator.map = map; + + /* Start ENUMERATOR at DICT's first node. */ + enumerator.node = map->first_node; + + return enumerator; +} + +objects_map_node_t * +_objects_map_enumerator_next_node (objects_map_enumerator_t * enumerator) +{ + objects_map_node_t *node; + + /* Remember ENUMERATOR's current node. */ + node = enumerator->node; + + /* If NODE is a real node, then we need to increment ENUMERATOR's + * current node to the next node in ENUMERATOR's map. */ + if (node != NULL) + enumerator->node = enumerator->node->next_in_map; + + /* Send back NODE. */ + return node; +} + +int +objects_map_enumerator_next_key_and_value (objects_map_enumerator_t * enumerator, + void **key, + void **value) +{ + objects_map_node_t *node; + + /* Try and get the next node in the enumeration represented by + * ENUMERATOR. */ + node = _objects_map_enumerator_next_node (enumerator); + + if (node != NULL) + { + /* If NODE is real, then return the key and value it contains. */ + if (key != NULL) + *key = node->key; + if (value != NULL) + *value = node->value; + + /* Indicate that the enumeration continues. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the ``bogus'' indicators. */ + if (key != NULL) + *key = objects_map_not_a_key_marker (enumerator->map); + if (value != NULL) + *value = objects_map_not_a_value_marker (enumerator->map); + + /* Indicate that the enumeration is over. */ + return 0; + } +} + +int +objects_map_enumerator_next_key (objects_map_enumerator_t * enumerator, + void **key) +{ + return objects_map_enumerator_next_key_and_value (enumerator, key, NULL); +} + +int +objects_map_enumerator_next_value (objects_map_enumerator_t * enumerator, + void **value) +{ + return objects_map_enumerator_next_key_and_value (enumerator, NULL, value); +} + +/** Adding **/ + +void * +objects_map_at_key_put_value_known_absent (objects_map_t * map, + void *key, + void *value) +{ + objects_map_node_t *node; + + /* Resize MAP if needed. */ + objects_map_rightsize (map); + + /* Make NODE a node which holds KEY and VALUE. */ + node = _objects_map_new_node (map, key, value); + + if (node != NULL) + { + /* NODE is real, so stick it in MAP. */ + _objects_map_add_node_to_map (map, node); + + /* Return ELEMENT, just in case someone wants to look at it. */ + return key; + } + else + { + /* NODE would be `NULL' only if an allocation failed, but it's + * worth checking and returning an error if appropriate, I guess. + * It just seems like the kind thing to do. */ + return objects_map_not_a_key_marker (map); + } +} + +void * +objects_map_at_key_put_value (objects_map_t * map, + void *key, + void *value) +{ + objects_map_node_t *node; + + /* First, we check for KEY in MAP. */ + node = _objects_map_node_for_key (map, key); + + if (node != NULL) + { + objects_retain (objects_map_value_callbacks (map), value, map); + objects_release (objects_map_value_callbacks (map), node->value, map); + node->value = value; + return node->key; + } + else + { + /* KEY isn't in MAP, so we can add it with impunity. */ + return objects_map_at_key_put_value_known_absent (map, key, value); + } +} + +void * +objects_map_at_key_put_value_if_absent (objects_map_t * map, + void *key, + void *value) +{ + objects_map_node_t *node; + + /* Look for a node with KEY in it. */ + node = _objects_map_node_for_key (map, key); + + if (node != NULL) + { + /* If NODE is real, then KEY is already in MAP. So we return the + * member key of MAP which is ``equal to'' KEY. */ + return node->key; + } + else + { + /* If NODE isn't real, then we may add KEY (and VALUE) to MAP + * without worrying too much. */ + return objects_map_at_key_put_value_known_absent (map, key, value); + } +} + +/** Removing **/ + +void +objects_map_remove_key (objects_map_t * map, void *key) +{ + objects_map_node_t *node; + + /* Look for a node with KEY in it. */ + node = _objects_map_node_for_key (map, key); + + if (node != NULL) + { + /* If NODE is real, then we've got something to remove. */ + _objects_map_remove_node_from_its_map (node); + _objects_map_free_node (node); + } + + return; +} + +/** Emptying **/ + +void +objects_map_empty (objects_map_t * map) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + /* Get an element enumerator for MAP. */ + enumerator = objects_map_enumerator (map); + + /* Just step through the nodes of MAP and wipe them out, one after + * another. Don't try this at home, kids! */ + while ((node = _objects_map_enumerator_next_node (&enumerator)) != NULL) + { + _objects_map_remove_node_from_its_map (node); + _objects_map_free_node (node); + } + + /* And return. */ + return; +} + +/** Creating **/ + +objects_map_t * +objects_map_alloc_with_allocs (objects_allocs_t allocs) +{ + objects_map_t *map; + + map = _objects_map_alloc_with_allocs (allocs); + + return map; +} + +objects_map_t * +objects_map_alloc (void) +{ + return objects_map_alloc_with_allocs (objects_allocs_standard ()); +} + +objects_map_t * +objects_map_with_allocs (objects_allocs_t allocs) +{ + return objects_map_init (objects_map_alloc_with_allocs (allocs)); +} + +objects_map_t * +objects_map_with_allocs_with_callbacks (objects_allocs_t allocs, + objects_callbacks_t key_callbacks, + objects_callbacks_t value_callbacks) +{ + return objects_map_init_with_callbacks (objects_map_alloc_with_allocs (allocs), + key_callbacks, + value_callbacks); +} + +objects_map_t * +objects_map_with_callbacks (objects_callbacks_t key_callbacks, + objects_callbacks_t value_callbacks) +{ + return objects_map_init_with_callbacks (objects_map_alloc (), + key_callbacks, + value_callbacks); +} + +objects_map_t * +objects_map_of_char_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_char_p, + objects_callbacks_for_char_p); +} + +objects_map_t * +objects_map_of_char_p_to_int (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_char_p, + objects_callbacks_for_int); +} + +objects_map_t * +objects_map_of_char_p_to_void_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_char_p, + objects_callbacks_for_void_p); +} + +objects_map_t * +objects_map_of_char_p_to_id (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_char_p, + objects_callbacks_for_id); +} + +objects_map_t * +objects_map_of_void_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_void_p, + objects_callbacks_for_void_p); +} + +objects_map_t * +objects_map_of_int (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_int, + objects_callbacks_for_int); +} + +objects_map_t * +objects_map_of_int_to_char_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_int, + objects_callbacks_for_char_p); +} + +objects_map_t * +objects_map_of_int_to_void_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_int, + objects_callbacks_for_void_p); +} + +objects_map_t * +objects_map_of_int_to_id (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_int, + objects_callbacks_for_id); +} + +objects_map_t * +objects_map_of_id (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_id, + objects_callbacks_for_id); +} + +objects_map_t * +objects_map_of_id_to_int (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_id, + objects_callbacks_for_int); +} + +objects_map_t * +objects_map_of_id_to_char_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_id, + objects_callbacks_for_char_p); +} + +objects_map_t * +objects_map_of_id_to_void_p (void) +{ + return objects_map_with_callbacks (objects_callbacks_for_id, + objects_callbacks_for_void_p); +} + +/** Initializing **/ + +objects_map_t * +objects_map_init_with_callbacks (objects_map_t * map, + objects_callbacks_t key_callbacks, + objects_callbacks_t value_callbacks) +{ + if (map != NULL) + { + size_t capacity = 10; + + /* Make a note of the callbacks and allocs for MAP. */ + map->key_callbacks = objects_callbacks_standardize (key_callbacks); + map->value_callbacks = objects_callbacks_standardize (value_callbacks); + + /* Zero out the various counts. */ + map->node_count = 0; + map->bucket_count = 0; + map->element_count = 0; + + /* Zero out the pointers. */ + map->first_node = NULL; + map->buckets = NULL; + + /* Resize MAP to the given CAPACITY. */ + objects_map_resize (map, capacity); + } + + /* Return the newly initialized MAP. */ + return map; +} + +objects_map_t * +objects_map_init (objects_map_t * map) +{ + return objects_map_init_with_callbacks (map, + objects_callbacks_standard(), + objects_callbacks_standard()); +} + +objects_map_t * +objects_map_init_from_map (objects_map_t * map, objects_map_t * old_map) +{ + objects_map_enumerator_t enumerator; + void *key; + void *value; + + /* Initialize MAP. */ + objects_map_init_with_callbacks (map, + objects_map_key_callbacks (old_map), + objects_map_value_callbacks (old_map)); + + objects_map_resize (map, objects_map_capacity (old_map)); + + /* Get an enumerator for OLD_MAP. */ + enumerator = objects_map_enumerator (old_map); + + /* Step through the pairs of OLD_MAP, adding each in turn to + * MAP. */ + while (objects_map_enumerator_next_key_and_value (&enumerator, &key, &value)) + objects_map_at_key_put_value (map, key, value); + + /* Return the newly initialized MAP. */ + return map; +} + +/** Destroying **/ + +void +objects_map_dealloc (objects_map_t * map) +{ + if (map != NULL) + { + /* Remove all of MAP's elements. */ + objects_map_empty (map); + + /* Free up the bucket array. */ + _objects_map_free_buckets (map, map->buckets); + + /* And finally, free up the space that MAP itself takes up. */ + _objects_map_dealloc (map); + } + + return; +} + +/** Replacing **/ + +void +objects_map_replace_key (objects_map_t * map, + void *key) +{ + objects_map_node_t *node; + + node = _objects_map_node_for_key (map, key); + + if (node != NULL) + { + objects_retain (objects_map_key_callbacks (map), key, map); + objects_release (objects_map_key_callbacks (map), node->key, map); + node->key = key; + } + + return; +} + +/** Comparing **/ + +int +objects_map_contains_map (objects_map_t * map1, objects_map_t * map2) +{ + objects_map_enumerator_t enumerator; + void *key; + + enumerator = objects_map_enumerator (map2); + + while (objects_map_enumerator_next_key (&enumerator, &key)) + if (!objects_map_contains_key (map1, key)) + return 0; + + return 1; +} + +int +objects_map_intersects_map (objects_map_t * map1, objects_map_t * map2) +{ + objects_map_enumerator_t enumerator; + void *key; + + enumerator = objects_map_enumerator (map1); + + while (objects_map_enumerator_next_key (&enumerator, &key)) + if (objects_map_contains_key (map2, key)) + return 1; + + return 0; +} + +int +objects_map_is_equal_to_map (objects_map_t * map1, objects_map_t * map2) +{ + size_t a, b; + + /* Count MAP1 and MAP2. */ + a = objects_map_count (map1); + b = objects_map_count (map2); + + /* Check the counts. */ + if (a != b) + return 0; + + /* If the counts match, then we do an element by element check. */ + if (!objects_map_contains_map (map1, map2) + || !objects_map_contains_map (map2, map1)) + return 0; + + /* If we made it this far, MAP1 and MAP2 are the same. */ + return 1; +} + +/** Copying **/ + +objects_map_t * +objects_map_copy_with_allocs (objects_map_t * old_map, objects_allocs_t new_allocs) +{ + objects_map_t *new_map; + + /* Alloc the NEW_MAP. */ + new_map = _objects_map_copy_with_allocs (old_map, new_allocs); + + /* Initialize the NEW_MAP. */ + objects_map_init_from_map (new_map, old_map); + + /* And return the copy. */ + return new_map; +} + +objects_map_t * +objects_map_copy (objects_map_t * old_map) +{ + /* FIXME: Should I be using `objects_allocs_standard()', or + * `objects_map_allocs(old_map)'? */ + return objects_map_copy_with_allocs (old_map, objects_map_allocs (old_map)); +} + +/** Mapping **/ + +objects_map_t * +objects_map_map_keys (objects_map_t * map, + void *(*fcn) (void *, void *), + void *user_data) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + enumerator = objects_map_enumerator (map); + + while ((node = _objects_map_enumerator_next_node (&enumerator)) != NULL) + { + void *key; + + key = (*fcn) (node->key, user_data); + + objects_retain (objects_map_key_callbacks (map), key, map); + objects_release (objects_map_key_callbacks (map), node->key, map); + node->key = key; + } + + return map; +} + +objects_map_t * +objects_map_map_values (objects_map_t * map, + void *(*fcn) (void *, void *), + void *user_data) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + enumerator = objects_map_enumerator (map); + + while ((node = _objects_map_enumerator_next_node (&enumerator)) != NULL) + { + void *value; + + value = (fcn) (node->value, user_data); + + objects_retain (objects_map_value_callbacks (map), value, map); + objects_release (objects_map_value_callbacks (map), node->value, map); + node->value = value; + } + + return map; +} + +/** Miscellaneous **/ + +objects_map_t * +objects_map_intersect_map (objects_map_t * map, objects_map_t * other_map) +{ + objects_map_enumerator_t enumerator; + void *key; + + enumerator = objects_map_enumerator (map); + + while (objects_map_enumerator_next_key (&enumerator, &key)) + if (!objects_map_contains_key (other_map, key)) + objects_map_remove_key (map, key); + + return map; +} + +objects_map_t * +objects_map_minus_map (objects_map_t * map, objects_map_t * other_map) +{ + objects_map_enumerator_t enumerator; + void *key; + + enumerator = objects_map_enumerator (other_map); + + while (objects_map_enumerator_next_key (&enumerator, &key)) + { + objects_map_remove_key (map, key); + } + + return map; +} + +objects_map_t * +objects_map_union_map (objects_map_t * map, objects_map_t * other_map) +{ + objects_map_enumerator_t enumerator; + void *key; + void *value; + + enumerator = objects_map_enumerator (other_map); + + while (objects_map_enumerator_next_key_and_value (&enumerator, &key, &value)) + { + objects_map_at_key_put_value_if_absent (map, key, value); + } + + return map; +} + diff --git a/Source/md5.c b/Source/md5.c new file mode 100644 index 000000000..e65724a3d --- /dev/null +++ b/Source/md5.c @@ -0,0 +1,368 @@ + +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995 Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Ulrich Drepper . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#if STDC_HEADERS +#include +#include +#else +#ifndef HAVE_MEMCPY +#define memcpy(d, s, n) bcopy ((s), (d), (n)) +#endif +#endif + +#include "md5.h" + +#ifdef WORDS_BIGENDIAN +#define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +#define SWAP(n) (n) +#endif + + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = +{0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (ctx) + struct md5_ctx *ctx; +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result must + be in little endian byte order. */ +void * +md5_read_ctx (ctx, resbuf) + const struct md5_ctx *ctx; + void *resbuf; +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (stream, resblock) + FILE *stream; + void *resblock; +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +#define BLOCKSIZE 4096 + struct md5_ctx ctx; + md5_uint32 len[2]; + char buffer[BLOCKSIZE + 72]; + size_t pad, sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + len[0] = 0; + len[1] = 0; + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do + { + n = fread (buffer, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror (stream)) + return 1; + + /* RFC 1321 specifies the possible length of the file up to 2^64 bits. + Here we only compute the number of bytes. Do a double word + increment. */ + len[0] += sum; + if (len[0] < sum) + ++len[1]; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + /* We can copy 64 byte because the buffer is always big enough. FILLBUF + contains the needed bits. */ + memcpy (&buffer[sum], fillbuf, 64); + + /* Compute amount of padding bytes needed. Alignment is done to + (N + PAD) % 64 == 56 + There is always at least one byte padded. I.e. even the alignment + is correctly aligned 64 padding bytes are added. */ + pad = sum & 63; + pad = pad >= 56 ? 64 + 56 - pad : 56 - pad; + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) & buffer[sum + pad] = SWAP (len[0] << 3); + *(md5_uint32 *) & buffer[sum + pad + 4] = SWAP ((len[1] << 3) + | (len[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (buffer, sum + pad + 8, &ctx); + + /* Construct result in desired memory. */ + md5_read_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (buffer, len, resblock) + const char *buffer; + size_t len; + void *resblock; +{ + struct md5_ctx ctx; + char restbuf[64 + 72]; + size_t blocks = len & ~63; + size_t pad, rest; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_block (buffer, blocks, &ctx); + + /* REST bytes are not processed yet. */ + rest = len - blocks; + /* Copy to own buffer. */ + memcpy (restbuf, &buffer[blocks], rest); + /* Append needed fill bytes at end of buffer. We can copy 64 byte + because the buffer is always big enough. */ + memcpy (&restbuf[rest], fillbuf, 64); + + /* PAD bytes are used for padding to correct alignment. Note that + always at least one byte is padded. */ + pad = rest >= 56 ? 64 + 56 - rest : 56 - rest; + + /* Put length of buffer in *bits* in last eight bytes. */ + *(md5_uint32 *) & restbuf[rest + pad] = (md5_uint32) SWAP (len << 3); + *(md5_uint32 *) & restbuf[rest + pad + 4] = (md5_uint32) SWAP (len >> 29); + + /* Process last bytes. */ + md5_process_block (restbuf, rest + pad + 8, &ctx); + + /* Put result in desired memory area. */ + return md5_read_ctx (&ctx, resblock); +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/Source/md5.h b/Source/md5.h new file mode 100644 index 000000000..ca3424de6 --- /dev/null +++ b/Source/md5.h @@ -0,0 +1,115 @@ +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. + Copyright (C) 1995 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _MD5_H +#define _MD5_H + +#include + +#if defined HAVE_LIMITS_H || _LIBC +#include +#endif + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#if defined __STDC__ && __STDC__ +#define UINT_MAX_32_BITS 4294967295U +#else +#define UINT_MAX_32_BITS 0xFFFFFFFF +#endif + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have ) have 64+-bit integral types. */ + +#ifndef UINT_MAX +#define UINT_MAX UINT_MAX_32_BITS +#endif + +#if UINT_MAX == UINT_MAX_32_BITS +typedef unsigned int md5_uint32; +#else +#if USHRT_MAX == UINT_MAX_32_BITS +typedef unsigned short md5_uint32; +#else +#if ULONG_MAX == UINT_MAX_32_BITS +typedef unsigned long md5_uint32; +#else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ +"Cannot determine unsigned 32-bit data type." +#endif +#endif +#endif + +#undef __P +#if defined (__STDC__) && __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx + { + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + }; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void md5_init_ctx __P ((struct md5_ctx * ctx)); + +/* Starting with the result of former calls of this function (or the + initialzation function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +void md5_process_block __P ((const void *buffer, size_t len, + struct md5_ctx * ctx)); + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. */ +void *md5_read_ctx __P ((const struct md5_ctx * ctx, void *resbuf)); + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int md5_stream __P ((FILE * stream, void *resblock)); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void *md5_buffer __P ((const char *buffer, size_t len, void *resblock)); + +#endif diff --git a/Source/number.c b/Source/number.c new file mode 100644 index 000000000..02436eee0 --- /dev/null +++ b/Source/number.c @@ -0,0 +1,59 @@ +/* Structure counters and functions for getting at them. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sun Dec 3 00:23:13 EST 1995 + * Updated: Sat Feb 10 13:52:15 EST 1996 + * Serial: 96.02.10.01 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +size_t ___objects_number_allocated = 0; +size_t ___objects_number_deallocated = 0; +size_t ___objects_number_serial = 0; + +/**** Function Implementations ***********************************************/ + +/* Returns the number of Libobjects structures allocated. */ +size_t +objects_number_allocated(void) +{ + return ___objects_number_allocated; +} + +/* Returns the number of Libobjects structures deallocated. */ +size_t +objects_number_deallocated(void) +{ + return ___objects_number_deallocated; +} + +/* Returns the next serial number to be handed out. */ +size_t +objects_number_serial(void) +{ + return ___objects_number_serial; +} + diff --git a/Source/x-basics.c.in b/Source/x-basics.c.in new file mode 100644 index 000000000..8cf4eb53d --- /dev/null +++ b/Source/x-basics.c.in @@ -0,0 +1,276 @@ +/* Basic functions for @XX@ structures. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Tue Dec 12 12:33:01 EST 1995 + * Updated: Sat Feb 10 09:08:35 EST 1996 + * Serial: 96.02.10.01 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +#define __@XX@__ 1 + +/**** Function Implementations ***********************************************/ + +/** Magic **/ + +/* Returns XX's magic number. */ +int +objects_@XX@_magic (objects_@XX@_t *xx) +{ + return xx->magic; +} + +/** Allocs **/ + +/* Returns the allocs used to create and maintain XX. */ +objects_allocs_t +objects_@XX@_allocs (objects_@XX@_t *xx) +{ + return xx->allocs; +} + +/** Names **/ + +/* Returns the name that was given to XX. */ +const char * +objects_@XX@_name (objects_@XX@_t *xx) +{ + return xx->name; +} + +/* Gives XX a name. Space is allocated, and the contents of the + * NUL-terminated NAME are copied. Deallocating XX frees up the + * space. I.e., it is not the responsibility of the programmer to + * keep track of space allocated for this procedure. */ +const char * +objects_@XX@_set_name (objects_@XX@_t *xx, const char *name) +{ + /* Figure out how much space we need. */ + size_t len = strlen (name); + char *old_name = xx->name; + + /* Allocate space for the new name. (Don't forget the extra space + * for the terminating `NUL'. */ + xx->name = (char *) objects_malloc (xx->allocs, len + 1); + + /* If the allocation was successful, copy the new name over and get + * rid of the old name. */ + if (xx->name != NULL) + { + /* Copy over the new name. */ + strncpy (xx->name, name, len); + + /* Free up any space we set aside for any older name. */ + if (old_name != NULL) + objects_free (xx->allocs, old_name); + } + + return xx->name; +} + +/* Takes away XX's name. */ +void +objects_@XX@_unset_name (objects_@XX@_t *xx) +{ + /* Free it. */ + if (xx->name != NULL) + objects_free (xx->allocs, xx->name); + + /* Reset it. */ + xx->name = NULL; + + return; +} + +/** Number **/ + +/* Returns the (process-wide) unique number given to the Libobjects + * structure XX. See for more info. */ +size_t +objects_@XX@_number (objects_@XX@_t *xx) +{ + return xx->number; +} + +/* Gives XX a new (process-wide) unique number. Numbers are not + * reused. See for more info. */ +size_t +_objects_@XX@_set_serial_number (objects_@XX@_t *xx) +{ + size_t old_number; + + old_number = xx->number; + xx->number = (___objects_number_serial)++; + + return old_number; +} + +/** Extras **/ + +/* Sets the callbacks associated with XX's ``extra''. NOTE: This must + * be done before calling `objects_@XX@_set_extra()', as these callbacks + * are used in that routine. */ +objects_callbacks_t +objects_@XX@_set_extra_callbacks (objects_@XX@_t *xx, objects_callbacks_t callbacks) +{ + objects_callbacks_t old_callbacks; + + /* Remember the old callbacks for later. */ + old_callbacks = xx->extra_callbacks; + + /* Set the new callbacks. */ + xx->extra_callbacks = callbacks; + + /* Release the old contents. */ + objects_release (old_callbacks, xx->extra, xx); + + /* Set the contents to something noticible. */ + xx->extra = (xx->extra_callbacks).not_an_item_marker; + + return old_callbacks; +} + +/* Returns the callbacks associated with XX's ``extra''. */ +objects_callbacks_t +objects_@XX@_extra_callbacks (objects_@XX@_t *xx, objects_callbacks_t callbacks) +{ + return xx->extra_callbacks; +} + +/* Returns XX's ``extra'', a little extra space that each Libobjects + * structure carries around with it. Its use is + * implementation-dependent. */ +void * +objects_@XX@_extra (objects_@XX@_t *xx) +{ + return xx->extra; +} + +/* Sets XX's ``extra'', a little extra space that each Libobjects structure + * carries around with it. Its use is implementation-dependent. */ +void * +objects_@XX@_set_extra (objects_@XX@_t *xx, void *extra) +{ + void *old_extra; + + /* Out with the old, and in with the new. */ + old_extra = xx->extra; + xx->extra = objects_retain (xx->extra_callbacks, extra, xx); + objects_release (xx->extra_callbacks, old_extra, xx); + + return old_extra; +} + +/* Resets XX's ``extra''. */ +void +objects_@XX@_unset_extra (objects_@XX@_t *xx) +{ + /* Release XX's extra. */ + objects_release (xx->extra_callbacks, xx->extra, xx); + + /* Reset XX's extra. */ + xx->extra = (xx->extra_callbacks).not_an_item_marker; + + return; +} + +/** Low-level Creation and Destruction **/ + +/* Handles the universal, low-level allocation of Libobjects structures. */ +objects_@XX@_t * +_objects_@XX@_alloc_with_allocs (objects_allocs_t allocs) +{ + objects_@XX@_t *xx; + + /* Try to allocate some space for XX. */ + xx = objects_malloc (allocs, sizeof (objects_@XX@_t)); + + /* The `objects_malloc()' was successful. */ + if (xx != NULL) + { + _objects_@XX@_set_serial_number (xx); + xx->magic = _OBJECTS_MAGIC_@XX@; + xx->name = NULL; + xx->allocs = allocs; + xx->extra_callbacks = objects_callbacks_for_void_p; + xx->extra = 0; + + /* Increment the counter of allocated Libobjects structures. */ + ++(___objects_number_allocated); + } + + return xx; +} + +/* Handles the universal, low-level deallocation of Libobjects structures. */ +void +_objects_@XX@_dealloc (objects_@XX@_t *xx) +{ + /* Make sure XX is valid. */ + if (xx != NULL) + { + /* Free up any space we needed to keep track of XX's name. */ + if (xx->name != NULL) + objects_free (objects_@XX@_allocs (xx), xx->name); + + /* Release XX's extra. */ + objects_@XX@_unset_extra (xx); + + /* Free up XX itself. */ + objects_free (objects_@XX@_allocs (xx), xx); + + /* Increment the counter of deallocated Libobjects structures. */ + ++(___objects_number_deallocated); + } + + return; +} + +/* Handles the low-level copying of Libobjects structures. */ +objects_@XX@_t * +_objects_@XX@_copy_with_allocs (objects_@XX@_t *xx, objects_allocs_t allocs) +{ + objects_@XX@_t *new; + + /* Create a new structure. */ + new = _objects_@XX@_alloc_with_allocs (allocs); + + if (new != NULL) + { + /* Copy over XX's name. */ + objects_@XX@_set_name (new, objects_@XX@_name (xx)); + + /* Copy over XX's extras. */ + objects_@XX@_set_extra_callbacks (new, xx->extra_callbacks); + objects_@XX@_set_extra (new, xx->extra); + } + + return new; +} + diff --git a/Source/x-callbacks.c.in b/Source/x-callbacks.c.in new file mode 100644 index 000000000..05d814483 --- /dev/null +++ b/Source/x-callbacks.c.in @@ -0,0 +1,89 @@ +/* Getting callbacks from @YY@ structures. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 11 02:44:09 EST 1995 + * Updated: Sat Feb 10 10:08:59 EST 1996 + * Serial: 96.02.10.01 + * + * This file is part of the GNU Objective C Class Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/**** Included Headers *******************************************************/ + +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +#define __@YY@__ 1 + +/**** Function Implementations ***********************************************/ + +#ifdef __map__ + +/** Callbacks **/ + +/* Returns the callbacks associated with YY's keys. */ +objects_callbacks_t +objects_@YY@_key_callbacks (objects_@YY@_t * yy) +{ + return yy->key_callbacks; +} + +/* Returns the ``bogus'' marker associated with YY's keys. */ +void * +objects_@YY@_not_a_key_marker (objects_@YY@_t *yy) +{ + return (yy->key_callbacks).not_an_item_marker; +} + +/* Returns the callbacks associated with YY's values. */ +objects_callbacks_t +objects_@YY@_value_callbacks (objects_@YY@_t *yy) +{ + return yy->value_callbacks; +} + +/* Returns the ``bogus'' marker associated with YY's values. */ +void * +objects_@YY@_not_a_value_marker (objects_@YY@_t *yy) +{ + return (yy->value_callbacks).not_an_item_marker; +} + +#else /* !__map__ */ + +/** Callbacks **/ + +/* Returns the callbacks associated with YY's elements. */ +objects_callbacks_t +objects_@YY@_element_callbacks (objects_@YY@_t *yy) +{ + return yy->callbacks; +} + +/* Returns the ``bogus'' marker associated with YY's elements. */ +void * +objects_@YY@_not_an_element_marker (objects_@YY@_t *yy) +{ + return (yy->callbacks).not_an_item_marker; +} + +#endif /* __map__ */ +